librpc: Shorten dcerpc_binding_handle_call a bit
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob72529276c8e6450c46f473353d825089136cf876
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 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
947 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
948 struct winbindd_domain *mydomain =
949 find_domain_from_sid_noinit(sid);
950 if (mydomain != NULL) {
951 domain_name = mydomain->name;
955 centry_put_uint32(centry, type);
956 centry_put_sid(centry, sid);
957 fstrcpy(uname, name);
958 (void)strupper_m(uname);
959 centry_end(centry, "NS/%s/%s", domain_name, uname);
960 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
961 uname, sid_string_dbg(sid), nt_errstr(status)));
962 centry_free(centry);
965 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
966 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
968 struct cache_entry *centry;
969 fstring sid_string;
971 centry = centry_start(domain, status);
972 if (!centry)
973 return;
975 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
976 struct winbindd_domain *mydomain =
977 find_domain_from_sid_noinit(sid);
978 if (mydomain != NULL) {
979 domain_name = mydomain->name;
983 if (NT_STATUS_IS_OK(status)) {
984 centry_put_uint32(centry, type);
985 centry_put_string(centry, domain_name);
986 centry_put_string(centry, name);
989 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
990 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
991 domain_name, name, nt_errstr(status)));
992 centry_free(centry);
996 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
997 struct wbint_userinfo *info)
999 struct cache_entry *centry;
1000 fstring sid_string;
1002 if (is_null_sid(&info->user_sid)) {
1003 return;
1006 centry = centry_start(domain, status);
1007 if (!centry)
1008 return;
1009 centry_put_string(centry, info->acct_name);
1010 centry_put_string(centry, info->full_name);
1011 centry_put_string(centry, info->homedir);
1012 centry_put_string(centry, info->shell);
1013 centry_put_uint32(centry, info->primary_gid);
1014 centry_put_sid(centry, &info->user_sid);
1015 centry_put_sid(centry, &info->group_sid);
1016 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1017 &info->user_sid));
1018 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1019 centry_free(centry);
1022 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1023 NTSTATUS status,
1024 struct samr_DomInfo12 *lockout_policy)
1026 struct cache_entry *centry;
1028 centry = centry_start(domain, status);
1029 if (!centry)
1030 return;
1032 centry_put_nttime(centry, lockout_policy->lockout_duration);
1033 centry_put_nttime(centry, lockout_policy->lockout_window);
1034 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1036 centry_end(centry, "LOC_POL/%s", domain->name);
1038 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1040 centry_free(centry);
1045 static void wcache_save_password_policy(struct winbindd_domain *domain,
1046 NTSTATUS status,
1047 struct samr_DomInfo1 *policy)
1049 struct cache_entry *centry;
1051 centry = centry_start(domain, status);
1052 if (!centry)
1053 return;
1055 centry_put_uint16(centry, policy->min_password_length);
1056 centry_put_uint16(centry, policy->password_history_length);
1057 centry_put_uint32(centry, policy->password_properties);
1058 centry_put_nttime(centry, policy->max_password_age);
1059 centry_put_nttime(centry, policy->min_password_age);
1061 centry_end(centry, "PWD_POL/%s", domain->name);
1063 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1065 centry_free(centry);
1068 /***************************************************************************
1069 ***************************************************************************/
1071 static void wcache_save_username_alias(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *name, const char *alias)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, alias );
1083 fstrcpy(uname, name);
1084 (void)strupper_m(uname);
1085 centry_end(centry, "NSS/NA/%s", uname);
1087 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1089 centry_free(centry);
1092 static void wcache_save_alias_username(struct winbindd_domain *domain,
1093 NTSTATUS status,
1094 const char *alias, const char *name)
1096 struct cache_entry *centry;
1097 fstring uname;
1099 if ( (centry = centry_start(domain, status)) == NULL )
1100 return;
1102 centry_put_string( centry, name );
1104 fstrcpy(uname, alias);
1105 (void)strupper_m(uname);
1106 centry_end(centry, "NSS/AN/%s", uname);
1108 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1110 centry_free(centry);
1113 /***************************************************************************
1114 ***************************************************************************/
1116 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1117 struct winbindd_domain *domain,
1118 const char *name, char **alias )
1120 struct winbind_cache *cache = get_cache(domain);
1121 struct cache_entry *centry = NULL;
1122 NTSTATUS status;
1123 char *upper_name;
1125 if ( domain->internal )
1126 return NT_STATUS_NOT_SUPPORTED;
1128 if (!cache->tdb)
1129 goto do_query;
1131 upper_name = talloc_strdup(mem_ctx, name);
1132 if (upper_name == NULL) {
1133 return NT_STATUS_NO_MEMORY;
1135 if (!strupper_m(upper_name)) {
1136 talloc_free(upper_name);
1137 return NT_STATUS_INVALID_PARAMETER;
1140 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1142 talloc_free(upper_name);
1144 if (!centry)
1145 goto do_query;
1147 status = centry->status;
1149 if (!NT_STATUS_IS_OK(status)) {
1150 centry_free(centry);
1151 return status;
1154 *alias = centry_string( centry, mem_ctx );
1156 centry_free(centry);
1158 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1159 name, *alias ? *alias : "(none)"));
1161 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1163 do_query:
1165 /* If its not in cache and we are offline, then fail */
1167 if ( get_global_winbindd_state_offline() || !domain->online ) {
1168 DEBUG(8,("resolve_username_to_alias: rejecting query "
1169 "in offline mode\n"));
1170 return NT_STATUS_NOT_FOUND;
1173 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1175 if ( NT_STATUS_IS_OK( status ) ) {
1176 wcache_save_username_alias(domain, status, name, *alias);
1179 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1180 wcache_save_username_alias(domain, status, name, "(NULL)");
1183 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1184 nt_errstr(status)));
1186 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1187 set_domain_offline( domain );
1190 return status;
1193 /***************************************************************************
1194 ***************************************************************************/
1196 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1197 struct winbindd_domain *domain,
1198 const char *alias, char **name )
1200 struct winbind_cache *cache = get_cache(domain);
1201 struct cache_entry *centry = NULL;
1202 NTSTATUS status;
1203 char *upper_name;
1205 if ( domain->internal )
1206 return NT_STATUS_NOT_SUPPORTED;
1208 if (!cache->tdb)
1209 goto do_query;
1211 upper_name = talloc_strdup(mem_ctx, alias);
1212 if (upper_name == NULL) {
1213 return NT_STATUS_NO_MEMORY;
1215 if (!strupper_m(upper_name)) {
1216 talloc_free(upper_name);
1217 return NT_STATUS_INVALID_PARAMETER;
1220 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1222 talloc_free(upper_name);
1224 if (!centry)
1225 goto do_query;
1227 status = centry->status;
1229 if (!NT_STATUS_IS_OK(status)) {
1230 centry_free(centry);
1231 return status;
1234 *name = centry_string( centry, mem_ctx );
1236 centry_free(centry);
1238 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1239 alias, *name ? *name : "(none)"));
1241 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1243 do_query:
1245 /* If its not in cache and we are offline, then fail */
1247 if ( get_global_winbindd_state_offline() || !domain->online ) {
1248 DEBUG(8,("resolve_alias_to_username: rejecting query "
1249 "in offline mode\n"));
1250 return NT_STATUS_NOT_FOUND;
1253 /* an alias cannot contain a domain prefix or '@' */
1255 if (strchr(alias, '\\') || strchr(alias, '@')) {
1256 DEBUG(10,("resolve_alias_to_username: skipping fully "
1257 "qualified name %s\n", alias));
1258 return NT_STATUS_OBJECT_NAME_INVALID;
1261 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1263 if ( NT_STATUS_IS_OK( status ) ) {
1264 wcache_save_alias_username( domain, status, alias, *name );
1267 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1268 wcache_save_alias_username(domain, status, alias, "(NULL)");
1271 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1272 nt_errstr(status)));
1274 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1275 set_domain_offline( domain );
1278 return status;
1281 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1283 struct winbind_cache *cache = get_cache(domain);
1284 TDB_DATA data;
1285 fstring key_str, tmp;
1286 uint32 rid;
1288 if (!cache->tdb) {
1289 return NT_STATUS_INTERNAL_DB_ERROR;
1292 if (is_null_sid(sid)) {
1293 return NT_STATUS_INVALID_SID;
1296 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1297 return NT_STATUS_INVALID_SID;
1300 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1302 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1303 if (!data.dptr) {
1304 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1307 SAFE_FREE(data.dptr);
1308 return NT_STATUS_OK;
1311 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1312 as new salted ones. */
1314 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1315 TALLOC_CTX *mem_ctx,
1316 const struct dom_sid *sid,
1317 const uint8 **cached_nt_pass,
1318 const uint8 **cached_salt)
1320 struct winbind_cache *cache = get_cache(domain);
1321 struct cache_entry *centry = NULL;
1322 NTSTATUS status;
1323 uint32 rid;
1324 fstring tmp;
1326 if (!cache->tdb) {
1327 return NT_STATUS_INTERNAL_DB_ERROR;
1330 if (is_null_sid(sid)) {
1331 return NT_STATUS_INVALID_SID;
1334 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1335 return NT_STATUS_INVALID_SID;
1338 /* Try and get a salted cred first. If we can't
1339 fall back to an unsalted cred. */
1341 centry = wcache_fetch(cache, domain, "CRED/%s",
1342 sid_to_fstring(tmp, sid));
1343 if (!centry) {
1344 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1345 sid_string_dbg(sid)));
1346 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1350 * We don't use the time element at this moment,
1351 * but we have to consume it, so that we don't
1352 * neet to change the disk format of the cache.
1354 (void)centry_time(centry);
1356 /* In the salted case this isn't actually the nt_hash itself,
1357 but the MD5 of the salt + nt_hash. Let the caller
1358 sort this out. It can tell as we only return the cached_salt
1359 if we are returning a salted cred. */
1361 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1362 if (*cached_nt_pass == NULL) {
1363 fstring sidstr;
1365 sid_to_fstring(sidstr, sid);
1367 /* Bad (old) cred cache. Delete and pretend we
1368 don't have it. */
1369 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1370 sidstr));
1371 wcache_delete("CRED/%s", sidstr);
1372 centry_free(centry);
1373 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1376 /* We only have 17 bytes more data in the salted cred case. */
1377 if (centry->len - centry->ofs == 17) {
1378 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1379 } else {
1380 *cached_salt = NULL;
1383 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1384 if (*cached_salt) {
1385 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1388 status = centry->status;
1390 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1391 sid_string_dbg(sid), nt_errstr(status) ));
1393 centry_free(centry);
1394 return status;
1397 /* Store creds for a SID - only writes out new salted ones. */
1399 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1400 const struct dom_sid *sid,
1401 const uint8 nt_pass[NT_HASH_LEN])
1403 struct cache_entry *centry;
1404 fstring sid_string;
1405 uint32 rid;
1406 uint8 cred_salt[NT_HASH_LEN];
1407 uint8 salted_hash[NT_HASH_LEN];
1409 if (is_null_sid(sid)) {
1410 return NT_STATUS_INVALID_SID;
1413 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1414 return NT_STATUS_INVALID_SID;
1417 centry = centry_start(domain, NT_STATUS_OK);
1418 if (!centry) {
1419 return NT_STATUS_INTERNAL_DB_ERROR;
1422 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1424 centry_put_time(centry, time(NULL));
1426 /* Create a salt and then salt the hash. */
1427 generate_random_buffer(cred_salt, NT_HASH_LEN);
1428 E_md5hash(cred_salt, nt_pass, salted_hash);
1430 centry_put_hash16(centry, salted_hash);
1431 centry_put_hash16(centry, cred_salt);
1432 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1434 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1436 centry_free(centry);
1438 return NT_STATUS_OK;
1442 /* Query display info. This is the basic user list fn */
1443 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1444 TALLOC_CTX *mem_ctx,
1445 uint32 *num_entries,
1446 struct wbint_userinfo **info)
1448 struct winbind_cache *cache = get_cache(domain);
1449 struct cache_entry *centry = NULL;
1450 NTSTATUS status;
1451 unsigned int i, retry;
1452 bool old_status = domain->online;
1454 if (!cache->tdb)
1455 goto do_query;
1457 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1458 if (!centry)
1459 goto do_query;
1461 do_fetch_cache:
1462 *num_entries = centry_uint32(centry);
1464 if (*num_entries == 0)
1465 goto do_cached;
1467 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1468 if (! (*info)) {
1469 smb_panic_fn("query_user_list out of memory");
1471 for (i=0; i<(*num_entries); i++) {
1472 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1473 (*info)[i].full_name = centry_string(centry, mem_ctx);
1474 (*info)[i].homedir = centry_string(centry, mem_ctx);
1475 (*info)[i].shell = centry_string(centry, mem_ctx);
1476 centry_sid(centry, &(*info)[i].user_sid);
1477 centry_sid(centry, &(*info)[i].group_sid);
1480 do_cached:
1481 status = centry->status;
1483 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1484 domain->name, nt_errstr(status) ));
1486 centry_free(centry);
1487 return status;
1489 do_query:
1490 *num_entries = 0;
1491 *info = NULL;
1493 /* Return status value returned by seq number check */
1495 if (!NT_STATUS_IS_OK(domain->last_status))
1496 return domain->last_status;
1498 /* Put the query_user_list() in a retry loop. There appears to be
1499 * some bug either with Windows 2000 or Samba's handling of large
1500 * rpc replies. This manifests itself as sudden disconnection
1501 * at a random point in the enumeration of a large (60k) user list.
1502 * The retry loop simply tries the operation again. )-: It's not
1503 * pretty but an acceptable workaround until we work out what the
1504 * real problem is. */
1506 retry = 0;
1507 do {
1509 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1510 domain->name ));
1512 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1513 if (!NT_STATUS_IS_OK(status)) {
1514 DEBUG(3, ("query_user_list: returned 0x%08x, "
1515 "retrying\n", NT_STATUS_V(status)));
1517 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1518 DEBUG(3, ("query_user_list: flushing "
1519 "connection cache\n"));
1520 invalidate_cm_connection(&domain->conn);
1522 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1523 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1524 if (!domain->internal && old_status) {
1525 set_domain_offline(domain);
1527 /* store partial response. */
1528 if (*num_entries > 0) {
1530 * humm, what about the status used for cache?
1531 * Should it be NT_STATUS_OK?
1533 break;
1536 * domain is offline now, and there is no user entries,
1537 * try to fetch from cache again.
1539 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1540 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1541 /* partial response... */
1542 if (!centry) {
1543 goto skip_save;
1544 } else {
1545 goto do_fetch_cache;
1547 } else {
1548 goto skip_save;
1552 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1553 (retry++ < 5));
1555 /* and save it */
1556 refresh_sequence_number(domain, false);
1557 if (!NT_STATUS_IS_OK(status)) {
1558 return status;
1560 centry = centry_start(domain, status);
1561 if (!centry)
1562 goto skip_save;
1563 centry_put_uint32(centry, *num_entries);
1564 for (i=0; i<(*num_entries); i++) {
1565 centry_put_string(centry, (*info)[i].acct_name);
1566 centry_put_string(centry, (*info)[i].full_name);
1567 centry_put_string(centry, (*info)[i].homedir);
1568 centry_put_string(centry, (*info)[i].shell);
1569 centry_put_sid(centry, &(*info)[i].user_sid);
1570 centry_put_sid(centry, &(*info)[i].group_sid);
1571 if (domain->backend && domain->backend->consistent) {
1572 /* when the backend is consistent we can pre-prime some mappings */
1573 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1574 domain->name,
1575 (*info)[i].acct_name,
1576 &(*info)[i].user_sid,
1577 SID_NAME_USER);
1578 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1579 &(*info)[i].user_sid,
1580 domain->name,
1581 (*info)[i].acct_name,
1582 SID_NAME_USER);
1583 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1586 centry_end(centry, "UL/%s", domain->name);
1587 centry_free(centry);
1589 skip_save:
1590 return status;
1593 /* list all domain groups */
1594 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1595 TALLOC_CTX *mem_ctx,
1596 uint32 *num_entries,
1597 struct wb_acct_info **info)
1599 struct winbind_cache *cache = get_cache(domain);
1600 struct cache_entry *centry = NULL;
1601 NTSTATUS status;
1602 unsigned int i;
1603 bool old_status;
1605 old_status = domain->online;
1606 if (!cache->tdb)
1607 goto do_query;
1609 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1610 if (!centry)
1611 goto do_query;
1613 do_fetch_cache:
1614 *num_entries = centry_uint32(centry);
1616 if (*num_entries == 0)
1617 goto do_cached;
1619 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1620 if (! (*info)) {
1621 smb_panic_fn("enum_dom_groups out of memory");
1623 for (i=0; i<(*num_entries); i++) {
1624 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1625 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1626 (*info)[i].rid = centry_uint32(centry);
1629 do_cached:
1630 status = centry->status;
1632 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1633 domain->name, nt_errstr(status) ));
1635 centry_free(centry);
1636 return status;
1638 do_query:
1639 *num_entries = 0;
1640 *info = NULL;
1642 /* Return status value returned by seq number check */
1644 if (!NT_STATUS_IS_OK(domain->last_status))
1645 return domain->last_status;
1647 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1648 domain->name ));
1650 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1652 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1653 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1654 if (!domain->internal && old_status) {
1655 set_domain_offline(domain);
1657 if (cache->tdb &&
1658 !domain->online &&
1659 !domain->internal &&
1660 old_status) {
1661 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1662 if (centry) {
1663 goto do_fetch_cache;
1667 /* and save it */
1668 refresh_sequence_number(domain, false);
1669 if (!NT_STATUS_IS_OK(status)) {
1670 return status;
1672 centry = centry_start(domain, status);
1673 if (!centry)
1674 goto skip_save;
1675 centry_put_uint32(centry, *num_entries);
1676 for (i=0; i<(*num_entries); i++) {
1677 centry_put_string(centry, (*info)[i].acct_name);
1678 centry_put_string(centry, (*info)[i].acct_desc);
1679 centry_put_uint32(centry, (*info)[i].rid);
1681 centry_end(centry, "GL/%s/domain", domain->name);
1682 centry_free(centry);
1684 skip_save:
1685 return status;
1688 /* list all domain groups */
1689 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1690 TALLOC_CTX *mem_ctx,
1691 uint32 *num_entries,
1692 struct wb_acct_info **info)
1694 struct winbind_cache *cache = get_cache(domain);
1695 struct cache_entry *centry = NULL;
1696 NTSTATUS status;
1697 unsigned int i;
1698 bool old_status;
1700 old_status = domain->online;
1701 if (!cache->tdb)
1702 goto do_query;
1704 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1705 if (!centry)
1706 goto do_query;
1708 do_fetch_cache:
1709 *num_entries = centry_uint32(centry);
1711 if (*num_entries == 0)
1712 goto do_cached;
1714 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1715 if (! (*info)) {
1716 smb_panic_fn("enum_dom_groups out of memory");
1718 for (i=0; i<(*num_entries); i++) {
1719 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1720 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1721 (*info)[i].rid = centry_uint32(centry);
1724 do_cached:
1726 /* If we are returning cached data and the domain controller
1727 is down then we don't know whether the data is up to date
1728 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1729 indicate this. */
1731 if (wcache_server_down(domain)) {
1732 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1733 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1734 } else
1735 status = centry->status;
1737 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1738 domain->name, nt_errstr(status) ));
1740 centry_free(centry);
1741 return status;
1743 do_query:
1744 *num_entries = 0;
1745 *info = NULL;
1747 /* Return status value returned by seq number check */
1749 if (!NT_STATUS_IS_OK(domain->last_status))
1750 return domain->last_status;
1752 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1753 domain->name ));
1755 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1757 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1758 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1759 if (!domain->internal && old_status) {
1760 set_domain_offline(domain);
1762 if (cache->tdb &&
1763 !domain->internal &&
1764 !domain->online &&
1765 old_status) {
1766 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1767 if (centry) {
1768 goto do_fetch_cache;
1772 /* and save it */
1773 refresh_sequence_number(domain, false);
1774 if (!NT_STATUS_IS_OK(status)) {
1775 return status;
1777 centry = centry_start(domain, status);
1778 if (!centry)
1779 goto skip_save;
1780 centry_put_uint32(centry, *num_entries);
1781 for (i=0; i<(*num_entries); i++) {
1782 centry_put_string(centry, (*info)[i].acct_name);
1783 centry_put_string(centry, (*info)[i].acct_desc);
1784 centry_put_uint32(centry, (*info)[i].rid);
1786 centry_end(centry, "GL/%s/local", domain->name);
1787 centry_free(centry);
1789 skip_save:
1790 return status;
1793 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1794 const char *domain_name,
1795 const char *name,
1796 struct dom_sid *sid,
1797 enum lsa_SidType *type)
1799 struct winbind_cache *cache = get_cache(domain);
1800 struct cache_entry *centry;
1801 NTSTATUS status;
1802 char *uname;
1804 if (cache->tdb == NULL) {
1805 return NT_STATUS_NOT_FOUND;
1808 uname = talloc_strdup_upper(talloc_tos(), name);
1809 if (uname == NULL) {
1810 return NT_STATUS_NO_MEMORY;
1813 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1814 domain_name = domain->name;
1817 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1818 TALLOC_FREE(uname);
1819 if (centry == NULL) {
1820 return NT_STATUS_NOT_FOUND;
1823 status = centry->status;
1824 if (NT_STATUS_IS_OK(status)) {
1825 *type = (enum lsa_SidType)centry_uint32(centry);
1826 centry_sid(centry, sid);
1829 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1830 "%s\n", domain->name, nt_errstr(status) ));
1832 centry_free(centry);
1833 return status;
1836 /* convert a single name to a sid in a domain */
1837 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1838 TALLOC_CTX *mem_ctx,
1839 const char *domain_name,
1840 const char *name,
1841 uint32_t flags,
1842 struct dom_sid *sid,
1843 enum lsa_SidType *type)
1845 NTSTATUS status;
1846 bool old_status;
1848 old_status = domain->online;
1850 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1851 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1852 return status;
1855 ZERO_STRUCTP(sid);
1857 /* If the seq number check indicated that there is a problem
1858 * with this DC, then return that status... except for
1859 * access_denied. This is special because the dc may be in
1860 * "restrict anonymous = 1" mode, in which case it will deny
1861 * most unauthenticated operations, but *will* allow the LSA
1862 * name-to-sid that we try as a fallback. */
1864 if (!(NT_STATUS_IS_OK(domain->last_status)
1865 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1866 return domain->last_status;
1868 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1869 domain->name ));
1871 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1872 name, flags, sid, type);
1874 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1875 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1876 if (!domain->internal && old_status) {
1877 set_domain_offline(domain);
1879 if (!domain->internal &&
1880 !domain->online &&
1881 old_status) {
1882 NTSTATUS cache_status;
1883 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1884 return cache_status;
1887 /* and save it */
1888 refresh_sequence_number(domain, false);
1890 if (domain->online &&
1891 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1892 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1894 /* Only save the reverse mapping if this was not a UPN */
1895 if (!strchr(name, '@')) {
1896 if (!strupper_m(discard_const_p(char, domain_name))) {
1897 return NT_STATUS_INVALID_PARAMETER;
1899 (void)strlower_m(discard_const_p(char, name));
1900 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1904 return status;
1907 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1908 const struct dom_sid *sid,
1909 TALLOC_CTX *mem_ctx,
1910 char **domain_name,
1911 char **name,
1912 enum lsa_SidType *type)
1914 struct winbind_cache *cache = get_cache(domain);
1915 struct cache_entry *centry;
1916 char *sid_string;
1917 NTSTATUS status;
1919 if (cache->tdb == NULL) {
1920 return NT_STATUS_NOT_FOUND;
1923 sid_string = sid_string_tos(sid);
1924 if (sid_string == NULL) {
1925 return NT_STATUS_NO_MEMORY;
1928 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1929 TALLOC_FREE(sid_string);
1930 if (centry == NULL) {
1931 return NT_STATUS_NOT_FOUND;
1934 if (NT_STATUS_IS_OK(centry->status)) {
1935 *type = (enum lsa_SidType)centry_uint32(centry);
1936 *domain_name = centry_string(centry, mem_ctx);
1937 *name = centry_string(centry, mem_ctx);
1940 status = centry->status;
1941 centry_free(centry);
1943 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1944 "%s\n", domain->name, nt_errstr(status) ));
1946 return status;
1949 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1950 given */
1951 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1952 TALLOC_CTX *mem_ctx,
1953 const struct dom_sid *sid,
1954 char **domain_name,
1955 char **name,
1956 enum lsa_SidType *type)
1958 NTSTATUS status;
1959 bool old_status;
1961 old_status = domain->online;
1962 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1963 type);
1964 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1965 return status;
1968 *name = NULL;
1969 *domain_name = NULL;
1971 /* If the seq number check indicated that there is a problem
1972 * with this DC, then return that status... except for
1973 * access_denied. This is special because the dc may be in
1974 * "restrict anonymous = 1" mode, in which case it will deny
1975 * most unauthenticated operations, but *will* allow the LSA
1976 * sid-to-name that we try as a fallback. */
1978 if (!(NT_STATUS_IS_OK(domain->last_status)
1979 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1980 return domain->last_status;
1982 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1983 domain->name ));
1985 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1987 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1988 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1989 if (!domain->internal && old_status) {
1990 set_domain_offline(domain);
1992 if (!domain->internal &&
1993 !domain->online &&
1994 old_status) {
1995 NTSTATUS cache_status;
1996 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1997 domain_name, name, type);
1998 return cache_status;
2001 /* and save it */
2002 refresh_sequence_number(domain, false);
2003 if (!NT_STATUS_IS_OK(status)) {
2004 return status;
2006 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2008 /* We can't save the name to sid mapping here, as with sid history a
2009 * later name2sid would give the wrong sid. */
2011 return status;
2014 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2015 TALLOC_CTX *mem_ctx,
2016 const struct dom_sid *domain_sid,
2017 uint32 *rids,
2018 size_t num_rids,
2019 char **domain_name,
2020 char ***names,
2021 enum lsa_SidType **types)
2023 struct winbind_cache *cache = get_cache(domain);
2024 size_t i;
2025 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2026 bool have_mapped;
2027 bool have_unmapped;
2028 bool old_status;
2030 old_status = domain->online;
2031 *domain_name = NULL;
2032 *names = NULL;
2033 *types = NULL;
2035 if (!cache->tdb) {
2036 goto do_query;
2039 if (num_rids == 0) {
2040 return NT_STATUS_OK;
2043 *names = talloc_array(mem_ctx, char *, num_rids);
2044 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2046 if ((*names == NULL) || (*types == NULL)) {
2047 result = NT_STATUS_NO_MEMORY;
2048 goto error;
2051 have_mapped = have_unmapped = false;
2053 for (i=0; i<num_rids; i++) {
2054 struct dom_sid sid;
2055 struct cache_entry *centry;
2056 fstring tmp;
2058 if (!sid_compose(&sid, domain_sid, rids[i])) {
2059 result = NT_STATUS_INTERNAL_ERROR;
2060 goto error;
2063 centry = wcache_fetch(cache, domain, "SN/%s",
2064 sid_to_fstring(tmp, &sid));
2065 if (!centry) {
2066 goto do_query;
2069 (*types)[i] = SID_NAME_UNKNOWN;
2070 (*names)[i] = talloc_strdup(*names, "");
2072 if (NT_STATUS_IS_OK(centry->status)) {
2073 char *dom;
2074 have_mapped = true;
2075 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2077 dom = centry_string(centry, mem_ctx);
2078 if (*domain_name == NULL) {
2079 *domain_name = dom;
2080 } else {
2081 talloc_free(dom);
2084 (*names)[i] = centry_string(centry, *names);
2086 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2087 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2088 have_unmapped = true;
2090 } else {
2091 /* something's definitely wrong */
2092 result = centry->status;
2093 goto error;
2096 centry_free(centry);
2099 if (!have_mapped) {
2100 return NT_STATUS_NONE_MAPPED;
2102 if (!have_unmapped) {
2103 return NT_STATUS_OK;
2105 return STATUS_SOME_UNMAPPED;
2107 do_query:
2109 TALLOC_FREE(*names);
2110 TALLOC_FREE(*types);
2112 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2113 rids, num_rids, domain_name,
2114 names, types);
2116 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2117 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2118 if (!domain->internal && old_status) {
2119 set_domain_offline(domain);
2121 if (cache->tdb &&
2122 !domain->internal &&
2123 !domain->online &&
2124 old_status) {
2125 have_mapped = have_unmapped = false;
2127 for (i=0; i<num_rids; i++) {
2128 struct dom_sid sid;
2129 struct cache_entry *centry;
2130 fstring tmp;
2132 if (!sid_compose(&sid, domain_sid, rids[i])) {
2133 result = NT_STATUS_INTERNAL_ERROR;
2134 goto error;
2137 centry = wcache_fetch(cache, domain, "SN/%s",
2138 sid_to_fstring(tmp, &sid));
2139 if (!centry) {
2140 (*types)[i] = SID_NAME_UNKNOWN;
2141 (*names)[i] = talloc_strdup(*names, "");
2142 continue;
2145 (*types)[i] = SID_NAME_UNKNOWN;
2146 (*names)[i] = talloc_strdup(*names, "");
2148 if (NT_STATUS_IS_OK(centry->status)) {
2149 char *dom;
2150 have_mapped = true;
2151 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2153 dom = centry_string(centry, mem_ctx);
2154 if (*domain_name == NULL) {
2155 *domain_name = dom;
2156 } else {
2157 talloc_free(dom);
2160 (*names)[i] = centry_string(centry, *names);
2162 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2163 have_unmapped = true;
2165 } else {
2166 /* something's definitely wrong */
2167 result = centry->status;
2168 centry_free(centry);
2169 goto error;
2172 centry_free(centry);
2175 if (!have_mapped) {
2176 return NT_STATUS_NONE_MAPPED;
2178 if (!have_unmapped) {
2179 return NT_STATUS_OK;
2181 return STATUS_SOME_UNMAPPED;
2185 None of the queried rids has been found so save all negative entries
2187 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2188 for (i = 0; i < num_rids; i++) {
2189 struct dom_sid sid;
2190 const char *name = "";
2191 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2192 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2194 if (!sid_compose(&sid, domain_sid, rids[i])) {
2195 return NT_STATUS_INTERNAL_ERROR;
2198 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2199 name, type);
2202 return result;
2206 Some or all of the queried rids have been found.
2208 if (!NT_STATUS_IS_OK(result) &&
2209 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2210 return result;
2213 refresh_sequence_number(domain, false);
2215 for (i=0; i<num_rids; i++) {
2216 struct dom_sid sid;
2217 NTSTATUS status;
2219 if (!sid_compose(&sid, domain_sid, rids[i])) {
2220 result = NT_STATUS_INTERNAL_ERROR;
2221 goto error;
2224 status = (*types)[i] == SID_NAME_UNKNOWN ?
2225 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2227 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2228 (*names)[i], (*types)[i]);
2231 return result;
2233 error:
2234 TALLOC_FREE(*names);
2235 TALLOC_FREE(*types);
2236 return result;
2239 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2240 TALLOC_CTX *mem_ctx,
2241 const struct dom_sid *user_sid,
2242 struct wbint_userinfo *info)
2244 struct winbind_cache *cache = get_cache(domain);
2245 struct cache_entry *centry = NULL;
2246 NTSTATUS status;
2247 char *sid_string;
2249 if (cache->tdb == NULL) {
2250 return NT_STATUS_NOT_FOUND;
2253 sid_string = sid_string_tos(user_sid);
2254 if (sid_string == NULL) {
2255 return NT_STATUS_NO_MEMORY;
2258 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2259 TALLOC_FREE(sid_string);
2260 if (centry == NULL) {
2261 return NT_STATUS_NOT_FOUND;
2265 * If we have an access denied cache entry and a cached info3
2266 * in the samlogon cache then do a query. This will force the
2267 * rpc back end to return the info3 data.
2270 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2271 netsamlogon_cache_have(user_sid)) {
2272 DEBUG(10, ("query_user: cached access denied and have cached "
2273 "info3\n"));
2274 domain->last_status = NT_STATUS_OK;
2275 centry_free(centry);
2276 return NT_STATUS_NOT_FOUND;
2279 /* if status is not ok then this is a negative hit
2280 and the rest of the data doesn't matter */
2281 status = centry->status;
2282 if (NT_STATUS_IS_OK(status)) {
2283 info->acct_name = centry_string(centry, mem_ctx);
2284 info->full_name = centry_string(centry, mem_ctx);
2285 info->homedir = centry_string(centry, mem_ctx);
2286 info->shell = centry_string(centry, mem_ctx);
2287 info->primary_gid = centry_uint32(centry);
2288 centry_sid(centry, &info->user_sid);
2289 centry_sid(centry, &info->group_sid);
2292 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2293 "%s\n", domain->name, nt_errstr(status) ));
2295 centry_free(centry);
2296 return status;
2299 /* Lookup user information from a rid */
2300 static NTSTATUS query_user(struct winbindd_domain *domain,
2301 TALLOC_CTX *mem_ctx,
2302 const struct dom_sid *user_sid,
2303 struct wbint_userinfo *info)
2305 NTSTATUS status;
2306 bool old_status;
2308 old_status = domain->online;
2309 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2310 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2311 return status;
2314 ZERO_STRUCTP(info);
2316 /* Return status value returned by seq number check */
2318 if (!NT_STATUS_IS_OK(domain->last_status))
2319 return domain->last_status;
2321 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2322 domain->name ));
2324 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2326 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2327 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2328 if (!domain->internal && old_status) {
2329 set_domain_offline(domain);
2331 if (!domain->internal &&
2332 !domain->online &&
2333 old_status) {
2334 NTSTATUS cache_status;
2335 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2336 return cache_status;
2339 /* and save it */
2340 refresh_sequence_number(domain, false);
2341 if (!NT_STATUS_IS_OK(status)) {
2342 return status;
2344 wcache_save_user(domain, status, info);
2346 return status;
2349 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2350 TALLOC_CTX *mem_ctx,
2351 const struct dom_sid *user_sid,
2352 uint32_t *pnum_sids,
2353 struct dom_sid **psids)
2355 struct winbind_cache *cache = get_cache(domain);
2356 struct cache_entry *centry = NULL;
2357 NTSTATUS status;
2358 uint32_t i, num_sids;
2359 struct dom_sid *sids;
2360 fstring sid_string;
2362 if (cache->tdb == NULL) {
2363 return NT_STATUS_NOT_FOUND;
2366 centry = wcache_fetch(cache, domain, "UG/%s",
2367 sid_to_fstring(sid_string, user_sid));
2368 if (centry == NULL) {
2369 return NT_STATUS_NOT_FOUND;
2372 /* If we have an access denied cache entry and a cached info3 in the
2373 samlogon cache then do a query. This will force the rpc back end
2374 to return the info3 data. */
2376 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2377 && netsamlogon_cache_have(user_sid)) {
2378 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2379 "cached info3\n"));
2380 domain->last_status = NT_STATUS_OK;
2381 centry_free(centry);
2382 return NT_STATUS_NOT_FOUND;
2385 num_sids = centry_uint32(centry);
2386 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2387 if (sids == NULL) {
2388 centry_free(centry);
2389 return NT_STATUS_NO_MEMORY;
2392 for (i=0; i<num_sids; i++) {
2393 centry_sid(centry, &sids[i]);
2396 status = centry->status;
2398 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2399 "status: %s\n", domain->name, nt_errstr(status)));
2401 centry_free(centry);
2403 *pnum_sids = num_sids;
2404 *psids = sids;
2405 return status;
2408 /* Lookup groups a user is a member of. */
2409 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2410 TALLOC_CTX *mem_ctx,
2411 const struct dom_sid *user_sid,
2412 uint32 *num_groups, struct dom_sid **user_gids)
2414 struct cache_entry *centry = NULL;
2415 NTSTATUS status;
2416 unsigned int i;
2417 fstring sid_string;
2418 bool old_status;
2420 old_status = domain->online;
2421 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2422 num_groups, user_gids);
2423 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2424 return status;
2427 (*num_groups) = 0;
2428 (*user_gids) = NULL;
2430 /* Return status value returned by seq number check */
2432 if (!NT_STATUS_IS_OK(domain->last_status))
2433 return domain->last_status;
2435 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2436 domain->name ));
2438 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2440 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2441 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2442 if (!domain->internal && old_status) {
2443 set_domain_offline(domain);
2445 if (!domain->internal &&
2446 !domain->online &&
2447 old_status) {
2448 NTSTATUS cache_status;
2449 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2450 num_groups, user_gids);
2451 return cache_status;
2454 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2455 goto skip_save;
2457 /* and save it */
2458 refresh_sequence_number(domain, false);
2459 if (!NT_STATUS_IS_OK(status)) {
2460 return status;
2462 centry = centry_start(domain, status);
2463 if (!centry)
2464 goto skip_save;
2466 centry_put_uint32(centry, *num_groups);
2467 for (i=0; i<(*num_groups); i++) {
2468 centry_put_sid(centry, &(*user_gids)[i]);
2471 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2472 centry_free(centry);
2474 skip_save:
2475 return status;
2478 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2479 const struct dom_sid *sids)
2481 uint32_t i;
2482 char *sidlist;
2484 sidlist = talloc_strdup(mem_ctx, "");
2485 if (sidlist == NULL) {
2486 return NULL;
2488 for (i=0; i<num_sids; i++) {
2489 fstring tmp;
2490 sidlist = talloc_asprintf_append_buffer(
2491 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2492 if (sidlist == NULL) {
2493 return NULL;
2496 return sidlist;
2499 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2500 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2501 const struct dom_sid *sids,
2502 uint32_t *pnum_aliases, uint32_t **paliases)
2504 struct winbind_cache *cache = get_cache(domain);
2505 struct cache_entry *centry = NULL;
2506 uint32_t num_aliases;
2507 uint32_t *aliases;
2508 NTSTATUS status;
2509 char *sidlist;
2510 int i;
2512 if (cache->tdb == NULL) {
2513 return NT_STATUS_NOT_FOUND;
2516 if (num_sids == 0) {
2517 *pnum_aliases = 0;
2518 *paliases = NULL;
2519 return NT_STATUS_OK;
2522 /* We need to cache indexed by the whole list of SIDs, the aliases
2523 * resulting might come from any of the SIDs. */
2525 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2526 if (sidlist == NULL) {
2527 return NT_STATUS_NO_MEMORY;
2530 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2531 TALLOC_FREE(sidlist);
2532 if (centry == NULL) {
2533 return NT_STATUS_NOT_FOUND;
2536 num_aliases = centry_uint32(centry);
2537 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2538 if (aliases == NULL) {
2539 centry_free(centry);
2540 return NT_STATUS_NO_MEMORY;
2543 for (i=0; i<num_aliases; i++) {
2544 aliases[i] = centry_uint32(centry);
2547 status = centry->status;
2549 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2550 "status %s\n", domain->name, nt_errstr(status)));
2552 centry_free(centry);
2554 *pnum_aliases = num_aliases;
2555 *paliases = aliases;
2557 return status;
2560 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2561 TALLOC_CTX *mem_ctx,
2562 uint32 num_sids, const struct dom_sid *sids,
2563 uint32 *num_aliases, uint32 **alias_rids)
2565 struct cache_entry *centry = NULL;
2566 NTSTATUS status;
2567 char *sidlist;
2568 int i;
2569 bool old_status;
2571 old_status = domain->online;
2572 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2573 num_aliases, alias_rids);
2574 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2575 return status;
2578 (*num_aliases) = 0;
2579 (*alias_rids) = NULL;
2581 if (!NT_STATUS_IS_OK(domain->last_status))
2582 return domain->last_status;
2584 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2585 "for domain %s\n", domain->name ));
2587 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2588 if (sidlist == NULL) {
2589 return NT_STATUS_NO_MEMORY;
2592 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2593 num_sids, sids,
2594 num_aliases, alias_rids);
2596 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2597 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2598 if (!domain->internal && old_status) {
2599 set_domain_offline(domain);
2601 if (!domain->internal &&
2602 !domain->online &&
2603 old_status) {
2604 NTSTATUS cache_status;
2605 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2606 sids, num_aliases, alias_rids);
2607 return cache_status;
2610 /* and save it */
2611 refresh_sequence_number(domain, false);
2612 if (!NT_STATUS_IS_OK(status)) {
2613 return status;
2615 centry = centry_start(domain, status);
2616 if (!centry)
2617 goto skip_save;
2618 centry_put_uint32(centry, *num_aliases);
2619 for (i=0; i<(*num_aliases); i++)
2620 centry_put_uint32(centry, (*alias_rids)[i]);
2621 centry_end(centry, "UA%s", sidlist);
2622 centry_free(centry);
2624 skip_save:
2625 return status;
2628 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2629 TALLOC_CTX *mem_ctx,
2630 const struct dom_sid *group_sid,
2631 uint32_t *num_names,
2632 struct dom_sid **sid_mem, char ***names,
2633 uint32_t **name_types)
2635 struct winbind_cache *cache = get_cache(domain);
2636 struct cache_entry *centry = NULL;
2637 NTSTATUS status;
2638 unsigned int i;
2639 char *sid_string;
2641 if (cache->tdb == NULL) {
2642 return NT_STATUS_NOT_FOUND;
2645 sid_string = sid_string_tos(group_sid);
2646 if (sid_string == NULL) {
2647 return NT_STATUS_NO_MEMORY;
2650 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2651 TALLOC_FREE(sid_string);
2652 if (centry == NULL) {
2653 return NT_STATUS_NOT_FOUND;
2656 *sid_mem = NULL;
2657 *names = NULL;
2658 *name_types = NULL;
2660 *num_names = centry_uint32(centry);
2661 if (*num_names == 0) {
2662 centry_free(centry);
2663 return NT_STATUS_OK;
2666 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2667 *names = talloc_array(mem_ctx, char *, *num_names);
2668 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2670 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2671 TALLOC_FREE(*sid_mem);
2672 TALLOC_FREE(*names);
2673 TALLOC_FREE(*name_types);
2674 centry_free(centry);
2675 return NT_STATUS_NO_MEMORY;
2678 for (i=0; i<(*num_names); i++) {
2679 centry_sid(centry, &(*sid_mem)[i]);
2680 (*names)[i] = centry_string(centry, mem_ctx);
2681 (*name_types)[i] = centry_uint32(centry);
2684 status = centry->status;
2686 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2687 "status: %s\n", domain->name, nt_errstr(status)));
2689 centry_free(centry);
2690 return status;
2693 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2694 TALLOC_CTX *mem_ctx,
2695 const struct dom_sid *group_sid,
2696 enum lsa_SidType type,
2697 uint32 *num_names,
2698 struct dom_sid **sid_mem, char ***names,
2699 uint32 **name_types)
2701 struct cache_entry *centry = NULL;
2702 NTSTATUS status;
2703 unsigned int i;
2704 fstring sid_string;
2705 bool old_status;
2707 old_status = domain->online;
2708 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2709 sid_mem, names, name_types);
2710 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2711 return status;
2714 (*num_names) = 0;
2715 (*sid_mem) = NULL;
2716 (*names) = NULL;
2717 (*name_types) = NULL;
2719 /* Return status value returned by seq number check */
2721 if (!NT_STATUS_IS_OK(domain->last_status))
2722 return domain->last_status;
2724 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2725 domain->name ));
2727 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2728 type, num_names,
2729 sid_mem, names, name_types);
2731 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2732 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2733 if (!domain->internal && old_status) {
2734 set_domain_offline(domain);
2736 if (!domain->internal &&
2737 !domain->online &&
2738 old_status) {
2739 NTSTATUS cache_status;
2740 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2741 num_names, sid_mem, names,
2742 name_types);
2743 return cache_status;
2746 /* and save it */
2747 refresh_sequence_number(domain, false);
2748 if (!NT_STATUS_IS_OK(status)) {
2749 return status;
2751 centry = centry_start(domain, status);
2752 if (!centry)
2753 goto skip_save;
2754 centry_put_uint32(centry, *num_names);
2755 for (i=0; i<(*num_names); i++) {
2756 centry_put_sid(centry, &(*sid_mem)[i]);
2757 centry_put_string(centry, (*names)[i]);
2758 centry_put_uint32(centry, (*name_types)[i]);
2760 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2761 centry_free(centry);
2763 skip_save:
2764 return status;
2767 /* find the sequence number for a domain */
2768 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2770 refresh_sequence_number(domain, false);
2772 *seq = domain->sequence_number;
2774 return NT_STATUS_OK;
2777 /* enumerate trusted domains
2778 * (we need to have the list of trustdoms in the cache when we go offline) -
2779 * Guenther */
2780 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2781 TALLOC_CTX *mem_ctx,
2782 struct netr_DomainTrustList *trusts)
2784 NTSTATUS status;
2785 struct winbind_cache *cache;
2786 struct winbindd_tdc_domain *dom_list = NULL;
2787 size_t num_domains = 0;
2788 bool retval = false;
2789 int i;
2790 bool old_status;
2792 old_status = domain->online;
2793 trusts->count = 0;
2794 trusts->array = NULL;
2796 cache = get_cache(domain);
2797 if (!cache || !cache->tdb) {
2798 goto do_query;
2801 if (domain->online) {
2802 goto do_query;
2805 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2806 if (!retval || !num_domains || !dom_list) {
2807 TALLOC_FREE(dom_list);
2808 goto do_query;
2811 do_fetch_cache:
2812 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2813 if (!trusts->array) {
2814 TALLOC_FREE(dom_list);
2815 return NT_STATUS_NO_MEMORY;
2818 for (i = 0; i < num_domains; i++) {
2819 struct netr_DomainTrust *trust;
2820 struct dom_sid *sid;
2821 struct winbindd_domain *dom;
2823 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2824 if (dom && dom->internal) {
2825 continue;
2828 trust = &trusts->array[trusts->count];
2829 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2830 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2831 sid = talloc(trusts->array, struct dom_sid);
2832 if (!trust->netbios_name || !trust->dns_name ||
2833 !sid) {
2834 TALLOC_FREE(dom_list);
2835 TALLOC_FREE(trusts->array);
2836 return NT_STATUS_NO_MEMORY;
2839 trust->trust_flags = dom_list[i].trust_flags;
2840 trust->trust_attributes = dom_list[i].trust_attribs;
2841 trust->trust_type = dom_list[i].trust_type;
2842 sid_copy(sid, &dom_list[i].sid);
2843 trust->sid = sid;
2844 trusts->count++;
2847 TALLOC_FREE(dom_list);
2848 return NT_STATUS_OK;
2850 do_query:
2851 /* Return status value returned by seq number check */
2853 if (!NT_STATUS_IS_OK(domain->last_status))
2854 return domain->last_status;
2856 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2857 domain->name ));
2859 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2861 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2862 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2863 if (!domain->internal && old_status) {
2864 set_domain_offline(domain);
2866 if (!domain->internal &&
2867 !domain->online &&
2868 old_status) {
2869 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2870 if (retval && num_domains && dom_list) {
2871 TALLOC_FREE(trusts->array);
2872 trusts->count = 0;
2873 goto do_fetch_cache;
2877 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2878 * so that the generic centry handling still applies correctly -
2879 * Guenther*/
2881 if (!NT_STATUS_IS_ERR(status)) {
2882 status = NT_STATUS_OK;
2884 return status;
2887 /* get lockout policy */
2888 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2889 TALLOC_CTX *mem_ctx,
2890 struct samr_DomInfo12 *policy)
2892 struct winbind_cache *cache = get_cache(domain);
2893 struct cache_entry *centry = NULL;
2894 NTSTATUS status;
2895 bool old_status;
2897 old_status = domain->online;
2898 if (!cache->tdb)
2899 goto do_query;
2901 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2903 if (!centry)
2904 goto do_query;
2906 do_fetch_cache:
2907 policy->lockout_duration = centry_nttime(centry);
2908 policy->lockout_window = centry_nttime(centry);
2909 policy->lockout_threshold = centry_uint16(centry);
2911 status = centry->status;
2913 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2914 domain->name, nt_errstr(status) ));
2916 centry_free(centry);
2917 return status;
2919 do_query:
2920 ZERO_STRUCTP(policy);
2922 /* Return status value returned by seq number check */
2924 if (!NT_STATUS_IS_OK(domain->last_status))
2925 return domain->last_status;
2927 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2928 domain->name ));
2930 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2932 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2933 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2934 if (!domain->internal && old_status) {
2935 set_domain_offline(domain);
2937 if (cache->tdb &&
2938 !domain->internal &&
2939 !domain->online &&
2940 old_status) {
2941 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2942 if (centry) {
2943 goto do_fetch_cache;
2947 /* and save it */
2948 refresh_sequence_number(domain, false);
2949 if (!NT_STATUS_IS_OK(status)) {
2950 return status;
2952 wcache_save_lockout_policy(domain, status, policy);
2954 return status;
2957 /* get password policy */
2958 static NTSTATUS password_policy(struct winbindd_domain *domain,
2959 TALLOC_CTX *mem_ctx,
2960 struct samr_DomInfo1 *policy)
2962 struct winbind_cache *cache = get_cache(domain);
2963 struct cache_entry *centry = NULL;
2964 NTSTATUS status;
2965 bool old_status;
2967 old_status = domain->online;
2968 if (!cache->tdb)
2969 goto do_query;
2971 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2973 if (!centry)
2974 goto do_query;
2976 do_fetch_cache:
2977 policy->min_password_length = centry_uint16(centry);
2978 policy->password_history_length = centry_uint16(centry);
2979 policy->password_properties = centry_uint32(centry);
2980 policy->max_password_age = centry_nttime(centry);
2981 policy->min_password_age = centry_nttime(centry);
2983 status = centry->status;
2985 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2986 domain->name, nt_errstr(status) ));
2988 centry_free(centry);
2989 return status;
2991 do_query:
2992 ZERO_STRUCTP(policy);
2994 /* Return status value returned by seq number check */
2996 if (!NT_STATUS_IS_OK(domain->last_status))
2997 return domain->last_status;
2999 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3000 domain->name ));
3002 status = domain->backend->password_policy(domain, mem_ctx, policy);
3004 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3005 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3006 if (!domain->internal && old_status) {
3007 set_domain_offline(domain);
3009 if (cache->tdb &&
3010 !domain->internal &&
3011 !domain->online &&
3012 old_status) {
3013 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3014 if (centry) {
3015 goto do_fetch_cache;
3019 /* and save it */
3020 refresh_sequence_number(domain, false);
3021 if (!NT_STATUS_IS_OK(status)) {
3022 return status;
3024 wcache_save_password_policy(domain, status, policy);
3026 return status;
3030 /* Invalidate cached user and group lists coherently */
3032 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3033 void *state)
3035 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3036 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3037 tdb_delete(the_tdb, kbuf);
3039 return 0;
3042 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3044 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3045 const struct dom_sid *sid)
3047 fstring key_str, sid_string;
3048 struct winbind_cache *cache;
3050 /* dont clear cached U/SID and UG/SID entries when we want to logon
3051 * offline - gd */
3053 if (lp_winbind_offline_logon()) {
3054 return;
3057 if (!domain)
3058 return;
3060 cache = get_cache(domain);
3062 if (!cache->tdb) {
3063 return;
3066 /* Clear U/SID cache entry */
3067 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3068 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3069 tdb_delete(cache->tdb, string_tdb_data(key_str));
3071 /* Clear UG/SID cache entry */
3072 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3073 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3074 tdb_delete(cache->tdb, string_tdb_data(key_str));
3076 /* Samba/winbindd never needs this. */
3077 netsamlogon_clear_cached_user(sid);
3080 bool wcache_invalidate_cache(void)
3082 struct winbindd_domain *domain;
3084 for (domain = domain_list(); domain; domain = domain->next) {
3085 struct winbind_cache *cache = get_cache(domain);
3087 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3088 "entries for %s\n", domain->name));
3089 if (cache) {
3090 if (cache->tdb) {
3091 tdb_traverse(cache->tdb, traverse_fn, NULL);
3092 } else {
3093 return false;
3097 return true;
3100 bool wcache_invalidate_cache_noinit(void)
3102 struct winbindd_domain *domain;
3104 for (domain = domain_list(); domain; domain = domain->next) {
3105 struct winbind_cache *cache;
3107 /* Skip uninitialized domains. */
3108 if (!domain->initialized && !domain->internal) {
3109 continue;
3112 cache = get_cache(domain);
3114 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3115 "entries for %s\n", domain->name));
3116 if (cache) {
3117 if (cache->tdb) {
3118 tdb_traverse(cache->tdb, traverse_fn, NULL);
3120 * Flushing cache has nothing to with domains.
3121 * return here if we successfully flushed once.
3122 * To avoid unnecessary traversing the cache.
3124 return true;
3125 } else {
3126 return false;
3130 return true;
3133 bool init_wcache(void)
3135 if (wcache == NULL) {
3136 wcache = SMB_XMALLOC_P(struct winbind_cache);
3137 ZERO_STRUCTP(wcache);
3140 if (wcache->tdb != NULL)
3141 return true;
3143 /* when working offline we must not clear the cache on restart */
3144 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3145 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3146 TDB_INCOMPATIBLE_HASH |
3147 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3148 O_RDWR|O_CREAT, 0600);
3150 if (wcache->tdb == NULL) {
3151 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3152 return false;
3155 return true;
3158 /************************************************************************
3159 This is called by the parent to initialize the cache file.
3160 We don't need sophisticated locking here as we know we're the
3161 only opener.
3162 ************************************************************************/
3164 bool initialize_winbindd_cache(void)
3166 bool cache_bad = true;
3167 uint32 vers;
3169 if (!init_wcache()) {
3170 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3171 return false;
3174 /* Check version number. */
3175 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3176 vers == WINBINDD_CACHE_VERSION) {
3177 cache_bad = false;
3180 if (cache_bad) {
3181 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3182 "and re-creating with version number %d\n",
3183 WINBINDD_CACHE_VERSION ));
3185 tdb_close(wcache->tdb);
3186 wcache->tdb = NULL;
3188 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3189 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3190 state_path("winbindd_cache.tdb"),
3191 strerror(errno) ));
3192 return false;
3194 if (!init_wcache()) {
3195 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3196 "init_wcache failed.\n"));
3197 return false;
3200 /* Write the version. */
3201 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3202 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3203 tdb_errorstr_compat(wcache->tdb) ));
3204 return false;
3208 tdb_close(wcache->tdb);
3209 wcache->tdb = NULL;
3210 return true;
3213 void close_winbindd_cache(void)
3215 if (!wcache) {
3216 return;
3218 if (wcache->tdb) {
3219 tdb_close(wcache->tdb);
3220 wcache->tdb = NULL;
3224 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3225 char **domain_name, char **name,
3226 enum lsa_SidType *type)
3228 struct winbindd_domain *domain;
3229 NTSTATUS status;
3231 domain = find_lookup_domain_from_sid(sid);
3232 if (domain == NULL) {
3233 return false;
3235 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3236 type);
3237 return NT_STATUS_IS_OK(status);
3240 bool lookup_cached_name(const char *domain_name,
3241 const char *name,
3242 struct dom_sid *sid,
3243 enum lsa_SidType *type)
3245 struct winbindd_domain *domain;
3246 NTSTATUS status;
3247 bool original_online_state;
3249 domain = find_lookup_domain_from_name(domain_name);
3250 if (domain == NULL) {
3251 return false;
3254 /* If we are doing a cached logon, temporarily set the domain
3255 offline so the cache won't expire the entry */
3257 original_online_state = domain->online;
3258 domain->online = false;
3259 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3260 domain->online = original_online_state;
3262 return NT_STATUS_IS_OK(status);
3265 void cache_name2sid(struct winbindd_domain *domain,
3266 const char *domain_name, const char *name,
3267 enum lsa_SidType type, const struct dom_sid *sid)
3269 refresh_sequence_number(domain, false);
3270 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3271 sid, type);
3275 * The original idea that this cache only contains centries has
3276 * been blurred - now other stuff gets put in here. Ensure we
3277 * ignore these things on cleanup.
3280 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3281 TDB_DATA dbuf, void *state)
3283 struct cache_entry *centry;
3285 if (is_non_centry_key(kbuf)) {
3286 return 0;
3289 centry = wcache_fetch_raw((char *)kbuf.dptr);
3290 if (!centry) {
3291 return 0;
3294 if (!NT_STATUS_IS_OK(centry->status)) {
3295 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3296 tdb_delete(the_tdb, kbuf);
3299 centry_free(centry);
3300 return 0;
3303 /* flush the cache */
3304 void wcache_flush_cache(void)
3306 if (!wcache)
3307 return;
3308 if (wcache->tdb) {
3309 tdb_close(wcache->tdb);
3310 wcache->tdb = NULL;
3312 if (!winbindd_use_cache()) {
3313 return;
3316 /* when working offline we must not clear the cache on restart */
3317 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3318 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3319 TDB_INCOMPATIBLE_HASH |
3320 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3321 O_RDWR|O_CREAT, 0600);
3323 if (!wcache->tdb) {
3324 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3325 return;
3328 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3330 DEBUG(10,("wcache_flush_cache success\n"));
3333 /* Count cached creds */
3335 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3336 void *state)
3338 int *cred_count = (int*)state;
3340 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3341 (*cred_count)++;
3343 return 0;
3346 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3348 struct winbind_cache *cache = get_cache(domain);
3350 *count = 0;
3352 if (!cache->tdb) {
3353 return NT_STATUS_INTERNAL_DB_ERROR;
3356 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3358 return NT_STATUS_OK;
3361 struct cred_list {
3362 struct cred_list *prev, *next;
3363 TDB_DATA key;
3364 fstring name;
3365 time_t created;
3367 static struct cred_list *wcache_cred_list;
3369 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3370 void *state)
3372 struct cred_list *cred;
3374 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3376 cred = SMB_MALLOC_P(struct cred_list);
3377 if (cred == NULL) {
3378 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3379 return -1;
3382 ZERO_STRUCTP(cred);
3384 /* save a copy of the key */
3386 fstrcpy(cred->name, (const char *)kbuf.dptr);
3387 DLIST_ADD(wcache_cred_list, cred);
3390 return 0;
3393 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3395 struct winbind_cache *cache = get_cache(domain);
3396 NTSTATUS status;
3397 int ret;
3398 struct cred_list *cred, *oldest = NULL;
3400 if (!cache->tdb) {
3401 return NT_STATUS_INTERNAL_DB_ERROR;
3404 /* we possibly already have an entry */
3405 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3407 fstring key_str, tmp;
3409 DEBUG(11,("we already have an entry, deleting that\n"));
3411 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3413 tdb_delete(cache->tdb, string_tdb_data(key_str));
3415 return NT_STATUS_OK;
3418 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3419 if (ret == 0) {
3420 return NT_STATUS_OK;
3421 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3422 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3425 ZERO_STRUCTP(oldest);
3427 for (cred = wcache_cred_list; cred; cred = cred->next) {
3429 TDB_DATA data;
3430 time_t t;
3432 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3433 if (!data.dptr) {
3434 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3435 cred->name));
3436 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3437 goto done;
3440 t = IVAL(data.dptr, 0);
3441 SAFE_FREE(data.dptr);
3443 if (!oldest) {
3444 oldest = SMB_MALLOC_P(struct cred_list);
3445 if (oldest == NULL) {
3446 status = NT_STATUS_NO_MEMORY;
3447 goto done;
3450 fstrcpy(oldest->name, cred->name);
3451 oldest->created = t;
3452 continue;
3455 if (t < oldest->created) {
3456 fstrcpy(oldest->name, cred->name);
3457 oldest->created = t;
3461 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3462 status = NT_STATUS_OK;
3463 } else {
3464 status = NT_STATUS_UNSUCCESSFUL;
3466 done:
3467 SAFE_FREE(wcache_cred_list);
3468 SAFE_FREE(oldest);
3470 return status;
3473 /* Change the global online/offline state. */
3474 bool set_global_winbindd_state_offline(void)
3476 TDB_DATA data;
3478 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3480 /* Only go offline if someone has created
3481 the key "WINBINDD_OFFLINE" in the cache tdb. */
3483 if (wcache == NULL || wcache->tdb == NULL) {
3484 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3485 return false;
3488 if (!lp_winbind_offline_logon()) {
3489 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3490 return false;
3493 if (global_winbindd_offline_state) {
3494 /* Already offline. */
3495 return true;
3498 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3500 if (!data.dptr || data.dsize != 4) {
3501 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3502 SAFE_FREE(data.dptr);
3503 return false;
3504 } else {
3505 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3506 global_winbindd_offline_state = true;
3507 SAFE_FREE(data.dptr);
3508 return true;
3512 void set_global_winbindd_state_online(void)
3514 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3516 if (!lp_winbind_offline_logon()) {
3517 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3518 return;
3521 if (!global_winbindd_offline_state) {
3522 /* Already online. */
3523 return;
3525 global_winbindd_offline_state = false;
3527 if (!wcache->tdb) {
3528 return;
3531 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3532 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3535 bool get_global_winbindd_state_offline(void)
3537 return global_winbindd_offline_state;
3540 /***********************************************************************
3541 Validate functions for all possible cache tdb keys.
3542 ***********************************************************************/
3544 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3545 struct tdb_validation_status *state)
3547 struct cache_entry *centry;
3549 centry = SMB_XMALLOC_P(struct cache_entry);
3550 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3551 if (!centry->data) {
3552 SAFE_FREE(centry);
3553 return NULL;
3555 centry->len = data.dsize;
3556 centry->ofs = 0;
3558 if (centry->len < 16) {
3559 /* huh? corrupt cache? */
3560 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3561 "(len < 16) ?\n", kstr));
3562 centry_free(centry);
3563 state->bad_entry = true;
3564 state->success = false;
3565 return NULL;
3568 centry->status = NT_STATUS(centry_uint32(centry));
3569 centry->sequence_number = centry_uint32(centry);
3570 centry->timeout = centry_uint64_t(centry);
3571 return centry;
3574 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3575 struct tdb_validation_status *state)
3577 if (dbuf.dsize != 8) {
3578 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3579 keystr, (unsigned int)dbuf.dsize ));
3580 state->bad_entry = true;
3581 return 1;
3583 return 0;
3586 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3587 struct tdb_validation_status *state)
3589 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3590 if (!centry) {
3591 return 1;
3594 (void)centry_uint32(centry);
3595 if (NT_STATUS_IS_OK(centry->status)) {
3596 struct dom_sid sid;
3597 (void)centry_sid(centry, &sid);
3600 centry_free(centry);
3602 if (!(state->success)) {
3603 return 1;
3605 DEBUG(10,("validate_ns: %s ok\n", keystr));
3606 return 0;
3609 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3610 struct tdb_validation_status *state)
3612 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3613 if (!centry) {
3614 return 1;
3617 if (NT_STATUS_IS_OK(centry->status)) {
3618 (void)centry_uint32(centry);
3619 (void)centry_string(centry, mem_ctx);
3620 (void)centry_string(centry, mem_ctx);
3623 centry_free(centry);
3625 if (!(state->success)) {
3626 return 1;
3628 DEBUG(10,("validate_sn: %s ok\n", keystr));
3629 return 0;
3632 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3633 struct tdb_validation_status *state)
3635 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3636 struct dom_sid sid;
3638 if (!centry) {
3639 return 1;
3642 (void)centry_string(centry, mem_ctx);
3643 (void)centry_string(centry, mem_ctx);
3644 (void)centry_string(centry, mem_ctx);
3645 (void)centry_string(centry, mem_ctx);
3646 (void)centry_uint32(centry);
3647 (void)centry_sid(centry, &sid);
3648 (void)centry_sid(centry, &sid);
3650 centry_free(centry);
3652 if (!(state->success)) {
3653 return 1;
3655 DEBUG(10,("validate_u: %s ok\n", keystr));
3656 return 0;
3659 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3660 struct tdb_validation_status *state)
3662 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3664 if (!centry) {
3665 return 1;
3668 (void)centry_nttime(centry);
3669 (void)centry_nttime(centry);
3670 (void)centry_uint16(centry);
3672 centry_free(centry);
3674 if (!(state->success)) {
3675 return 1;
3677 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3678 return 0;
3681 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3682 struct tdb_validation_status *state)
3684 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3686 if (!centry) {
3687 return 1;
3690 (void)centry_uint16(centry);
3691 (void)centry_uint16(centry);
3692 (void)centry_uint32(centry);
3693 (void)centry_nttime(centry);
3694 (void)centry_nttime(centry);
3696 centry_free(centry);
3698 if (!(state->success)) {
3699 return 1;
3701 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3702 return 0;
3705 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3706 struct tdb_validation_status *state)
3708 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3710 if (!centry) {
3711 return 1;
3714 (void)centry_time(centry);
3715 (void)centry_hash16(centry, mem_ctx);
3717 /* We only have 17 bytes more data in the salted cred case. */
3718 if (centry->len - centry->ofs == 17) {
3719 (void)centry_hash16(centry, mem_ctx);
3722 centry_free(centry);
3724 if (!(state->success)) {
3725 return 1;
3727 DEBUG(10,("validate_cred: %s ok\n", keystr));
3728 return 0;
3731 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3732 struct tdb_validation_status *state)
3734 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3735 int32 num_entries, i;
3737 if (!centry) {
3738 return 1;
3741 num_entries = (int32)centry_uint32(centry);
3743 for (i=0; i< num_entries; i++) {
3744 struct dom_sid sid;
3745 (void)centry_string(centry, mem_ctx);
3746 (void)centry_string(centry, mem_ctx);
3747 (void)centry_string(centry, mem_ctx);
3748 (void)centry_string(centry, mem_ctx);
3749 (void)centry_sid(centry, &sid);
3750 (void)centry_sid(centry, &sid);
3753 centry_free(centry);
3755 if (!(state->success)) {
3756 return 1;
3758 DEBUG(10,("validate_ul: %s ok\n", keystr));
3759 return 0;
3762 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3763 struct tdb_validation_status *state)
3765 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3766 int32 num_entries, i;
3768 if (!centry) {
3769 return 1;
3772 num_entries = centry_uint32(centry);
3774 for (i=0; i< num_entries; i++) {
3775 (void)centry_string(centry, mem_ctx);
3776 (void)centry_string(centry, mem_ctx);
3777 (void)centry_uint32(centry);
3780 centry_free(centry);
3782 if (!(state->success)) {
3783 return 1;
3785 DEBUG(10,("validate_gl: %s ok\n", keystr));
3786 return 0;
3789 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3790 struct tdb_validation_status *state)
3792 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3793 int32 num_groups, i;
3795 if (!centry) {
3796 return 1;
3799 num_groups = centry_uint32(centry);
3801 for (i=0; i< num_groups; i++) {
3802 struct dom_sid sid;
3803 centry_sid(centry, &sid);
3806 centry_free(centry);
3808 if (!(state->success)) {
3809 return 1;
3811 DEBUG(10,("validate_ug: %s ok\n", keystr));
3812 return 0;
3815 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3816 struct tdb_validation_status *state)
3818 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3819 int32 num_aliases, i;
3821 if (!centry) {
3822 return 1;
3825 num_aliases = centry_uint32(centry);
3827 for (i=0; i < num_aliases; i++) {
3828 (void)centry_uint32(centry);
3831 centry_free(centry);
3833 if (!(state->success)) {
3834 return 1;
3836 DEBUG(10,("validate_ua: %s ok\n", keystr));
3837 return 0;
3840 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3841 struct tdb_validation_status *state)
3843 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3844 int32 num_names, i;
3846 if (!centry) {
3847 return 1;
3850 num_names = centry_uint32(centry);
3852 for (i=0; i< num_names; i++) {
3853 struct dom_sid sid;
3854 centry_sid(centry, &sid);
3855 (void)centry_string(centry, mem_ctx);
3856 (void)centry_uint32(centry);
3859 centry_free(centry);
3861 if (!(state->success)) {
3862 return 1;
3864 DEBUG(10,("validate_gm: %s ok\n", keystr));
3865 return 0;
3868 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3869 struct tdb_validation_status *state)
3871 /* Can't say anything about this other than must be nonzero. */
3872 if (dbuf.dsize == 0) {
3873 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3874 keystr));
3875 state->bad_entry = true;
3876 state->success = false;
3877 return 1;
3880 DEBUG(10,("validate_dr: %s ok\n", keystr));
3881 return 0;
3884 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3885 struct tdb_validation_status *state)
3887 /* Can't say anything about this other than must be nonzero. */
3888 if (dbuf.dsize == 0) {
3889 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3890 keystr));
3891 state->bad_entry = true;
3892 state->success = false;
3893 return 1;
3896 DEBUG(10,("validate_de: %s ok\n", keystr));
3897 return 0;
3900 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3901 TDB_DATA dbuf, struct tdb_validation_status *state)
3903 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3905 if (!centry) {
3906 return 1;
3909 (void)centry_string(centry, mem_ctx);
3910 (void)centry_string(centry, mem_ctx);
3911 (void)centry_string(centry, mem_ctx);
3912 (void)centry_uint32(centry);
3914 centry_free(centry);
3916 if (!(state->success)) {
3917 return 1;
3919 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3920 return 0;
3923 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3924 TDB_DATA dbuf,
3925 struct tdb_validation_status *state)
3927 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3929 if (!centry) {
3930 return 1;
3933 (void)centry_string( centry, mem_ctx );
3935 centry_free(centry);
3937 if (!(state->success)) {
3938 return 1;
3940 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3941 return 0;
3944 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3945 TDB_DATA dbuf,
3946 struct tdb_validation_status *state)
3948 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3950 if (!centry) {
3951 return 1;
3954 (void)centry_string( centry, mem_ctx );
3956 centry_free(centry);
3958 if (!(state->success)) {
3959 return 1;
3961 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3962 return 0;
3965 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3966 TDB_DATA dbuf,
3967 struct tdb_validation_status *state)
3969 if (dbuf.dsize == 0) {
3970 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3971 "key %s (len ==0) ?\n", keystr));
3972 state->bad_entry = true;
3973 state->success = false;
3974 return 1;
3977 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3978 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3979 return 0;
3982 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3983 struct tdb_validation_status *state)
3985 if (dbuf.dsize != 4) {
3986 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3987 keystr, (unsigned int)dbuf.dsize ));
3988 state->bad_entry = true;
3989 state->success = false;
3990 return 1;
3992 DEBUG(10,("validate_offline: %s ok\n", keystr));
3993 return 0;
3996 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3997 struct tdb_validation_status *state)
4000 * Ignore validation for now. The proper way to do this is with a
4001 * checksum. Just pure parsing does not really catch much.
4003 return 0;
4006 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4007 struct tdb_validation_status *state)
4009 if (dbuf.dsize != 4) {
4010 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4011 "key %s (len %u != 4) ?\n",
4012 keystr, (unsigned int)dbuf.dsize));
4013 state->bad_entry = true;
4014 state->success = false;
4015 return 1;
4018 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4019 return 0;
4022 /***********************************************************************
4023 A list of all possible cache tdb keys with associated validation
4024 functions.
4025 ***********************************************************************/
4027 struct key_val_struct {
4028 const char *keyname;
4029 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4030 } key_val[] = {
4031 {"SEQNUM/", validate_seqnum},
4032 {"NS/", validate_ns},
4033 {"SN/", validate_sn},
4034 {"U/", validate_u},
4035 {"LOC_POL/", validate_loc_pol},
4036 {"PWD_POL/", validate_pwd_pol},
4037 {"CRED/", validate_cred},
4038 {"UL/", validate_ul},
4039 {"GL/", validate_gl},
4040 {"UG/", validate_ug},
4041 {"UA", validate_ua},
4042 {"GM/", validate_gm},
4043 {"DR/", validate_dr},
4044 {"DE/", validate_de},
4045 {"NSS/PWINFO/", validate_pwinfo},
4046 {"TRUSTDOMCACHE/", validate_trustdomcache},
4047 {"NSS/NA/", validate_nss_na},
4048 {"NSS/AN/", validate_nss_an},
4049 {"WINBINDD_OFFLINE", validate_offline},
4050 {"NDR/", validate_ndr},
4051 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4052 {NULL, NULL}
4055 /***********************************************************************
4056 Function to look at every entry in the tdb and validate it as far as
4057 possible.
4058 ***********************************************************************/
4060 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4062 int i;
4063 unsigned int max_key_len = 1024;
4064 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4066 /* Paranoia check. */
4067 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4068 max_key_len = 1024 * 1024;
4070 if (kbuf.dsize > max_key_len) {
4071 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4072 "(%u) > (%u)\n\n",
4073 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4074 return 1;
4077 for (i = 0; key_val[i].keyname; i++) {
4078 size_t namelen = strlen(key_val[i].keyname);
4079 if (kbuf.dsize >= namelen && (
4080 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4081 TALLOC_CTX *mem_ctx;
4082 char *keystr;
4083 int ret;
4085 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4086 if (!keystr) {
4087 return 1;
4089 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4090 keystr[kbuf.dsize] = '\0';
4092 mem_ctx = talloc_init("validate_ctx");
4093 if (!mem_ctx) {
4094 SAFE_FREE(keystr);
4095 return 1;
4098 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4099 v_state);
4101 SAFE_FREE(keystr);
4102 talloc_destroy(mem_ctx);
4103 return ret;
4107 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4108 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4109 DEBUG(0,("data :\n"));
4110 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4111 v_state->unknown_key = true;
4112 v_state->success = false;
4113 return 1; /* terminate. */
4116 static void validate_panic(const char *const why)
4118 DEBUG(0,("validating cache: would panic %s\n", why ));
4119 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4120 exit(47);
4123 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4124 TDB_DATA key,
4125 TDB_DATA data,
4126 void *state)
4128 uint64_t ctimeout;
4129 TDB_DATA blob;
4131 if (is_non_centry_key(key)) {
4132 return 0;
4135 if (data.dptr == NULL || data.dsize == 0) {
4136 if (tdb_delete(tdb, key) < 0) {
4137 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4138 key.dptr));
4139 return 1;
4143 /* add timeout to blob (uint64_t) */
4144 blob.dsize = data.dsize + 8;
4146 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4147 if (blob.dptr == NULL) {
4148 return 1;
4150 memset(blob.dptr, 0, blob.dsize);
4152 /* copy status and seqnum */
4153 memcpy(blob.dptr, data.dptr, 8);
4155 /* add timeout */
4156 ctimeout = lp_winbind_cache_time() + time(NULL);
4157 SBVAL(blob.dptr, 8, ctimeout);
4159 /* copy the rest */
4160 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4162 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4163 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4164 key.dptr));
4165 SAFE_FREE(blob.dptr);
4166 return 1;
4169 SAFE_FREE(blob.dptr);
4170 return 0;
4173 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4175 int rc;
4177 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4179 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4180 if (rc < 0) {
4181 return false;
4184 return true;
4187 /***********************************************************************
4188 Try and validate every entry in the winbindd cache. If we fail here,
4189 delete the cache tdb and return non-zero.
4190 ***********************************************************************/
4192 int winbindd_validate_cache(void)
4194 int ret = -1;
4195 const char *tdb_path = state_path("winbindd_cache.tdb");
4196 TDB_CONTEXT *tdb = NULL;
4197 uint32_t vers_id;
4198 bool ok;
4200 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4201 smb_panic_fn = validate_panic;
4203 tdb = tdb_open_log(tdb_path,
4204 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4205 TDB_INCOMPATIBLE_HASH |
4206 ( lp_winbind_offline_logon()
4207 ? TDB_DEFAULT
4208 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4209 O_RDWR|O_CREAT,
4210 0600);
4211 if (!tdb) {
4212 DEBUG(0, ("winbindd_validate_cache: "
4213 "error opening/initializing tdb\n"));
4214 goto done;
4217 /* Version check and upgrade code. */
4218 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4219 DEBUG(10, ("Fresh database\n"));
4220 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4221 vers_id = WINBINDD_CACHE_VERSION;
4224 if (vers_id != WINBINDD_CACHE_VERSION) {
4225 if (vers_id == WINBINDD_CACHE_VER1) {
4226 ok = wbcache_upgrade_v1_to_v2(tdb);
4227 if (!ok) {
4228 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4229 unlink(tdb_path);
4230 goto done;
4233 tdb_store_uint32(tdb,
4234 WINBINDD_CACHE_VERSION_KEYSTR,
4235 WINBINDD_CACHE_VERSION);
4236 vers_id = WINBINDD_CACHE_VER2;
4240 tdb_close(tdb);
4242 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4244 if (ret != 0) {
4245 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4246 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4247 unlink(tdb_path);
4250 done:
4251 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4252 smb_panic_fn = smb_panic;
4253 return ret;
4256 /***********************************************************************
4257 Try and validate every entry in the winbindd cache.
4258 ***********************************************************************/
4260 int winbindd_validate_cache_nobackup(void)
4262 int ret = -1;
4263 const char *tdb_path = state_path("winbindd_cache.tdb");
4265 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4266 smb_panic_fn = validate_panic;
4269 if (wcache == NULL || wcache->tdb == NULL) {
4270 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4271 } else {
4272 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4275 if (ret != 0) {
4276 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4277 "successful.\n"));
4280 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4281 "function\n"));
4282 smb_panic_fn = smb_panic;
4283 return ret;
4286 bool winbindd_cache_validate_and_initialize(void)
4288 close_winbindd_cache();
4290 if (lp_winbind_offline_logon()) {
4291 if (winbindd_validate_cache() < 0) {
4292 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4293 "could be restored.\n"));
4297 return initialize_winbindd_cache();
4300 /*********************************************************************
4301 ********************************************************************/
4303 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4304 struct winbindd_tdc_domain **domains,
4305 size_t *num_domains )
4307 struct winbindd_tdc_domain *list = NULL;
4308 size_t idx;
4309 int i;
4310 bool set_only = false;
4312 /* don't allow duplicates */
4314 idx = *num_domains;
4315 list = *domains;
4317 for ( i=0; i< (*num_domains); i++ ) {
4318 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4319 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4320 new_dom->name));
4321 idx = i;
4322 set_only = true;
4324 break;
4328 if ( !set_only ) {
4329 if ( !*domains ) {
4330 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4331 idx = 0;
4332 } else {
4333 list = talloc_realloc( *domains, *domains,
4334 struct winbindd_tdc_domain,
4335 (*num_domains)+1);
4336 idx = *num_domains;
4339 ZERO_STRUCT( list[idx] );
4342 if ( !list )
4343 return false;
4345 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4346 if (list[idx].domain_name == NULL) {
4347 return false;
4349 if (new_dom->alt_name != NULL) {
4350 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4351 if (list[idx].dns_name == NULL) {
4352 return false;
4356 if ( !is_null_sid( &new_dom->sid ) ) {
4357 sid_copy( &list[idx].sid, &new_dom->sid );
4358 } else {
4359 sid_copy(&list[idx].sid, &global_sid_NULL);
4362 if ( new_dom->domain_flags != 0x0 )
4363 list[idx].trust_flags = new_dom->domain_flags;
4365 if ( new_dom->domain_type != 0x0 )
4366 list[idx].trust_type = new_dom->domain_type;
4368 if ( new_dom->domain_trust_attribs != 0x0 )
4369 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4371 if ( !set_only ) {
4372 *domains = list;
4373 *num_domains = idx + 1;
4376 return true;
4379 /*********************************************************************
4380 ********************************************************************/
4382 static TDB_DATA make_tdc_key( const char *domain_name )
4384 char *keystr = NULL;
4385 TDB_DATA key = { NULL, 0 };
4387 if ( !domain_name ) {
4388 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4389 return key;
4392 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4393 return key;
4395 key = string_term_tdb_data(keystr);
4397 return key;
4400 /*********************************************************************
4401 ********************************************************************/
4403 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4404 size_t num_domains,
4405 unsigned char **buf )
4407 unsigned char *buffer = NULL;
4408 int len = 0;
4409 int buflen = 0;
4410 int i = 0;
4412 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4413 (int)num_domains));
4415 buflen = 0;
4417 again:
4418 len = 0;
4420 /* Store the number of array items first */
4421 len += tdb_pack( buffer+len, buflen-len, "d",
4422 num_domains );
4424 /* now pack each domain trust record */
4425 for ( i=0; i<num_domains; i++ ) {
4427 fstring tmp;
4429 if ( buflen > 0 ) {
4430 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4431 domains[i].domain_name,
4432 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4435 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4436 domains[i].domain_name,
4437 domains[i].dns_name ? domains[i].dns_name : "",
4438 sid_to_fstring(tmp, &domains[i].sid),
4439 domains[i].trust_flags,
4440 domains[i].trust_attribs,
4441 domains[i].trust_type );
4444 if ( buflen < len ) {
4445 SAFE_FREE(buffer);
4446 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4447 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4448 buflen = -1;
4449 goto done;
4451 buflen = len;
4452 goto again;
4455 *buf = buffer;
4457 done:
4458 return buflen;
4461 /*********************************************************************
4462 ********************************************************************/
4464 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4465 struct winbindd_tdc_domain **domains )
4467 fstring domain_name, dns_name, sid_string;
4468 uint32 type, attribs, flags;
4469 int num_domains;
4470 int len = 0;
4471 int i;
4472 struct winbindd_tdc_domain *list = NULL;
4474 /* get the number of domains */
4475 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4476 if ( len == -1 ) {
4477 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4478 return 0;
4481 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4482 if ( !list ) {
4483 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4484 return 0;
4487 for ( i=0; i<num_domains; i++ ) {
4488 int this_len;
4490 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4491 domain_name,
4492 dns_name,
4493 sid_string,
4494 &flags,
4495 &attribs,
4496 &type );
4498 if ( this_len == -1 ) {
4499 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4500 TALLOC_FREE( list );
4501 return 0;
4503 len += this_len;
4505 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4506 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4507 domain_name, dns_name, sid_string,
4508 flags, attribs, type));
4510 list[i].domain_name = talloc_strdup( list, domain_name );
4511 list[i].dns_name = NULL;
4512 if (dns_name[0] != '\0') {
4513 list[i].dns_name = talloc_strdup(list, dns_name);
4515 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4516 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4517 domain_name));
4519 list[i].trust_flags = flags;
4520 list[i].trust_attribs = attribs;
4521 list[i].trust_type = type;
4524 *domains = list;
4526 return num_domains;
4529 /*********************************************************************
4530 ********************************************************************/
4532 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4534 TDB_DATA key = make_tdc_key( lp_workgroup() );
4535 TDB_DATA data = { NULL, 0 };
4536 int ret;
4538 if ( !key.dptr )
4539 return false;
4541 /* See if we were asked to delete the cache entry */
4543 if ( !domains ) {
4544 ret = tdb_delete( wcache->tdb, key );
4545 goto done;
4548 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4550 if ( !data.dptr ) {
4551 ret = -1;
4552 goto done;
4555 ret = tdb_store( wcache->tdb, key, data, 0 );
4557 done:
4558 SAFE_FREE( data.dptr );
4559 SAFE_FREE( key.dptr );
4561 return ( ret == 0 );
4564 /*********************************************************************
4565 ********************************************************************/
4567 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4569 TDB_DATA key = make_tdc_key( lp_workgroup() );
4570 TDB_DATA data = { NULL, 0 };
4572 *domains = NULL;
4573 *num_domains = 0;
4575 if ( !key.dptr )
4576 return false;
4578 data = tdb_fetch_compat( wcache->tdb, key );
4580 SAFE_FREE( key.dptr );
4582 if ( !data.dptr )
4583 return false;
4585 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4587 SAFE_FREE( data.dptr );
4589 if ( !*domains )
4590 return false;
4592 return true;
4595 /*********************************************************************
4596 ********************************************************************/
4598 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4600 struct winbindd_tdc_domain *dom_list = NULL;
4601 size_t num_domains = 0;
4602 bool ret = false;
4604 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4605 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4606 domain->name, domain->alt_name,
4607 sid_string_dbg(&domain->sid),
4608 domain->domain_flags,
4609 domain->domain_trust_attribs,
4610 domain->domain_type));
4612 if ( !init_wcache() ) {
4613 return false;
4616 /* fetch the list */
4618 wcache_tdc_fetch_list( &dom_list, &num_domains );
4620 /* add the new domain */
4622 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4623 goto done;
4626 /* pack the domain */
4628 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4629 goto done;
4632 /* Success */
4634 ret = true;
4635 done:
4636 TALLOC_FREE( dom_list );
4638 return ret;
4641 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4642 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4644 struct winbindd_tdc_domain *dst;
4646 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4647 if (dst == NULL) {
4648 goto fail;
4650 dst->domain_name = talloc_strdup(dst, src->domain_name);
4651 if (dst->domain_name == NULL) {
4652 goto fail;
4655 dst->dns_name = NULL;
4656 if (src->dns_name != NULL) {
4657 dst->dns_name = talloc_strdup(dst, src->dns_name);
4658 if (dst->dns_name == NULL) {
4659 goto fail;
4663 sid_copy(&dst->sid, &src->sid);
4664 dst->trust_flags = src->trust_flags;
4665 dst->trust_type = src->trust_type;
4666 dst->trust_attribs = src->trust_attribs;
4667 return dst;
4668 fail:
4669 TALLOC_FREE(dst);
4670 return NULL;
4673 /*********************************************************************
4674 ********************************************************************/
4676 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4678 struct winbindd_tdc_domain *dom_list = NULL;
4679 size_t num_domains = 0;
4680 int i;
4681 struct winbindd_tdc_domain *d = NULL;
4683 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4685 if ( !init_wcache() ) {
4686 return NULL;
4689 /* fetch the list */
4691 wcache_tdc_fetch_list( &dom_list, &num_domains );
4693 for ( i=0; i<num_domains; i++ ) {
4694 if ( strequal(name, dom_list[i].domain_name) ||
4695 strequal(name, dom_list[i].dns_name) )
4697 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4698 name));
4700 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4701 break;
4705 TALLOC_FREE( dom_list );
4707 return d;
4710 /*********************************************************************
4711 ********************************************************************/
4713 struct winbindd_tdc_domain*
4714 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4715 const struct dom_sid *sid)
4717 struct winbindd_tdc_domain *dom_list = NULL;
4718 size_t num_domains = 0;
4719 int i;
4720 struct winbindd_tdc_domain *d = NULL;
4722 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4723 sid_string_dbg(sid)));
4725 if (!init_wcache()) {
4726 return NULL;
4729 /* fetch the list */
4731 wcache_tdc_fetch_list(&dom_list, &num_domains);
4733 for (i = 0; i<num_domains; i++) {
4734 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4735 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4736 "Found domain %s for SID %s\n",
4737 dom_list[i].domain_name,
4738 sid_string_dbg(sid)));
4740 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4741 break;
4745 TALLOC_FREE(dom_list);
4747 return d;
4751 /*********************************************************************
4752 ********************************************************************/
4754 void wcache_tdc_clear( void )
4756 if ( !init_wcache() )
4757 return;
4759 wcache_tdc_store_list( NULL, 0 );
4761 return;
4765 /*********************************************************************
4766 ********************************************************************/
4768 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4769 NTSTATUS status,
4770 const struct dom_sid *user_sid,
4771 const char *homedir,
4772 const char *shell,
4773 const char *gecos,
4774 uint32 gid)
4776 struct cache_entry *centry;
4777 fstring tmp;
4779 if ( (centry = centry_start(domain, status)) == NULL )
4780 return;
4782 centry_put_string( centry, homedir );
4783 centry_put_string( centry, shell );
4784 centry_put_string( centry, gecos );
4785 centry_put_uint32( centry, gid );
4787 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4789 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4791 centry_free(centry);
4794 #ifdef HAVE_ADS
4796 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4797 const struct dom_sid *user_sid,
4798 TALLOC_CTX *ctx,
4799 const char **homedir, const char **shell,
4800 const char **gecos, gid_t *p_gid)
4802 struct winbind_cache *cache = get_cache(domain);
4803 struct cache_entry *centry = NULL;
4804 NTSTATUS nt_status;
4805 fstring tmp;
4807 if (!cache->tdb)
4808 goto do_query;
4810 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4811 sid_to_fstring(tmp, user_sid));
4813 if (!centry)
4814 goto do_query;
4816 *homedir = centry_string( centry, ctx );
4817 *shell = centry_string( centry, ctx );
4818 *gecos = centry_string( centry, ctx );
4819 *p_gid = centry_uint32( centry );
4821 centry_free(centry);
4823 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4824 sid_string_dbg(user_sid)));
4826 return NT_STATUS_OK;
4828 do_query:
4830 nt_status = nss_get_info( domain->name, user_sid, ctx,
4831 homedir, shell, gecos, p_gid );
4833 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4835 if ( NT_STATUS_IS_OK(nt_status) ) {
4836 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4837 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4838 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4839 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4841 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4842 *homedir, *shell, *gecos, *p_gid );
4845 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4846 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4847 domain->name ));
4848 set_domain_offline( domain );
4851 return nt_status;
4854 #endif
4856 /* the cache backend methods are exposed via this structure */
4857 struct winbindd_methods cache_methods = {
4858 true,
4859 query_user_list,
4860 enum_dom_groups,
4861 enum_local_groups,
4862 name_to_sid,
4863 sid_to_name,
4864 rids_to_names,
4865 query_user,
4866 lookup_usergroups,
4867 lookup_useraliases,
4868 lookup_groupmem,
4869 sequence_number,
4870 lockout_policy,
4871 password_policy,
4872 trusted_domains
4875 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4876 uint32_t opnum, const DATA_BLOB *req,
4877 TDB_DATA *pkey)
4879 char *key;
4880 size_t keylen;
4882 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4883 if (key == NULL) {
4884 return false;
4886 keylen = talloc_get_size(key) - 1;
4888 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4889 if (key == NULL) {
4890 return false;
4892 memcpy(key + keylen, req->data, req->length);
4894 pkey->dptr = (uint8_t *)key;
4895 pkey->dsize = talloc_get_size(key);
4896 return true;
4899 static bool wcache_opnum_cacheable(uint32_t opnum)
4901 switch (opnum) {
4902 case NDR_WBINT_PING:
4903 case NDR_WBINT_QUERYSEQUENCENUMBER:
4904 case NDR_WBINT_ALLOCATEUID:
4905 case NDR_WBINT_ALLOCATEGID:
4906 case NDR_WBINT_CHECKMACHINEACCOUNT:
4907 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4908 case NDR_WBINT_PINGDC:
4909 return false;
4911 return true;
4914 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4915 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4917 TDB_DATA key, data;
4918 bool ret = false;
4920 if (!wcache_opnum_cacheable(opnum) ||
4921 is_my_own_sam_domain(domain) ||
4922 is_builtin_domain(domain)) {
4923 return false;
4926 if (wcache->tdb == NULL) {
4927 return false;
4930 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4931 return false;
4933 data = tdb_fetch_compat(wcache->tdb, key);
4934 TALLOC_FREE(key.dptr);
4936 if (data.dptr == NULL) {
4937 return false;
4939 if (data.dsize < 12) {
4940 goto fail;
4943 if (!is_domain_offline(domain)) {
4944 uint32_t entry_seqnum, dom_seqnum, last_check;
4945 uint64_t entry_timeout;
4947 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4948 &last_check)) {
4949 goto fail;
4951 entry_seqnum = IVAL(data.dptr, 0);
4952 if (entry_seqnum != dom_seqnum) {
4953 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4954 (int)entry_seqnum));
4955 goto fail;
4957 entry_timeout = BVAL(data.dptr, 4);
4958 if (time(NULL) > entry_timeout) {
4959 DEBUG(10, ("Entry has timed out\n"));
4960 goto fail;
4964 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4965 data.dsize - 12);
4966 if (resp->data == NULL) {
4967 DEBUG(10, ("talloc failed\n"));
4968 goto fail;
4970 resp->length = data.dsize - 12;
4972 ret = true;
4973 fail:
4974 SAFE_FREE(data.dptr);
4975 return ret;
4978 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4979 const DATA_BLOB *req, const DATA_BLOB *resp)
4981 TDB_DATA key, data;
4982 uint32_t dom_seqnum, last_check;
4983 uint64_t timeout;
4985 if (!wcache_opnum_cacheable(opnum) ||
4986 is_my_own_sam_domain(domain) ||
4987 is_builtin_domain(domain)) {
4988 return;
4991 if (wcache->tdb == NULL) {
4992 return;
4995 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4996 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4997 domain->name));
4998 return;
5001 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5002 return;
5005 timeout = time(NULL) + lp_winbind_cache_time();
5007 data.dsize = resp->length + 12;
5008 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5009 if (data.dptr == NULL) {
5010 goto done;
5013 SIVAL(data.dptr, 0, dom_seqnum);
5014 SBVAL(data.dptr, 4, timeout);
5015 memcpy(data.dptr + 12, resp->data, resp->length);
5017 tdb_store(wcache->tdb, key, data, 0);
5019 done:
5020 TALLOC_FREE(key.dptr);
5021 return;