s4:drsuapi: always use the current uptodateness_vector
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob76970d6be404178700b2c265028fc9d44c68f92e
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;
946 centry_put_uint32(centry, type);
947 centry_put_sid(centry, sid);
948 fstrcpy(uname, name);
949 (void)strupper_m(uname);
950 centry_end(centry, "NS/%s/%s", domain_name, uname);
951 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
952 uname, sid_string_dbg(sid), nt_errstr(status)));
953 centry_free(centry);
956 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
957 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring sid_string;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if (NT_STATUS_IS_OK(status)) {
967 centry_put_uint32(centry, type);
968 centry_put_string(centry, domain_name);
969 centry_put_string(centry, name);
972 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
973 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
974 domain_name, name, nt_errstr(status)));
975 centry_free(centry);
979 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
980 struct wbint_userinfo *info)
982 struct cache_entry *centry;
983 fstring sid_string;
985 if (is_null_sid(&info->user_sid)) {
986 return;
989 centry = centry_start(domain, status);
990 if (!centry)
991 return;
992 centry_put_string(centry, info->acct_name);
993 centry_put_string(centry, info->full_name);
994 centry_put_string(centry, info->homedir);
995 centry_put_string(centry, info->shell);
996 centry_put_uint32(centry, info->primary_gid);
997 centry_put_sid(centry, &info->user_sid);
998 centry_put_sid(centry, &info->group_sid);
999 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1000 &info->user_sid));
1001 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 centry_free(centry);
1005 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1006 NTSTATUS status,
1007 struct samr_DomInfo12 *lockout_policy)
1009 struct cache_entry *centry;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1015 centry_put_nttime(centry, lockout_policy->lockout_duration);
1016 centry_put_nttime(centry, lockout_policy->lockout_window);
1017 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1019 centry_end(centry, "LOC_POL/%s", domain->name);
1021 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1023 centry_free(centry);
1028 static void wcache_save_password_policy(struct winbindd_domain *domain,
1029 NTSTATUS status,
1030 struct samr_DomInfo1 *policy)
1032 struct cache_entry *centry;
1034 centry = centry_start(domain, status);
1035 if (!centry)
1036 return;
1038 centry_put_uint16(centry, policy->min_password_length);
1039 centry_put_uint16(centry, policy->password_history_length);
1040 centry_put_uint32(centry, policy->password_properties);
1041 centry_put_nttime(centry, policy->max_password_age);
1042 centry_put_nttime(centry, policy->min_password_age);
1044 centry_end(centry, "PWD_POL/%s", domain->name);
1046 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1048 centry_free(centry);
1051 /***************************************************************************
1052 ***************************************************************************/
1054 static void wcache_save_username_alias(struct winbindd_domain *domain,
1055 NTSTATUS status,
1056 const char *name, const char *alias)
1058 struct cache_entry *centry;
1059 fstring uname;
1061 if ( (centry = centry_start(domain, status)) == NULL )
1062 return;
1064 centry_put_string( centry, alias );
1066 fstrcpy(uname, name);
1067 (void)strupper_m(uname);
1068 centry_end(centry, "NSS/NA/%s", uname);
1070 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1072 centry_free(centry);
1075 static void wcache_save_alias_username(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *alias, const char *name)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, name );
1087 fstrcpy(uname, alias);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/AN/%s", uname);
1091 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1093 centry_free(centry);
1096 /***************************************************************************
1097 ***************************************************************************/
1099 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1100 struct winbindd_domain *domain,
1101 const char *name, char **alias )
1103 struct winbind_cache *cache = get_cache(domain);
1104 struct cache_entry *centry = NULL;
1105 NTSTATUS status;
1106 char *upper_name;
1108 if ( domain->internal )
1109 return NT_STATUS_NOT_SUPPORTED;
1111 if (!cache->tdb)
1112 goto do_query;
1114 upper_name = talloc_strdup(mem_ctx, name);
1115 if (upper_name == NULL) {
1116 return NT_STATUS_NO_MEMORY;
1118 if (!strupper_m(upper_name)) {
1119 talloc_free(upper_name);
1120 return NT_STATUS_INVALID_PARAMETER;
1123 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1125 talloc_free(upper_name);
1127 if (!centry)
1128 goto do_query;
1130 status = centry->status;
1132 if (!NT_STATUS_IS_OK(status)) {
1133 centry_free(centry);
1134 return status;
1137 *alias = centry_string( centry, mem_ctx );
1139 centry_free(centry);
1141 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1142 name, *alias ? *alias : "(none)"));
1144 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1146 do_query:
1148 /* If its not in cache and we are offline, then fail */
1150 if ( get_global_winbindd_state_offline() || !domain->online ) {
1151 DEBUG(8,("resolve_username_to_alias: rejecting query "
1152 "in offline mode\n"));
1153 return NT_STATUS_NOT_FOUND;
1156 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1158 if ( NT_STATUS_IS_OK( status ) ) {
1159 wcache_save_username_alias(domain, status, name, *alias);
1162 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1163 wcache_save_username_alias(domain, status, name, "(NULL)");
1166 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1167 nt_errstr(status)));
1169 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1170 set_domain_offline( domain );
1173 return status;
1176 /***************************************************************************
1177 ***************************************************************************/
1179 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1180 struct winbindd_domain *domain,
1181 const char *alias, char **name )
1183 struct winbind_cache *cache = get_cache(domain);
1184 struct cache_entry *centry = NULL;
1185 NTSTATUS status;
1186 char *upper_name;
1188 if ( domain->internal )
1189 return NT_STATUS_NOT_SUPPORTED;
1191 if (!cache->tdb)
1192 goto do_query;
1194 upper_name = talloc_strdup(mem_ctx, alias);
1195 if (upper_name == NULL) {
1196 return NT_STATUS_NO_MEMORY;
1198 if (!strupper_m(upper_name)) {
1199 talloc_free(upper_name);
1200 return NT_STATUS_INVALID_PARAMETER;
1203 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1205 talloc_free(upper_name);
1207 if (!centry)
1208 goto do_query;
1210 status = centry->status;
1212 if (!NT_STATUS_IS_OK(status)) {
1213 centry_free(centry);
1214 return status;
1217 *name = centry_string( centry, mem_ctx );
1219 centry_free(centry);
1221 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1222 alias, *name ? *name : "(none)"));
1224 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1226 do_query:
1228 /* If its not in cache and we are offline, then fail */
1230 if ( get_global_winbindd_state_offline() || !domain->online ) {
1231 DEBUG(8,("resolve_alias_to_username: rejecting query "
1232 "in offline mode\n"));
1233 return NT_STATUS_NOT_FOUND;
1236 /* an alias cannot contain a domain prefix or '@' */
1238 if (strchr(alias, '\\') || strchr(alias, '@')) {
1239 DEBUG(10,("resolve_alias_to_username: skipping fully "
1240 "qualified name %s\n", alias));
1241 return NT_STATUS_OBJECT_NAME_INVALID;
1244 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1246 if ( NT_STATUS_IS_OK( status ) ) {
1247 wcache_save_alias_username( domain, status, alias, *name );
1250 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1251 wcache_save_alias_username(domain, status, alias, "(NULL)");
1254 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1255 nt_errstr(status)));
1257 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1258 set_domain_offline( domain );
1261 return status;
1264 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1266 struct winbind_cache *cache = get_cache(domain);
1267 TDB_DATA data;
1268 fstring key_str, tmp;
1269 uint32 rid;
1271 if (!cache->tdb) {
1272 return NT_STATUS_INTERNAL_DB_ERROR;
1275 if (is_null_sid(sid)) {
1276 return NT_STATUS_INVALID_SID;
1279 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1280 return NT_STATUS_INVALID_SID;
1283 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1285 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1286 if (!data.dptr) {
1287 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1290 SAFE_FREE(data.dptr);
1291 return NT_STATUS_OK;
1294 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1295 as new salted ones. */
1297 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1298 TALLOC_CTX *mem_ctx,
1299 const struct dom_sid *sid,
1300 const uint8 **cached_nt_pass,
1301 const uint8 **cached_salt)
1303 struct winbind_cache *cache = get_cache(domain);
1304 struct cache_entry *centry = NULL;
1305 NTSTATUS status;
1306 uint32 rid;
1307 fstring tmp;
1309 if (!cache->tdb) {
1310 return NT_STATUS_INTERNAL_DB_ERROR;
1313 if (is_null_sid(sid)) {
1314 return NT_STATUS_INVALID_SID;
1317 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1318 return NT_STATUS_INVALID_SID;
1321 /* Try and get a salted cred first. If we can't
1322 fall back to an unsalted cred. */
1324 centry = wcache_fetch(cache, domain, "CRED/%s",
1325 sid_to_fstring(tmp, sid));
1326 if (!centry) {
1327 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1328 sid_string_dbg(sid)));
1329 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1333 * We don't use the time element at this moment,
1334 * but we have to consume it, so that we don't
1335 * neet to change the disk format of the cache.
1337 (void)centry_time(centry);
1339 /* In the salted case this isn't actually the nt_hash itself,
1340 but the MD5 of the salt + nt_hash. Let the caller
1341 sort this out. It can tell as we only return the cached_salt
1342 if we are returning a salted cred. */
1344 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1345 if (*cached_nt_pass == NULL) {
1346 fstring sidstr;
1348 sid_to_fstring(sidstr, sid);
1350 /* Bad (old) cred cache. Delete and pretend we
1351 don't have it. */
1352 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1353 sidstr));
1354 wcache_delete("CRED/%s", sidstr);
1355 centry_free(centry);
1356 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1359 /* We only have 17 bytes more data in the salted cred case. */
1360 if (centry->len - centry->ofs == 17) {
1361 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1362 } else {
1363 *cached_salt = NULL;
1366 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1367 if (*cached_salt) {
1368 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1371 status = centry->status;
1373 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1374 sid_string_dbg(sid), nt_errstr(status) ));
1376 centry_free(centry);
1377 return status;
1380 /* Store creds for a SID - only writes out new salted ones. */
1382 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1383 const struct dom_sid *sid,
1384 const uint8 nt_pass[NT_HASH_LEN])
1386 struct cache_entry *centry;
1387 fstring sid_string;
1388 uint32 rid;
1389 uint8 cred_salt[NT_HASH_LEN];
1390 uint8 salted_hash[NT_HASH_LEN];
1392 if (is_null_sid(sid)) {
1393 return NT_STATUS_INVALID_SID;
1396 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1397 return NT_STATUS_INVALID_SID;
1400 centry = centry_start(domain, NT_STATUS_OK);
1401 if (!centry) {
1402 return NT_STATUS_INTERNAL_DB_ERROR;
1405 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1407 centry_put_time(centry, time(NULL));
1409 /* Create a salt and then salt the hash. */
1410 generate_random_buffer(cred_salt, NT_HASH_LEN);
1411 E_md5hash(cred_salt, nt_pass, salted_hash);
1413 centry_put_hash16(centry, salted_hash);
1414 centry_put_hash16(centry, cred_salt);
1415 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1417 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1419 centry_free(centry);
1421 return NT_STATUS_OK;
1425 /* Query display info. This is the basic user list fn */
1426 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1427 TALLOC_CTX *mem_ctx,
1428 uint32 *num_entries,
1429 struct wbint_userinfo **info)
1431 struct winbind_cache *cache = get_cache(domain);
1432 struct cache_entry *centry = NULL;
1433 NTSTATUS status;
1434 unsigned int i, retry;
1435 bool old_status = domain->online;
1437 if (!cache->tdb)
1438 goto do_query;
1440 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1441 if (!centry)
1442 goto do_query;
1444 do_fetch_cache:
1445 *num_entries = centry_uint32(centry);
1447 if (*num_entries == 0)
1448 goto do_cached;
1450 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1451 if (! (*info)) {
1452 smb_panic_fn("query_user_list out of memory");
1454 for (i=0; i<(*num_entries); i++) {
1455 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1456 (*info)[i].full_name = centry_string(centry, mem_ctx);
1457 (*info)[i].homedir = centry_string(centry, mem_ctx);
1458 (*info)[i].shell = centry_string(centry, mem_ctx);
1459 centry_sid(centry, &(*info)[i].user_sid);
1460 centry_sid(centry, &(*info)[i].group_sid);
1463 do_cached:
1464 status = centry->status;
1466 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1467 domain->name, nt_errstr(status) ));
1469 centry_free(centry);
1470 return status;
1472 do_query:
1473 *num_entries = 0;
1474 *info = NULL;
1476 /* Return status value returned by seq number check */
1478 if (!NT_STATUS_IS_OK(domain->last_status))
1479 return domain->last_status;
1481 /* Put the query_user_list() in a retry loop. There appears to be
1482 * some bug either with Windows 2000 or Samba's handling of large
1483 * rpc replies. This manifests itself as sudden disconnection
1484 * at a random point in the enumeration of a large (60k) user list.
1485 * The retry loop simply tries the operation again. )-: It's not
1486 * pretty but an acceptable workaround until we work out what the
1487 * real problem is. */
1489 retry = 0;
1490 do {
1492 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1493 domain->name ));
1495 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1496 if (!NT_STATUS_IS_OK(status)) {
1497 DEBUG(3, ("query_user_list: returned 0x%08x, "
1498 "retrying\n", NT_STATUS_V(status)));
1500 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1501 DEBUG(3, ("query_user_list: flushing "
1502 "connection cache\n"));
1503 invalidate_cm_connection(&domain->conn);
1505 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1506 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1507 if (!domain->internal && old_status) {
1508 set_domain_offline(domain);
1510 /* store partial response. */
1511 if (*num_entries > 0) {
1513 * humm, what about the status used for cache?
1514 * Should it be NT_STATUS_OK?
1516 break;
1519 * domain is offline now, and there is no user entries,
1520 * try to fetch from cache again.
1522 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1523 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1524 /* partial response... */
1525 if (!centry) {
1526 goto skip_save;
1527 } else {
1528 goto do_fetch_cache;
1530 } else {
1531 goto skip_save;
1535 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1536 (retry++ < 5));
1538 /* and save it */
1539 refresh_sequence_number(domain, false);
1540 if (!NT_STATUS_IS_OK(status)) {
1541 return status;
1543 centry = centry_start(domain, status);
1544 if (!centry)
1545 goto skip_save;
1546 centry_put_uint32(centry, *num_entries);
1547 for (i=0; i<(*num_entries); i++) {
1548 centry_put_string(centry, (*info)[i].acct_name);
1549 centry_put_string(centry, (*info)[i].full_name);
1550 centry_put_string(centry, (*info)[i].homedir);
1551 centry_put_string(centry, (*info)[i].shell);
1552 centry_put_sid(centry, &(*info)[i].user_sid);
1553 centry_put_sid(centry, &(*info)[i].group_sid);
1554 if (domain->backend && domain->backend->consistent) {
1555 /* when the backend is consistent we can pre-prime some mappings */
1556 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1557 domain->name,
1558 (*info)[i].acct_name,
1559 &(*info)[i].user_sid,
1560 SID_NAME_USER);
1561 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1562 &(*info)[i].user_sid,
1563 domain->name,
1564 (*info)[i].acct_name,
1565 SID_NAME_USER);
1566 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1569 centry_end(centry, "UL/%s", domain->name);
1570 centry_free(centry);
1572 skip_save:
1573 return status;
1576 /* list all domain groups */
1577 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1578 TALLOC_CTX *mem_ctx,
1579 uint32 *num_entries,
1580 struct wb_acct_info **info)
1582 struct winbind_cache *cache = get_cache(domain);
1583 struct cache_entry *centry = NULL;
1584 NTSTATUS status;
1585 unsigned int i;
1586 bool old_status;
1588 old_status = domain->online;
1589 if (!cache->tdb)
1590 goto do_query;
1592 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1593 if (!centry)
1594 goto do_query;
1596 do_fetch_cache:
1597 *num_entries = centry_uint32(centry);
1599 if (*num_entries == 0)
1600 goto do_cached;
1602 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1603 if (! (*info)) {
1604 smb_panic_fn("enum_dom_groups out of memory");
1606 for (i=0; i<(*num_entries); i++) {
1607 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1608 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1609 (*info)[i].rid = centry_uint32(centry);
1612 do_cached:
1613 status = centry->status;
1615 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1616 domain->name, nt_errstr(status) ));
1618 centry_free(centry);
1619 return status;
1621 do_query:
1622 *num_entries = 0;
1623 *info = NULL;
1625 /* Return status value returned by seq number check */
1627 if (!NT_STATUS_IS_OK(domain->last_status))
1628 return domain->last_status;
1630 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1631 domain->name ));
1633 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1635 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1636 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1637 if (!domain->internal && old_status) {
1638 set_domain_offline(domain);
1640 if (cache->tdb &&
1641 !domain->online &&
1642 !domain->internal &&
1643 old_status) {
1644 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1645 if (centry) {
1646 goto do_fetch_cache;
1650 /* and save it */
1651 refresh_sequence_number(domain, false);
1652 if (!NT_STATUS_IS_OK(status)) {
1653 return status;
1655 centry = centry_start(domain, status);
1656 if (!centry)
1657 goto skip_save;
1658 centry_put_uint32(centry, *num_entries);
1659 for (i=0; i<(*num_entries); i++) {
1660 centry_put_string(centry, (*info)[i].acct_name);
1661 centry_put_string(centry, (*info)[i].acct_desc);
1662 centry_put_uint32(centry, (*info)[i].rid);
1664 centry_end(centry, "GL/%s/domain", domain->name);
1665 centry_free(centry);
1667 skip_save:
1668 return status;
1671 /* list all domain groups */
1672 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1673 TALLOC_CTX *mem_ctx,
1674 uint32 *num_entries,
1675 struct wb_acct_info **info)
1677 struct winbind_cache *cache = get_cache(domain);
1678 struct cache_entry *centry = NULL;
1679 NTSTATUS status;
1680 unsigned int i;
1681 bool old_status;
1683 old_status = domain->online;
1684 if (!cache->tdb)
1685 goto do_query;
1687 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1688 if (!centry)
1689 goto do_query;
1691 do_fetch_cache:
1692 *num_entries = centry_uint32(centry);
1694 if (*num_entries == 0)
1695 goto do_cached;
1697 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1698 if (! (*info)) {
1699 smb_panic_fn("enum_dom_groups out of memory");
1701 for (i=0; i<(*num_entries); i++) {
1702 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1703 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1704 (*info)[i].rid = centry_uint32(centry);
1707 do_cached:
1709 /* If we are returning cached data and the domain controller
1710 is down then we don't know whether the data is up to date
1711 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1712 indicate this. */
1714 if (wcache_server_down(domain)) {
1715 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1716 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1717 } else
1718 status = centry->status;
1720 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1721 domain->name, nt_errstr(status) ));
1723 centry_free(centry);
1724 return status;
1726 do_query:
1727 *num_entries = 0;
1728 *info = NULL;
1730 /* Return status value returned by seq number check */
1732 if (!NT_STATUS_IS_OK(domain->last_status))
1733 return domain->last_status;
1735 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1736 domain->name ));
1738 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1740 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1741 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1742 if (!domain->internal && old_status) {
1743 set_domain_offline(domain);
1745 if (cache->tdb &&
1746 !domain->internal &&
1747 !domain->online &&
1748 old_status) {
1749 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1750 if (centry) {
1751 goto do_fetch_cache;
1755 /* and save it */
1756 refresh_sequence_number(domain, false);
1757 if (!NT_STATUS_IS_OK(status)) {
1758 return status;
1760 centry = centry_start(domain, status);
1761 if (!centry)
1762 goto skip_save;
1763 centry_put_uint32(centry, *num_entries);
1764 for (i=0; i<(*num_entries); i++) {
1765 centry_put_string(centry, (*info)[i].acct_name);
1766 centry_put_string(centry, (*info)[i].acct_desc);
1767 centry_put_uint32(centry, (*info)[i].rid);
1769 centry_end(centry, "GL/%s/local", domain->name);
1770 centry_free(centry);
1772 skip_save:
1773 return status;
1776 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1777 const char *domain_name,
1778 const char *name,
1779 struct dom_sid *sid,
1780 enum lsa_SidType *type)
1782 struct winbind_cache *cache = get_cache(domain);
1783 struct cache_entry *centry;
1784 NTSTATUS status;
1785 char *uname;
1787 if (cache->tdb == NULL) {
1788 return NT_STATUS_NOT_FOUND;
1791 uname = talloc_strdup_upper(talloc_tos(), name);
1792 if (uname == NULL) {
1793 return NT_STATUS_NO_MEMORY;
1796 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1797 TALLOC_FREE(uname);
1798 if (centry == NULL) {
1799 return NT_STATUS_NOT_FOUND;
1802 status = centry->status;
1803 if (NT_STATUS_IS_OK(status)) {
1804 *type = (enum lsa_SidType)centry_uint32(centry);
1805 centry_sid(centry, sid);
1808 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1809 "%s\n", domain->name, nt_errstr(status) ));
1811 centry_free(centry);
1812 return status;
1815 /* convert a single name to a sid in a domain */
1816 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1817 TALLOC_CTX *mem_ctx,
1818 const char *domain_name,
1819 const char *name,
1820 uint32_t flags,
1821 struct dom_sid *sid,
1822 enum lsa_SidType *type)
1824 NTSTATUS status;
1825 bool old_status;
1827 old_status = domain->online;
1829 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1830 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1831 return status;
1834 ZERO_STRUCTP(sid);
1836 /* If the seq number check indicated that there is a problem
1837 * with this DC, then return that status... except for
1838 * access_denied. This is special because the dc may be in
1839 * "restrict anonymous = 1" mode, in which case it will deny
1840 * most unauthenticated operations, but *will* allow the LSA
1841 * name-to-sid that we try as a fallback. */
1843 if (!(NT_STATUS_IS_OK(domain->last_status)
1844 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1845 return domain->last_status;
1847 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1848 domain->name ));
1850 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1851 name, flags, sid, type);
1853 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1854 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1855 if (!domain->internal && old_status) {
1856 set_domain_offline(domain);
1858 if (!domain->internal &&
1859 !domain->online &&
1860 old_status) {
1861 NTSTATUS cache_status;
1862 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1863 return cache_status;
1866 /* and save it */
1867 refresh_sequence_number(domain, false);
1869 if (domain->online &&
1870 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1871 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1873 /* Only save the reverse mapping if this was not a UPN */
1874 if (!strchr(name, '@')) {
1875 if (!strupper_m(discard_const_p(char, domain_name))) {
1876 return NT_STATUS_INVALID_PARAMETER;
1878 (void)strlower_m(discard_const_p(char, name));
1879 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1883 return status;
1886 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1887 const struct dom_sid *sid,
1888 TALLOC_CTX *mem_ctx,
1889 char **domain_name,
1890 char **name,
1891 enum lsa_SidType *type)
1893 struct winbind_cache *cache = get_cache(domain);
1894 struct cache_entry *centry;
1895 char *sid_string;
1896 NTSTATUS status;
1898 if (cache->tdb == NULL) {
1899 return NT_STATUS_NOT_FOUND;
1902 sid_string = sid_string_tos(sid);
1903 if (sid_string == NULL) {
1904 return NT_STATUS_NO_MEMORY;
1907 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1908 TALLOC_FREE(sid_string);
1909 if (centry == NULL) {
1910 return NT_STATUS_NOT_FOUND;
1913 if (NT_STATUS_IS_OK(centry->status)) {
1914 *type = (enum lsa_SidType)centry_uint32(centry);
1915 *domain_name = centry_string(centry, mem_ctx);
1916 *name = centry_string(centry, mem_ctx);
1919 status = centry->status;
1920 centry_free(centry);
1922 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1923 "%s\n", domain->name, nt_errstr(status) ));
1925 return status;
1928 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1929 given */
1930 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1931 TALLOC_CTX *mem_ctx,
1932 const struct dom_sid *sid,
1933 char **domain_name,
1934 char **name,
1935 enum lsa_SidType *type)
1937 NTSTATUS status;
1938 bool old_status;
1940 old_status = domain->online;
1941 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1942 type);
1943 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1944 return status;
1947 *name = NULL;
1948 *domain_name = NULL;
1950 /* If the seq number check indicated that there is a problem
1951 * with this DC, then return that status... except for
1952 * access_denied. This is special because the dc may be in
1953 * "restrict anonymous = 1" mode, in which case it will deny
1954 * most unauthenticated operations, but *will* allow the LSA
1955 * sid-to-name that we try as a fallback. */
1957 if (!(NT_STATUS_IS_OK(domain->last_status)
1958 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1959 return domain->last_status;
1961 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1962 domain->name ));
1964 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1966 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1967 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1968 if (!domain->internal && old_status) {
1969 set_domain_offline(domain);
1971 if (!domain->internal &&
1972 !domain->online &&
1973 old_status) {
1974 NTSTATUS cache_status;
1975 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1976 domain_name, name, type);
1977 return cache_status;
1980 /* and save it */
1981 refresh_sequence_number(domain, false);
1982 if (!NT_STATUS_IS_OK(status)) {
1983 return status;
1985 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1987 /* We can't save the name to sid mapping here, as with sid history a
1988 * later name2sid would give the wrong sid. */
1990 return status;
1993 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1994 TALLOC_CTX *mem_ctx,
1995 const struct dom_sid *domain_sid,
1996 uint32 *rids,
1997 size_t num_rids,
1998 char **domain_name,
1999 char ***names,
2000 enum lsa_SidType **types)
2002 struct winbind_cache *cache = get_cache(domain);
2003 size_t i;
2004 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2005 bool have_mapped;
2006 bool have_unmapped;
2007 bool old_status;
2009 old_status = domain->online;
2010 *domain_name = NULL;
2011 *names = NULL;
2012 *types = NULL;
2014 if (!cache->tdb) {
2015 goto do_query;
2018 if (num_rids == 0) {
2019 return NT_STATUS_OK;
2022 *names = talloc_array(mem_ctx, char *, num_rids);
2023 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2025 if ((*names == NULL) || (*types == NULL)) {
2026 result = NT_STATUS_NO_MEMORY;
2027 goto error;
2030 have_mapped = have_unmapped = false;
2032 for (i=0; i<num_rids; i++) {
2033 struct dom_sid sid;
2034 struct cache_entry *centry;
2035 fstring tmp;
2037 if (!sid_compose(&sid, domain_sid, rids[i])) {
2038 result = NT_STATUS_INTERNAL_ERROR;
2039 goto error;
2042 centry = wcache_fetch(cache, domain, "SN/%s",
2043 sid_to_fstring(tmp, &sid));
2044 if (!centry) {
2045 goto do_query;
2048 (*types)[i] = SID_NAME_UNKNOWN;
2049 (*names)[i] = talloc_strdup(*names, "");
2051 if (NT_STATUS_IS_OK(centry->status)) {
2052 char *dom;
2053 have_mapped = true;
2054 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2056 dom = centry_string(centry, mem_ctx);
2057 if (*domain_name == NULL) {
2058 *domain_name = dom;
2059 } else {
2060 talloc_free(dom);
2063 (*names)[i] = centry_string(centry, *names);
2065 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2066 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2067 have_unmapped = true;
2069 } else {
2070 /* something's definitely wrong */
2071 result = centry->status;
2072 goto error;
2075 centry_free(centry);
2078 if (!have_mapped) {
2079 return NT_STATUS_NONE_MAPPED;
2081 if (!have_unmapped) {
2082 return NT_STATUS_OK;
2084 return STATUS_SOME_UNMAPPED;
2086 do_query:
2088 TALLOC_FREE(*names);
2089 TALLOC_FREE(*types);
2091 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2092 rids, num_rids, domain_name,
2093 names, types);
2095 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2096 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2097 if (!domain->internal && old_status) {
2098 set_domain_offline(domain);
2100 if (cache->tdb &&
2101 !domain->internal &&
2102 !domain->online &&
2103 old_status) {
2104 have_mapped = have_unmapped = false;
2106 for (i=0; i<num_rids; i++) {
2107 struct dom_sid sid;
2108 struct cache_entry *centry;
2109 fstring tmp;
2111 if (!sid_compose(&sid, domain_sid, rids[i])) {
2112 result = NT_STATUS_INTERNAL_ERROR;
2113 goto error;
2116 centry = wcache_fetch(cache, domain, "SN/%s",
2117 sid_to_fstring(tmp, &sid));
2118 if (!centry) {
2119 (*types)[i] = SID_NAME_UNKNOWN;
2120 (*names)[i] = talloc_strdup(*names, "");
2121 continue;
2124 (*types)[i] = SID_NAME_UNKNOWN;
2125 (*names)[i] = talloc_strdup(*names, "");
2127 if (NT_STATUS_IS_OK(centry->status)) {
2128 char *dom;
2129 have_mapped = true;
2130 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2132 dom = centry_string(centry, mem_ctx);
2133 if (*domain_name == NULL) {
2134 *domain_name = dom;
2135 } else {
2136 talloc_free(dom);
2139 (*names)[i] = centry_string(centry, *names);
2141 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2142 have_unmapped = true;
2144 } else {
2145 /* something's definitely wrong */
2146 result = centry->status;
2147 goto error;
2150 centry_free(centry);
2153 if (!have_mapped) {
2154 return NT_STATUS_NONE_MAPPED;
2156 if (!have_unmapped) {
2157 return NT_STATUS_OK;
2159 return STATUS_SOME_UNMAPPED;
2163 None of the queried rids has been found so save all negative entries
2165 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2166 for (i = 0; i < num_rids; i++) {
2167 struct dom_sid sid;
2168 const char *name = "";
2169 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2170 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2172 if (!sid_compose(&sid, domain_sid, rids[i])) {
2173 return NT_STATUS_INTERNAL_ERROR;
2176 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2177 name, type);
2180 return result;
2184 Some or all of the queried rids have been found.
2186 if (!NT_STATUS_IS_OK(result) &&
2187 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2188 return result;
2191 refresh_sequence_number(domain, false);
2193 for (i=0; i<num_rids; i++) {
2194 struct dom_sid sid;
2195 NTSTATUS status;
2197 if (!sid_compose(&sid, domain_sid, rids[i])) {
2198 result = NT_STATUS_INTERNAL_ERROR;
2199 goto error;
2202 status = (*types)[i] == SID_NAME_UNKNOWN ?
2203 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2205 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2206 (*names)[i], (*types)[i]);
2209 return result;
2211 error:
2212 TALLOC_FREE(*names);
2213 TALLOC_FREE(*types);
2214 return result;
2217 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2218 TALLOC_CTX *mem_ctx,
2219 const struct dom_sid *user_sid,
2220 struct wbint_userinfo *info)
2222 struct winbind_cache *cache = get_cache(domain);
2223 struct cache_entry *centry = NULL;
2224 NTSTATUS status;
2225 char *sid_string;
2227 if (cache->tdb == NULL) {
2228 return NT_STATUS_NOT_FOUND;
2231 sid_string = sid_string_tos(user_sid);
2232 if (sid_string == NULL) {
2233 return NT_STATUS_NO_MEMORY;
2236 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2237 TALLOC_FREE(sid_string);
2238 if (centry == NULL) {
2239 return NT_STATUS_NOT_FOUND;
2243 * If we have an access denied cache entry and a cached info3
2244 * in the samlogon cache then do a query. This will force the
2245 * rpc back end to return the info3 data.
2248 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2249 netsamlogon_cache_have(user_sid)) {
2250 DEBUG(10, ("query_user: cached access denied and have cached "
2251 "info3\n"));
2252 domain->last_status = NT_STATUS_OK;
2253 centry_free(centry);
2254 return NT_STATUS_NOT_FOUND;
2257 /* if status is not ok then this is a negative hit
2258 and the rest of the data doesn't matter */
2259 status = centry->status;
2260 if (NT_STATUS_IS_OK(status)) {
2261 info->acct_name = centry_string(centry, mem_ctx);
2262 info->full_name = centry_string(centry, mem_ctx);
2263 info->homedir = centry_string(centry, mem_ctx);
2264 info->shell = centry_string(centry, mem_ctx);
2265 info->primary_gid = centry_uint32(centry);
2266 centry_sid(centry, &info->user_sid);
2267 centry_sid(centry, &info->group_sid);
2270 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2271 "%s\n", domain->name, nt_errstr(status) ));
2273 centry_free(centry);
2274 return status;
2277 /* Lookup user information from a rid */
2278 static NTSTATUS query_user(struct winbindd_domain *domain,
2279 TALLOC_CTX *mem_ctx,
2280 const struct dom_sid *user_sid,
2281 struct wbint_userinfo *info)
2283 NTSTATUS status;
2284 bool old_status;
2286 old_status = domain->online;
2287 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2288 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2289 return status;
2292 ZERO_STRUCTP(info);
2294 /* Return status value returned by seq number check */
2296 if (!NT_STATUS_IS_OK(domain->last_status))
2297 return domain->last_status;
2299 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2300 domain->name ));
2302 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2304 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2305 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2306 if (!domain->internal && old_status) {
2307 set_domain_offline(domain);
2309 if (!domain->internal &&
2310 !domain->online &&
2311 old_status) {
2312 NTSTATUS cache_status;
2313 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2314 return cache_status;
2317 /* and save it */
2318 refresh_sequence_number(domain, false);
2319 if (!NT_STATUS_IS_OK(status)) {
2320 return status;
2322 wcache_save_user(domain, status, info);
2324 return status;
2327 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2328 TALLOC_CTX *mem_ctx,
2329 const struct dom_sid *user_sid,
2330 uint32_t *pnum_sids,
2331 struct dom_sid **psids)
2333 struct winbind_cache *cache = get_cache(domain);
2334 struct cache_entry *centry = NULL;
2335 NTSTATUS status;
2336 uint32_t i, num_sids;
2337 struct dom_sid *sids;
2338 fstring sid_string;
2340 if (cache->tdb == NULL) {
2341 return NT_STATUS_NOT_FOUND;
2344 centry = wcache_fetch(cache, domain, "UG/%s",
2345 sid_to_fstring(sid_string, user_sid));
2346 if (centry == NULL) {
2347 return NT_STATUS_NOT_FOUND;
2350 /* If we have an access denied cache entry and a cached info3 in the
2351 samlogon cache then do a query. This will force the rpc back end
2352 to return the info3 data. */
2354 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2355 && netsamlogon_cache_have(user_sid)) {
2356 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2357 "cached info3\n"));
2358 domain->last_status = NT_STATUS_OK;
2359 centry_free(centry);
2360 return NT_STATUS_NOT_FOUND;
2363 num_sids = centry_uint32(centry);
2364 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2365 if (sids == NULL) {
2366 centry_free(centry);
2367 return NT_STATUS_NO_MEMORY;
2370 for (i=0; i<num_sids; i++) {
2371 centry_sid(centry, &sids[i]);
2374 status = centry->status;
2376 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2377 "status: %s\n", domain->name, nt_errstr(status)));
2379 centry_free(centry);
2381 *pnum_sids = num_sids;
2382 *psids = sids;
2383 return status;
2386 /* Lookup groups a user is a member of. */
2387 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2388 TALLOC_CTX *mem_ctx,
2389 const struct dom_sid *user_sid,
2390 uint32 *num_groups, struct dom_sid **user_gids)
2392 struct cache_entry *centry = NULL;
2393 NTSTATUS status;
2394 unsigned int i;
2395 fstring sid_string;
2396 bool old_status;
2398 old_status = domain->online;
2399 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2400 num_groups, user_gids);
2401 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2402 return status;
2405 (*num_groups) = 0;
2406 (*user_gids) = NULL;
2408 /* Return status value returned by seq number check */
2410 if (!NT_STATUS_IS_OK(domain->last_status))
2411 return domain->last_status;
2413 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2414 domain->name ));
2416 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2418 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2419 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2420 if (!domain->internal && old_status) {
2421 set_domain_offline(domain);
2423 if (!domain->internal &&
2424 !domain->online &&
2425 old_status) {
2426 NTSTATUS cache_status;
2427 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2428 num_groups, user_gids);
2429 return cache_status;
2432 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2433 goto skip_save;
2435 /* and save it */
2436 refresh_sequence_number(domain, false);
2437 if (!NT_STATUS_IS_OK(status)) {
2438 return status;
2440 centry = centry_start(domain, status);
2441 if (!centry)
2442 goto skip_save;
2444 centry_put_uint32(centry, *num_groups);
2445 for (i=0; i<(*num_groups); i++) {
2446 centry_put_sid(centry, &(*user_gids)[i]);
2449 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2450 centry_free(centry);
2452 skip_save:
2453 return status;
2456 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2457 const struct dom_sid *sids)
2459 uint32_t i;
2460 char *sidlist;
2462 sidlist = talloc_strdup(mem_ctx, "");
2463 if (sidlist == NULL) {
2464 return NULL;
2466 for (i=0; i<num_sids; i++) {
2467 fstring tmp;
2468 sidlist = talloc_asprintf_append_buffer(
2469 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2470 if (sidlist == NULL) {
2471 return NULL;
2474 return sidlist;
2477 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2478 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2479 const struct dom_sid *sids,
2480 uint32_t *pnum_aliases, uint32_t **paliases)
2482 struct winbind_cache *cache = get_cache(domain);
2483 struct cache_entry *centry = NULL;
2484 uint32_t num_aliases;
2485 uint32_t *aliases;
2486 NTSTATUS status;
2487 char *sidlist;
2488 int i;
2490 if (cache->tdb == NULL) {
2491 return NT_STATUS_NOT_FOUND;
2494 if (num_sids == 0) {
2495 *pnum_aliases = 0;
2496 *paliases = NULL;
2497 return NT_STATUS_OK;
2500 /* We need to cache indexed by the whole list of SIDs, the aliases
2501 * resulting might come from any of the SIDs. */
2503 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2504 if (sidlist == NULL) {
2505 return NT_STATUS_NO_MEMORY;
2508 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2509 TALLOC_FREE(sidlist);
2510 if (centry == NULL) {
2511 return NT_STATUS_NOT_FOUND;
2514 num_aliases = centry_uint32(centry);
2515 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2516 if (aliases == NULL) {
2517 centry_free(centry);
2518 return NT_STATUS_NO_MEMORY;
2521 for (i=0; i<num_aliases; i++) {
2522 aliases[i] = centry_uint32(centry);
2525 status = centry->status;
2527 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2528 "status %s\n", domain->name, nt_errstr(status)));
2530 centry_free(centry);
2532 *pnum_aliases = num_aliases;
2533 *paliases = aliases;
2535 return status;
2538 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2539 TALLOC_CTX *mem_ctx,
2540 uint32 num_sids, const struct dom_sid *sids,
2541 uint32 *num_aliases, uint32 **alias_rids)
2543 struct cache_entry *centry = NULL;
2544 NTSTATUS status;
2545 char *sidlist;
2546 int i;
2547 bool old_status;
2549 old_status = domain->online;
2550 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2551 num_aliases, alias_rids);
2552 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2553 return status;
2556 (*num_aliases) = 0;
2557 (*alias_rids) = NULL;
2559 if (!NT_STATUS_IS_OK(domain->last_status))
2560 return domain->last_status;
2562 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2563 "for domain %s\n", domain->name ));
2565 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2566 if (sidlist == NULL) {
2567 return NT_STATUS_NO_MEMORY;
2570 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2571 num_sids, sids,
2572 num_aliases, alias_rids);
2574 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2575 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2576 if (!domain->internal && old_status) {
2577 set_domain_offline(domain);
2579 if (!domain->internal &&
2580 !domain->online &&
2581 old_status) {
2582 NTSTATUS cache_status;
2583 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2584 sids, num_aliases, alias_rids);
2585 return cache_status;
2588 /* and save it */
2589 refresh_sequence_number(domain, false);
2590 if (!NT_STATUS_IS_OK(status)) {
2591 return status;
2593 centry = centry_start(domain, status);
2594 if (!centry)
2595 goto skip_save;
2596 centry_put_uint32(centry, *num_aliases);
2597 for (i=0; i<(*num_aliases); i++)
2598 centry_put_uint32(centry, (*alias_rids)[i]);
2599 centry_end(centry, "UA%s", sidlist);
2600 centry_free(centry);
2602 skip_save:
2603 return status;
2606 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2607 TALLOC_CTX *mem_ctx,
2608 const struct dom_sid *group_sid,
2609 uint32_t *num_names,
2610 struct dom_sid **sid_mem, char ***names,
2611 uint32_t **name_types)
2613 struct winbind_cache *cache = get_cache(domain);
2614 struct cache_entry *centry = NULL;
2615 NTSTATUS status;
2616 unsigned int i;
2617 char *sid_string;
2619 if (cache->tdb == NULL) {
2620 return NT_STATUS_NOT_FOUND;
2623 sid_string = sid_string_tos(group_sid);
2624 if (sid_string == NULL) {
2625 return NT_STATUS_NO_MEMORY;
2628 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2629 TALLOC_FREE(sid_string);
2630 if (centry == NULL) {
2631 return NT_STATUS_NOT_FOUND;
2634 *sid_mem = NULL;
2635 *names = NULL;
2636 *name_types = NULL;
2638 *num_names = centry_uint32(centry);
2639 if (*num_names == 0) {
2640 centry_free(centry);
2641 return NT_STATUS_OK;
2644 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2645 *names = talloc_array(mem_ctx, char *, *num_names);
2646 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2648 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2649 TALLOC_FREE(*sid_mem);
2650 TALLOC_FREE(*names);
2651 TALLOC_FREE(*name_types);
2652 centry_free(centry);
2653 return NT_STATUS_NO_MEMORY;
2656 for (i=0; i<(*num_names); i++) {
2657 centry_sid(centry, &(*sid_mem)[i]);
2658 (*names)[i] = centry_string(centry, mem_ctx);
2659 (*name_types)[i] = centry_uint32(centry);
2662 status = centry->status;
2664 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2665 "status: %s\n", domain->name, nt_errstr(status)));
2667 centry_free(centry);
2668 return status;
2671 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2672 TALLOC_CTX *mem_ctx,
2673 const struct dom_sid *group_sid,
2674 enum lsa_SidType type,
2675 uint32 *num_names,
2676 struct dom_sid **sid_mem, char ***names,
2677 uint32 **name_types)
2679 struct cache_entry *centry = NULL;
2680 NTSTATUS status;
2681 unsigned int i;
2682 fstring sid_string;
2683 bool old_status;
2685 old_status = domain->online;
2686 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2687 sid_mem, names, name_types);
2688 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2689 return status;
2692 (*num_names) = 0;
2693 (*sid_mem) = NULL;
2694 (*names) = NULL;
2695 (*name_types) = NULL;
2697 /* Return status value returned by seq number check */
2699 if (!NT_STATUS_IS_OK(domain->last_status))
2700 return domain->last_status;
2702 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2703 domain->name ));
2705 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2706 type, num_names,
2707 sid_mem, names, name_types);
2709 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2710 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2711 if (!domain->internal && old_status) {
2712 set_domain_offline(domain);
2714 if (!domain->internal &&
2715 !domain->online &&
2716 old_status) {
2717 NTSTATUS cache_status;
2718 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2719 num_names, sid_mem, names,
2720 name_types);
2721 return cache_status;
2724 /* and save it */
2725 refresh_sequence_number(domain, false);
2726 if (!NT_STATUS_IS_OK(status)) {
2727 return status;
2729 centry = centry_start(domain, status);
2730 if (!centry)
2731 goto skip_save;
2732 centry_put_uint32(centry, *num_names);
2733 for (i=0; i<(*num_names); i++) {
2734 centry_put_sid(centry, &(*sid_mem)[i]);
2735 centry_put_string(centry, (*names)[i]);
2736 centry_put_uint32(centry, (*name_types)[i]);
2738 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2739 centry_free(centry);
2741 skip_save:
2742 return status;
2745 /* find the sequence number for a domain */
2746 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2748 refresh_sequence_number(domain, false);
2750 *seq = domain->sequence_number;
2752 return NT_STATUS_OK;
2755 /* enumerate trusted domains
2756 * (we need to have the list of trustdoms in the cache when we go offline) -
2757 * Guenther */
2758 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2759 TALLOC_CTX *mem_ctx,
2760 struct netr_DomainTrustList *trusts)
2762 NTSTATUS status;
2763 struct winbind_cache *cache;
2764 struct winbindd_tdc_domain *dom_list = NULL;
2765 size_t num_domains = 0;
2766 bool retval = false;
2767 int i;
2768 bool old_status;
2770 old_status = domain->online;
2771 trusts->count = 0;
2772 trusts->array = NULL;
2774 cache = get_cache(domain);
2775 if (!cache || !cache->tdb) {
2776 goto do_query;
2779 if (domain->online) {
2780 goto do_query;
2783 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2784 if (!retval || !num_domains || !dom_list) {
2785 TALLOC_FREE(dom_list);
2786 goto do_query;
2789 do_fetch_cache:
2790 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2791 if (!trusts->array) {
2792 TALLOC_FREE(dom_list);
2793 return NT_STATUS_NO_MEMORY;
2796 for (i = 0; i < num_domains; i++) {
2797 struct netr_DomainTrust *trust;
2798 struct dom_sid *sid;
2799 struct winbindd_domain *dom;
2801 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2802 if (dom && dom->internal) {
2803 continue;
2806 trust = &trusts->array[trusts->count];
2807 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2808 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2809 sid = talloc(trusts->array, struct dom_sid);
2810 if (!trust->netbios_name || !trust->dns_name ||
2811 !sid) {
2812 TALLOC_FREE(dom_list);
2813 TALLOC_FREE(trusts->array);
2814 return NT_STATUS_NO_MEMORY;
2817 trust->trust_flags = dom_list[i].trust_flags;
2818 trust->trust_attributes = dom_list[i].trust_attribs;
2819 trust->trust_type = dom_list[i].trust_type;
2820 sid_copy(sid, &dom_list[i].sid);
2821 trust->sid = sid;
2822 trusts->count++;
2825 TALLOC_FREE(dom_list);
2826 return NT_STATUS_OK;
2828 do_query:
2829 /* Return status value returned by seq number check */
2831 if (!NT_STATUS_IS_OK(domain->last_status))
2832 return domain->last_status;
2834 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2835 domain->name ));
2837 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2839 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2840 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2841 if (!domain->internal && old_status) {
2842 set_domain_offline(domain);
2844 if (!domain->internal &&
2845 !domain->online &&
2846 old_status) {
2847 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2848 if (retval && num_domains && dom_list) {
2849 TALLOC_FREE(trusts->array);
2850 trusts->count = 0;
2851 goto do_fetch_cache;
2855 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2856 * so that the generic centry handling still applies correctly -
2857 * Guenther*/
2859 if (!NT_STATUS_IS_ERR(status)) {
2860 status = NT_STATUS_OK;
2862 return status;
2865 /* get lockout policy */
2866 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2867 TALLOC_CTX *mem_ctx,
2868 struct samr_DomInfo12 *policy)
2870 struct winbind_cache *cache = get_cache(domain);
2871 struct cache_entry *centry = NULL;
2872 NTSTATUS status;
2873 bool old_status;
2875 old_status = domain->online;
2876 if (!cache->tdb)
2877 goto do_query;
2879 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2881 if (!centry)
2882 goto do_query;
2884 do_fetch_cache:
2885 policy->lockout_duration = centry_nttime(centry);
2886 policy->lockout_window = centry_nttime(centry);
2887 policy->lockout_threshold = centry_uint16(centry);
2889 status = centry->status;
2891 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2892 domain->name, nt_errstr(status) ));
2894 centry_free(centry);
2895 return status;
2897 do_query:
2898 ZERO_STRUCTP(policy);
2900 /* Return status value returned by seq number check */
2902 if (!NT_STATUS_IS_OK(domain->last_status))
2903 return domain->last_status;
2905 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2906 domain->name ));
2908 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2910 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2911 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2912 if (!domain->internal && old_status) {
2913 set_domain_offline(domain);
2915 if (cache->tdb &&
2916 !domain->internal &&
2917 !domain->online &&
2918 old_status) {
2919 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2920 if (centry) {
2921 goto do_fetch_cache;
2925 /* and save it */
2926 refresh_sequence_number(domain, false);
2927 if (!NT_STATUS_IS_OK(status)) {
2928 return status;
2930 wcache_save_lockout_policy(domain, status, policy);
2932 return status;
2935 /* get password policy */
2936 static NTSTATUS password_policy(struct winbindd_domain *domain,
2937 TALLOC_CTX *mem_ctx,
2938 struct samr_DomInfo1 *policy)
2940 struct winbind_cache *cache = get_cache(domain);
2941 struct cache_entry *centry = NULL;
2942 NTSTATUS status;
2943 bool old_status;
2945 old_status = domain->online;
2946 if (!cache->tdb)
2947 goto do_query;
2949 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2951 if (!centry)
2952 goto do_query;
2954 do_fetch_cache:
2955 policy->min_password_length = centry_uint16(centry);
2956 policy->password_history_length = centry_uint16(centry);
2957 policy->password_properties = centry_uint32(centry);
2958 policy->max_password_age = centry_nttime(centry);
2959 policy->min_password_age = centry_nttime(centry);
2961 status = centry->status;
2963 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2964 domain->name, nt_errstr(status) ));
2966 centry_free(centry);
2967 return status;
2969 do_query:
2970 ZERO_STRUCTP(policy);
2972 /* Return status value returned by seq number check */
2974 if (!NT_STATUS_IS_OK(domain->last_status))
2975 return domain->last_status;
2977 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2978 domain->name ));
2980 status = domain->backend->password_policy(domain, mem_ctx, policy);
2982 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2983 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2984 if (!domain->internal && old_status) {
2985 set_domain_offline(domain);
2987 if (cache->tdb &&
2988 !domain->internal &&
2989 !domain->online &&
2990 old_status) {
2991 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2992 if (centry) {
2993 goto do_fetch_cache;
2997 /* and save it */
2998 refresh_sequence_number(domain, false);
2999 if (!NT_STATUS_IS_OK(status)) {
3000 return status;
3002 wcache_save_password_policy(domain, status, policy);
3004 return status;
3008 /* Invalidate cached user and group lists coherently */
3010 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3011 void *state)
3013 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3014 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3015 tdb_delete(the_tdb, kbuf);
3017 return 0;
3020 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3022 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3023 const struct dom_sid *sid)
3025 fstring key_str, sid_string;
3026 struct winbind_cache *cache;
3028 /* dont clear cached U/SID and UG/SID entries when we want to logon
3029 * offline - gd */
3031 if (lp_winbind_offline_logon()) {
3032 return;
3035 if (!domain)
3036 return;
3038 cache = get_cache(domain);
3040 if (!cache->tdb) {
3041 return;
3044 /* Clear U/SID cache entry */
3045 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3046 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3047 tdb_delete(cache->tdb, string_tdb_data(key_str));
3049 /* Clear UG/SID cache entry */
3050 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3051 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3052 tdb_delete(cache->tdb, string_tdb_data(key_str));
3054 /* Samba/winbindd never needs this. */
3055 netsamlogon_clear_cached_user(sid);
3058 bool wcache_invalidate_cache(void)
3060 struct winbindd_domain *domain;
3062 for (domain = domain_list(); domain; domain = domain->next) {
3063 struct winbind_cache *cache = get_cache(domain);
3065 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3066 "entries for %s\n", domain->name));
3067 if (cache) {
3068 if (cache->tdb) {
3069 tdb_traverse(cache->tdb, traverse_fn, NULL);
3070 } else {
3071 return false;
3075 return true;
3078 bool wcache_invalidate_cache_noinit(void)
3080 struct winbindd_domain *domain;
3082 for (domain = domain_list(); domain; domain = domain->next) {
3083 struct winbind_cache *cache;
3085 /* Skip uninitialized domains. */
3086 if (!domain->initialized && !domain->internal) {
3087 continue;
3090 cache = get_cache(domain);
3092 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3093 "entries for %s\n", domain->name));
3094 if (cache) {
3095 if (cache->tdb) {
3096 tdb_traverse(cache->tdb, traverse_fn, NULL);
3098 * Flushing cache has nothing to with domains.
3099 * return here if we successfully flushed once.
3100 * To avoid unnecessary traversing the cache.
3102 return true;
3103 } else {
3104 return false;
3108 return true;
3111 bool init_wcache(void)
3113 if (wcache == NULL) {
3114 wcache = SMB_XMALLOC_P(struct winbind_cache);
3115 ZERO_STRUCTP(wcache);
3118 if (wcache->tdb != NULL)
3119 return true;
3121 /* when working offline we must not clear the cache on restart */
3122 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3123 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3124 TDB_INCOMPATIBLE_HASH |
3125 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3126 O_RDWR|O_CREAT, 0600);
3128 if (wcache->tdb == NULL) {
3129 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3130 return false;
3133 return true;
3136 /************************************************************************
3137 This is called by the parent to initialize the cache file.
3138 We don't need sophisticated locking here as we know we're the
3139 only opener.
3140 ************************************************************************/
3142 bool initialize_winbindd_cache(void)
3144 bool cache_bad = true;
3145 uint32 vers;
3147 if (!init_wcache()) {
3148 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3149 return false;
3152 /* Check version number. */
3153 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3154 vers == WINBINDD_CACHE_VERSION) {
3155 cache_bad = false;
3158 if (cache_bad) {
3159 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3160 "and re-creating with version number %d\n",
3161 WINBINDD_CACHE_VERSION ));
3163 tdb_close(wcache->tdb);
3164 wcache->tdb = NULL;
3166 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3167 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3168 state_path("winbindd_cache.tdb"),
3169 strerror(errno) ));
3170 return false;
3172 if (!init_wcache()) {
3173 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3174 "init_wcache failed.\n"));
3175 return false;
3178 /* Write the version. */
3179 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3180 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3181 tdb_errorstr_compat(wcache->tdb) ));
3182 return false;
3186 tdb_close(wcache->tdb);
3187 wcache->tdb = NULL;
3188 return true;
3191 void close_winbindd_cache(void)
3193 if (!wcache) {
3194 return;
3196 if (wcache->tdb) {
3197 tdb_close(wcache->tdb);
3198 wcache->tdb = NULL;
3202 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3203 char **domain_name, char **name,
3204 enum lsa_SidType *type)
3206 struct winbindd_domain *domain;
3207 NTSTATUS status;
3209 domain = find_lookup_domain_from_sid(sid);
3210 if (domain == NULL) {
3211 return false;
3213 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3214 type);
3215 return NT_STATUS_IS_OK(status);
3218 bool lookup_cached_name(const char *domain_name,
3219 const char *name,
3220 struct dom_sid *sid,
3221 enum lsa_SidType *type)
3223 struct winbindd_domain *domain;
3224 NTSTATUS status;
3225 bool original_online_state;
3227 domain = find_lookup_domain_from_name(domain_name);
3228 if (domain == NULL) {
3229 return false;
3232 /* If we are doing a cached logon, temporarily set the domain
3233 offline so the cache won't expire the entry */
3235 original_online_state = domain->online;
3236 domain->online = false;
3237 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3238 domain->online = original_online_state;
3240 return NT_STATUS_IS_OK(status);
3243 void cache_name2sid(struct winbindd_domain *domain,
3244 const char *domain_name, const char *name,
3245 enum lsa_SidType type, const struct dom_sid *sid)
3247 refresh_sequence_number(domain, false);
3248 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3249 sid, type);
3253 * The original idea that this cache only contains centries has
3254 * been blurred - now other stuff gets put in here. Ensure we
3255 * ignore these things on cleanup.
3258 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3259 TDB_DATA dbuf, void *state)
3261 struct cache_entry *centry;
3263 if (is_non_centry_key(kbuf)) {
3264 return 0;
3267 centry = wcache_fetch_raw((char *)kbuf.dptr);
3268 if (!centry) {
3269 return 0;
3272 if (!NT_STATUS_IS_OK(centry->status)) {
3273 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3274 tdb_delete(the_tdb, kbuf);
3277 centry_free(centry);
3278 return 0;
3281 /* flush the cache */
3282 void wcache_flush_cache(void)
3284 if (!wcache)
3285 return;
3286 if (wcache->tdb) {
3287 tdb_close(wcache->tdb);
3288 wcache->tdb = NULL;
3290 if (!winbindd_use_cache()) {
3291 return;
3294 /* when working offline we must not clear the cache on restart */
3295 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3296 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3297 TDB_INCOMPATIBLE_HASH |
3298 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3299 O_RDWR|O_CREAT, 0600);
3301 if (!wcache->tdb) {
3302 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3303 return;
3306 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3308 DEBUG(10,("wcache_flush_cache success\n"));
3311 /* Count cached creds */
3313 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3314 void *state)
3316 int *cred_count = (int*)state;
3318 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3319 (*cred_count)++;
3321 return 0;
3324 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3326 struct winbind_cache *cache = get_cache(domain);
3328 *count = 0;
3330 if (!cache->tdb) {
3331 return NT_STATUS_INTERNAL_DB_ERROR;
3334 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3336 return NT_STATUS_OK;
3339 struct cred_list {
3340 struct cred_list *prev, *next;
3341 TDB_DATA key;
3342 fstring name;
3343 time_t created;
3345 static struct cred_list *wcache_cred_list;
3347 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3348 void *state)
3350 struct cred_list *cred;
3352 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3354 cred = SMB_MALLOC_P(struct cred_list);
3355 if (cred == NULL) {
3356 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3357 return -1;
3360 ZERO_STRUCTP(cred);
3362 /* save a copy of the key */
3364 fstrcpy(cred->name, (const char *)kbuf.dptr);
3365 DLIST_ADD(wcache_cred_list, cred);
3368 return 0;
3371 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3373 struct winbind_cache *cache = get_cache(domain);
3374 NTSTATUS status;
3375 int ret;
3376 struct cred_list *cred, *oldest = NULL;
3378 if (!cache->tdb) {
3379 return NT_STATUS_INTERNAL_DB_ERROR;
3382 /* we possibly already have an entry */
3383 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3385 fstring key_str, tmp;
3387 DEBUG(11,("we already have an entry, deleting that\n"));
3389 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3391 tdb_delete(cache->tdb, string_tdb_data(key_str));
3393 return NT_STATUS_OK;
3396 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3397 if (ret == 0) {
3398 return NT_STATUS_OK;
3399 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3400 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3403 ZERO_STRUCTP(oldest);
3405 for (cred = wcache_cred_list; cred; cred = cred->next) {
3407 TDB_DATA data;
3408 time_t t;
3410 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3411 if (!data.dptr) {
3412 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3413 cred->name));
3414 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3415 goto done;
3418 t = IVAL(data.dptr, 0);
3419 SAFE_FREE(data.dptr);
3421 if (!oldest) {
3422 oldest = SMB_MALLOC_P(struct cred_list);
3423 if (oldest == NULL) {
3424 status = NT_STATUS_NO_MEMORY;
3425 goto done;
3428 fstrcpy(oldest->name, cred->name);
3429 oldest->created = t;
3430 continue;
3433 if (t < oldest->created) {
3434 fstrcpy(oldest->name, cred->name);
3435 oldest->created = t;
3439 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3440 status = NT_STATUS_OK;
3441 } else {
3442 status = NT_STATUS_UNSUCCESSFUL;
3444 done:
3445 SAFE_FREE(wcache_cred_list);
3446 SAFE_FREE(oldest);
3448 return status;
3451 /* Change the global online/offline state. */
3452 bool set_global_winbindd_state_offline(void)
3454 TDB_DATA data;
3456 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3458 /* Only go offline if someone has created
3459 the key "WINBINDD_OFFLINE" in the cache tdb. */
3461 if (wcache == NULL || wcache->tdb == NULL) {
3462 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3463 return false;
3466 if (!lp_winbind_offline_logon()) {
3467 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3468 return false;
3471 if (global_winbindd_offline_state) {
3472 /* Already offline. */
3473 return true;
3476 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3478 if (!data.dptr || data.dsize != 4) {
3479 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3480 SAFE_FREE(data.dptr);
3481 return false;
3482 } else {
3483 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3484 global_winbindd_offline_state = true;
3485 SAFE_FREE(data.dptr);
3486 return true;
3490 void set_global_winbindd_state_online(void)
3492 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3494 if (!lp_winbind_offline_logon()) {
3495 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3496 return;
3499 if (!global_winbindd_offline_state) {
3500 /* Already online. */
3501 return;
3503 global_winbindd_offline_state = false;
3505 if (!wcache->tdb) {
3506 return;
3509 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3510 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3513 bool get_global_winbindd_state_offline(void)
3515 return global_winbindd_offline_state;
3518 /***********************************************************************
3519 Validate functions for all possible cache tdb keys.
3520 ***********************************************************************/
3522 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3523 struct tdb_validation_status *state)
3525 struct cache_entry *centry;
3527 centry = SMB_XMALLOC_P(struct cache_entry);
3528 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3529 if (!centry->data) {
3530 SAFE_FREE(centry);
3531 return NULL;
3533 centry->len = data.dsize;
3534 centry->ofs = 0;
3536 if (centry->len < 16) {
3537 /* huh? corrupt cache? */
3538 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3539 "(len < 16) ?\n", kstr));
3540 centry_free(centry);
3541 state->bad_entry = true;
3542 state->success = false;
3543 return NULL;
3546 centry->status = NT_STATUS(centry_uint32(centry));
3547 centry->sequence_number = centry_uint32(centry);
3548 centry->timeout = centry_uint64_t(centry);
3549 return centry;
3552 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3553 struct tdb_validation_status *state)
3555 if (dbuf.dsize != 8) {
3556 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3557 keystr, (unsigned int)dbuf.dsize ));
3558 state->bad_entry = true;
3559 return 1;
3561 return 0;
3564 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3565 struct tdb_validation_status *state)
3567 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3568 if (!centry) {
3569 return 1;
3572 (void)centry_uint32(centry);
3573 if (NT_STATUS_IS_OK(centry->status)) {
3574 struct dom_sid sid;
3575 (void)centry_sid(centry, &sid);
3578 centry_free(centry);
3580 if (!(state->success)) {
3581 return 1;
3583 DEBUG(10,("validate_ns: %s ok\n", keystr));
3584 return 0;
3587 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3588 struct tdb_validation_status *state)
3590 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3591 if (!centry) {
3592 return 1;
3595 if (NT_STATUS_IS_OK(centry->status)) {
3596 (void)centry_uint32(centry);
3597 (void)centry_string(centry, mem_ctx);
3598 (void)centry_string(centry, mem_ctx);
3601 centry_free(centry);
3603 if (!(state->success)) {
3604 return 1;
3606 DEBUG(10,("validate_sn: %s ok\n", keystr));
3607 return 0;
3610 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3611 struct tdb_validation_status *state)
3613 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3614 struct dom_sid sid;
3616 if (!centry) {
3617 return 1;
3620 (void)centry_string(centry, mem_ctx);
3621 (void)centry_string(centry, mem_ctx);
3622 (void)centry_string(centry, mem_ctx);
3623 (void)centry_string(centry, mem_ctx);
3624 (void)centry_uint32(centry);
3625 (void)centry_sid(centry, &sid);
3626 (void)centry_sid(centry, &sid);
3628 centry_free(centry);
3630 if (!(state->success)) {
3631 return 1;
3633 DEBUG(10,("validate_u: %s ok\n", keystr));
3634 return 0;
3637 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3638 struct tdb_validation_status *state)
3640 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3642 if (!centry) {
3643 return 1;
3646 (void)centry_nttime(centry);
3647 (void)centry_nttime(centry);
3648 (void)centry_uint16(centry);
3650 centry_free(centry);
3652 if (!(state->success)) {
3653 return 1;
3655 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3656 return 0;
3659 static int validate_pwd_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_uint16(centry);
3669 (void)centry_uint16(centry);
3670 (void)centry_uint32(centry);
3671 (void)centry_nttime(centry);
3672 (void)centry_nttime(centry);
3674 centry_free(centry);
3676 if (!(state->success)) {
3677 return 1;
3679 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3680 return 0;
3683 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3684 struct tdb_validation_status *state)
3686 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3688 if (!centry) {
3689 return 1;
3692 (void)centry_time(centry);
3693 (void)centry_hash16(centry, mem_ctx);
3695 /* We only have 17 bytes more data in the salted cred case. */
3696 if (centry->len - centry->ofs == 17) {
3697 (void)centry_hash16(centry, mem_ctx);
3700 centry_free(centry);
3702 if (!(state->success)) {
3703 return 1;
3705 DEBUG(10,("validate_cred: %s ok\n", keystr));
3706 return 0;
3709 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3710 struct tdb_validation_status *state)
3712 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3713 int32 num_entries, i;
3715 if (!centry) {
3716 return 1;
3719 num_entries = (int32)centry_uint32(centry);
3721 for (i=0; i< num_entries; i++) {
3722 struct dom_sid sid;
3723 (void)centry_string(centry, mem_ctx);
3724 (void)centry_string(centry, mem_ctx);
3725 (void)centry_string(centry, mem_ctx);
3726 (void)centry_string(centry, mem_ctx);
3727 (void)centry_sid(centry, &sid);
3728 (void)centry_sid(centry, &sid);
3731 centry_free(centry);
3733 if (!(state->success)) {
3734 return 1;
3736 DEBUG(10,("validate_ul: %s ok\n", keystr));
3737 return 0;
3740 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3741 struct tdb_validation_status *state)
3743 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3744 int32 num_entries, i;
3746 if (!centry) {
3747 return 1;
3750 num_entries = centry_uint32(centry);
3752 for (i=0; i< num_entries; i++) {
3753 (void)centry_string(centry, mem_ctx);
3754 (void)centry_string(centry, mem_ctx);
3755 (void)centry_uint32(centry);
3758 centry_free(centry);
3760 if (!(state->success)) {
3761 return 1;
3763 DEBUG(10,("validate_gl: %s ok\n", keystr));
3764 return 0;
3767 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3768 struct tdb_validation_status *state)
3770 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3771 int32 num_groups, i;
3773 if (!centry) {
3774 return 1;
3777 num_groups = centry_uint32(centry);
3779 for (i=0; i< num_groups; i++) {
3780 struct dom_sid sid;
3781 centry_sid(centry, &sid);
3784 centry_free(centry);
3786 if (!(state->success)) {
3787 return 1;
3789 DEBUG(10,("validate_ug: %s ok\n", keystr));
3790 return 0;
3793 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3794 struct tdb_validation_status *state)
3796 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3797 int32 num_aliases, i;
3799 if (!centry) {
3800 return 1;
3803 num_aliases = centry_uint32(centry);
3805 for (i=0; i < num_aliases; i++) {
3806 (void)centry_uint32(centry);
3809 centry_free(centry);
3811 if (!(state->success)) {
3812 return 1;
3814 DEBUG(10,("validate_ua: %s ok\n", keystr));
3815 return 0;
3818 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3819 struct tdb_validation_status *state)
3821 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3822 int32 num_names, i;
3824 if (!centry) {
3825 return 1;
3828 num_names = centry_uint32(centry);
3830 for (i=0; i< num_names; i++) {
3831 struct dom_sid sid;
3832 centry_sid(centry, &sid);
3833 (void)centry_string(centry, mem_ctx);
3834 (void)centry_uint32(centry);
3837 centry_free(centry);
3839 if (!(state->success)) {
3840 return 1;
3842 DEBUG(10,("validate_gm: %s ok\n", keystr));
3843 return 0;
3846 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3847 struct tdb_validation_status *state)
3849 /* Can't say anything about this other than must be nonzero. */
3850 if (dbuf.dsize == 0) {
3851 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3852 keystr));
3853 state->bad_entry = true;
3854 state->success = false;
3855 return 1;
3858 DEBUG(10,("validate_dr: %s ok\n", keystr));
3859 return 0;
3862 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3863 struct tdb_validation_status *state)
3865 /* Can't say anything about this other than must be nonzero. */
3866 if (dbuf.dsize == 0) {
3867 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3868 keystr));
3869 state->bad_entry = true;
3870 state->success = false;
3871 return 1;
3874 DEBUG(10,("validate_de: %s ok\n", keystr));
3875 return 0;
3878 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3879 TDB_DATA dbuf, struct tdb_validation_status *state)
3881 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3883 if (!centry) {
3884 return 1;
3887 (void)centry_string(centry, mem_ctx);
3888 (void)centry_string(centry, mem_ctx);
3889 (void)centry_string(centry, mem_ctx);
3890 (void)centry_uint32(centry);
3892 centry_free(centry);
3894 if (!(state->success)) {
3895 return 1;
3897 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3898 return 0;
3901 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3902 TDB_DATA dbuf,
3903 struct tdb_validation_status *state)
3905 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3907 if (!centry) {
3908 return 1;
3911 (void)centry_string( centry, mem_ctx );
3913 centry_free(centry);
3915 if (!(state->success)) {
3916 return 1;
3918 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3919 return 0;
3922 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3923 TDB_DATA dbuf,
3924 struct tdb_validation_status *state)
3926 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3928 if (!centry) {
3929 return 1;
3932 (void)centry_string( centry, mem_ctx );
3934 centry_free(centry);
3936 if (!(state->success)) {
3937 return 1;
3939 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3940 return 0;
3943 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3944 TDB_DATA dbuf,
3945 struct tdb_validation_status *state)
3947 if (dbuf.dsize == 0) {
3948 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3949 "key %s (len ==0) ?\n", keystr));
3950 state->bad_entry = true;
3951 state->success = false;
3952 return 1;
3955 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3956 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3957 return 0;
3960 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3961 struct tdb_validation_status *state)
3963 if (dbuf.dsize != 4) {
3964 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3965 keystr, (unsigned int)dbuf.dsize ));
3966 state->bad_entry = true;
3967 state->success = false;
3968 return 1;
3970 DEBUG(10,("validate_offline: %s ok\n", keystr));
3971 return 0;
3974 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3975 struct tdb_validation_status *state)
3978 * Ignore validation for now. The proper way to do this is with a
3979 * checksum. Just pure parsing does not really catch much.
3981 return 0;
3984 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3985 struct tdb_validation_status *state)
3987 if (dbuf.dsize != 4) {
3988 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3989 "key %s (len %u != 4) ?\n",
3990 keystr, (unsigned int)dbuf.dsize));
3991 state->bad_entry = true;
3992 state->success = false;
3993 return 1;
3996 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3997 return 0;
4000 /***********************************************************************
4001 A list of all possible cache tdb keys with associated validation
4002 functions.
4003 ***********************************************************************/
4005 struct key_val_struct {
4006 const char *keyname;
4007 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4008 } key_val[] = {
4009 {"SEQNUM/", validate_seqnum},
4010 {"NS/", validate_ns},
4011 {"SN/", validate_sn},
4012 {"U/", validate_u},
4013 {"LOC_POL/", validate_loc_pol},
4014 {"PWD_POL/", validate_pwd_pol},
4015 {"CRED/", validate_cred},
4016 {"UL/", validate_ul},
4017 {"GL/", validate_gl},
4018 {"UG/", validate_ug},
4019 {"UA", validate_ua},
4020 {"GM/", validate_gm},
4021 {"DR/", validate_dr},
4022 {"DE/", validate_de},
4023 {"NSS/PWINFO/", validate_pwinfo},
4024 {"TRUSTDOMCACHE/", validate_trustdomcache},
4025 {"NSS/NA/", validate_nss_na},
4026 {"NSS/AN/", validate_nss_an},
4027 {"WINBINDD_OFFLINE", validate_offline},
4028 {"NDR/", validate_ndr},
4029 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4030 {NULL, NULL}
4033 /***********************************************************************
4034 Function to look at every entry in the tdb and validate it as far as
4035 possible.
4036 ***********************************************************************/
4038 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4040 int i;
4041 unsigned int max_key_len = 1024;
4042 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4044 /* Paranoia check. */
4045 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4046 max_key_len = 1024 * 1024;
4048 if (kbuf.dsize > max_key_len) {
4049 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4050 "(%u) > (%u)\n\n",
4051 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4052 return 1;
4055 for (i = 0; key_val[i].keyname; i++) {
4056 size_t namelen = strlen(key_val[i].keyname);
4057 if (kbuf.dsize >= namelen && (
4058 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4059 TALLOC_CTX *mem_ctx;
4060 char *keystr;
4061 int ret;
4063 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4064 if (!keystr) {
4065 return 1;
4067 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4068 keystr[kbuf.dsize] = '\0';
4070 mem_ctx = talloc_init("validate_ctx");
4071 if (!mem_ctx) {
4072 SAFE_FREE(keystr);
4073 return 1;
4076 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4077 v_state);
4079 SAFE_FREE(keystr);
4080 talloc_destroy(mem_ctx);
4081 return ret;
4085 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4086 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4087 DEBUG(0,("data :\n"));
4088 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4089 v_state->unknown_key = true;
4090 v_state->success = false;
4091 return 1; /* terminate. */
4094 static void validate_panic(const char *const why)
4096 DEBUG(0,("validating cache: would panic %s\n", why ));
4097 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4098 exit(47);
4101 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4102 TDB_DATA key,
4103 TDB_DATA data,
4104 void *state)
4106 uint64_t ctimeout;
4107 TDB_DATA blob;
4109 if (is_non_centry_key(key)) {
4110 return 0;
4113 if (data.dptr == NULL || data.dsize == 0) {
4114 if (tdb_delete(tdb, key) < 0) {
4115 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4116 key.dptr));
4117 return 1;
4121 /* add timeout to blob (uint64_t) */
4122 blob.dsize = data.dsize + 8;
4124 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4125 if (blob.dptr == NULL) {
4126 return 1;
4128 memset(blob.dptr, 0, blob.dsize);
4130 /* copy status and seqnum */
4131 memcpy(blob.dptr, data.dptr, 8);
4133 /* add timeout */
4134 ctimeout = lp_winbind_cache_time() + time(NULL);
4135 SBVAL(blob.dptr, 8, ctimeout);
4137 /* copy the rest */
4138 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4140 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4141 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4142 key.dptr));
4143 SAFE_FREE(blob.dptr);
4144 return 1;
4147 SAFE_FREE(blob.dptr);
4148 return 0;
4151 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4153 int rc;
4155 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4157 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4158 if (rc < 0) {
4159 return false;
4162 return true;
4165 /***********************************************************************
4166 Try and validate every entry in the winbindd cache. If we fail here,
4167 delete the cache tdb and return non-zero.
4168 ***********************************************************************/
4170 int winbindd_validate_cache(void)
4172 int ret = -1;
4173 const char *tdb_path = state_path("winbindd_cache.tdb");
4174 TDB_CONTEXT *tdb = NULL;
4175 uint32_t vers_id;
4176 bool ok;
4178 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4179 smb_panic_fn = validate_panic;
4181 tdb = tdb_open_log(tdb_path,
4182 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4183 TDB_INCOMPATIBLE_HASH |
4184 ( lp_winbind_offline_logon()
4185 ? TDB_DEFAULT
4186 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4187 O_RDWR|O_CREAT,
4188 0600);
4189 if (!tdb) {
4190 DEBUG(0, ("winbindd_validate_cache: "
4191 "error opening/initializing tdb\n"));
4192 goto done;
4195 /* Version check and upgrade code. */
4196 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4197 DEBUG(10, ("Fresh database\n"));
4198 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4199 vers_id = WINBINDD_CACHE_VERSION;
4202 if (vers_id != WINBINDD_CACHE_VERSION) {
4203 if (vers_id == WINBINDD_CACHE_VER1) {
4204 ok = wbcache_upgrade_v1_to_v2(tdb);
4205 if (!ok) {
4206 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4207 unlink(tdb_path);
4208 goto done;
4211 tdb_store_uint32(tdb,
4212 WINBINDD_CACHE_VERSION_KEYSTR,
4213 WINBINDD_CACHE_VERSION);
4214 vers_id = WINBINDD_CACHE_VER2;
4218 tdb_close(tdb);
4220 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4222 if (ret != 0) {
4223 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4224 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4225 unlink(tdb_path);
4228 done:
4229 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4230 smb_panic_fn = smb_panic;
4231 return ret;
4234 /***********************************************************************
4235 Try and validate every entry in the winbindd cache.
4236 ***********************************************************************/
4238 int winbindd_validate_cache_nobackup(void)
4240 int ret = -1;
4241 const char *tdb_path = state_path("winbindd_cache.tdb");
4243 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4244 smb_panic_fn = validate_panic;
4247 if (wcache == NULL || wcache->tdb == NULL) {
4248 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4249 } else {
4250 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4253 if (ret != 0) {
4254 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4255 "successful.\n"));
4258 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4259 "function\n"));
4260 smb_panic_fn = smb_panic;
4261 return ret;
4264 bool winbindd_cache_validate_and_initialize(void)
4266 close_winbindd_cache();
4268 if (lp_winbind_offline_logon()) {
4269 if (winbindd_validate_cache() < 0) {
4270 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4271 "could be restored.\n"));
4275 return initialize_winbindd_cache();
4278 /*********************************************************************
4279 ********************************************************************/
4281 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4282 struct winbindd_tdc_domain **domains,
4283 size_t *num_domains )
4285 struct winbindd_tdc_domain *list = NULL;
4286 size_t idx;
4287 int i;
4288 bool set_only = false;
4290 /* don't allow duplicates */
4292 idx = *num_domains;
4293 list = *domains;
4295 for ( i=0; i< (*num_domains); i++ ) {
4296 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4297 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4298 new_dom->name));
4299 idx = i;
4300 set_only = true;
4302 break;
4306 if ( !set_only ) {
4307 if ( !*domains ) {
4308 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4309 idx = 0;
4310 } else {
4311 list = talloc_realloc( *domains, *domains,
4312 struct winbindd_tdc_domain,
4313 (*num_domains)+1);
4314 idx = *num_domains;
4317 ZERO_STRUCT( list[idx] );
4320 if ( !list )
4321 return false;
4323 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4324 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4326 if ( !is_null_sid( &new_dom->sid ) ) {
4327 sid_copy( &list[idx].sid, &new_dom->sid );
4328 } else {
4329 sid_copy(&list[idx].sid, &global_sid_NULL);
4332 if ( new_dom->domain_flags != 0x0 )
4333 list[idx].trust_flags = new_dom->domain_flags;
4335 if ( new_dom->domain_type != 0x0 )
4336 list[idx].trust_type = new_dom->domain_type;
4338 if ( new_dom->domain_trust_attribs != 0x0 )
4339 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4341 if ( !set_only ) {
4342 *domains = list;
4343 *num_domains = idx + 1;
4346 return true;
4349 /*********************************************************************
4350 ********************************************************************/
4352 static TDB_DATA make_tdc_key( const char *domain_name )
4354 char *keystr = NULL;
4355 TDB_DATA key = { NULL, 0 };
4357 if ( !domain_name ) {
4358 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4359 return key;
4362 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4363 return key;
4365 key = string_term_tdb_data(keystr);
4367 return key;
4370 /*********************************************************************
4371 ********************************************************************/
4373 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4374 size_t num_domains,
4375 unsigned char **buf )
4377 unsigned char *buffer = NULL;
4378 int len = 0;
4379 int buflen = 0;
4380 int i = 0;
4382 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4383 (int)num_domains));
4385 buflen = 0;
4387 again:
4388 len = 0;
4390 /* Store the number of array items first */
4391 len += tdb_pack( buffer+len, buflen-len, "d",
4392 num_domains );
4394 /* now pack each domain trust record */
4395 for ( i=0; i<num_domains; i++ ) {
4397 fstring tmp;
4399 if ( buflen > 0 ) {
4400 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4401 domains[i].domain_name,
4402 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4405 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4406 domains[i].domain_name,
4407 domains[i].dns_name,
4408 sid_to_fstring(tmp, &domains[i].sid),
4409 domains[i].trust_flags,
4410 domains[i].trust_attribs,
4411 domains[i].trust_type );
4414 if ( buflen < len ) {
4415 SAFE_FREE(buffer);
4416 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4417 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4418 buflen = -1;
4419 goto done;
4421 buflen = len;
4422 goto again;
4425 *buf = buffer;
4427 done:
4428 return buflen;
4431 /*********************************************************************
4432 ********************************************************************/
4434 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4435 struct winbindd_tdc_domain **domains )
4437 fstring domain_name, dns_name, sid_string;
4438 uint32 type, attribs, flags;
4439 int num_domains;
4440 int len = 0;
4441 int i;
4442 struct winbindd_tdc_domain *list = NULL;
4444 /* get the number of domains */
4445 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4446 if ( len == -1 ) {
4447 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4448 return 0;
4451 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4452 if ( !list ) {
4453 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4454 return 0;
4457 for ( i=0; i<num_domains; i++ ) {
4458 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4459 domain_name,
4460 dns_name,
4461 sid_string,
4462 &flags,
4463 &attribs,
4464 &type );
4466 if ( len == -1 ) {
4467 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4468 TALLOC_FREE( list );
4469 return 0;
4472 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4473 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4474 domain_name, dns_name, sid_string,
4475 flags, attribs, type));
4477 list[i].domain_name = talloc_strdup( list, domain_name );
4478 list[i].dns_name = talloc_strdup( list, dns_name );
4479 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4480 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4481 domain_name));
4483 list[i].trust_flags = flags;
4484 list[i].trust_attribs = attribs;
4485 list[i].trust_type = type;
4488 *domains = list;
4490 return num_domains;
4493 /*********************************************************************
4494 ********************************************************************/
4496 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4498 TDB_DATA key = make_tdc_key( lp_workgroup() );
4499 TDB_DATA data = { NULL, 0 };
4500 int ret;
4502 if ( !key.dptr )
4503 return false;
4505 /* See if we were asked to delete the cache entry */
4507 if ( !domains ) {
4508 ret = tdb_delete( wcache->tdb, key );
4509 goto done;
4512 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4514 if ( !data.dptr ) {
4515 ret = -1;
4516 goto done;
4519 ret = tdb_store( wcache->tdb, key, data, 0 );
4521 done:
4522 SAFE_FREE( data.dptr );
4523 SAFE_FREE( key.dptr );
4525 return ( ret == 0 );
4528 /*********************************************************************
4529 ********************************************************************/
4531 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4533 TDB_DATA key = make_tdc_key( lp_workgroup() );
4534 TDB_DATA data = { NULL, 0 };
4536 *domains = NULL;
4537 *num_domains = 0;
4539 if ( !key.dptr )
4540 return false;
4542 data = tdb_fetch_compat( wcache->tdb, key );
4544 SAFE_FREE( key.dptr );
4546 if ( !data.dptr )
4547 return false;
4549 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4551 SAFE_FREE( data.dptr );
4553 if ( !*domains )
4554 return false;
4556 return true;
4559 /*********************************************************************
4560 ********************************************************************/
4562 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4564 struct winbindd_tdc_domain *dom_list = NULL;
4565 size_t num_domains = 0;
4566 bool ret = false;
4568 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4569 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4570 domain->name, domain->alt_name,
4571 sid_string_dbg(&domain->sid),
4572 domain->domain_flags,
4573 domain->domain_trust_attribs,
4574 domain->domain_type));
4576 if ( !init_wcache() ) {
4577 return false;
4580 /* fetch the list */
4582 wcache_tdc_fetch_list( &dom_list, &num_domains );
4584 /* add the new domain */
4586 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4587 goto done;
4590 /* pack the domain */
4592 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4593 goto done;
4596 /* Success */
4598 ret = true;
4599 done:
4600 TALLOC_FREE( dom_list );
4602 return ret;
4605 /*********************************************************************
4606 ********************************************************************/
4608 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4610 struct winbindd_tdc_domain *dom_list = NULL;
4611 size_t num_domains = 0;
4612 int i;
4613 struct winbindd_tdc_domain *d = NULL;
4615 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4617 if ( !init_wcache() ) {
4618 return NULL;
4621 /* fetch the list */
4623 wcache_tdc_fetch_list( &dom_list, &num_domains );
4625 for ( i=0; i<num_domains; i++ ) {
4626 if ( strequal(name, dom_list[i].domain_name) ||
4627 strequal(name, dom_list[i].dns_name) )
4629 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4630 name));
4632 d = talloc( ctx, struct winbindd_tdc_domain );
4633 if ( !d )
4634 break;
4636 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4637 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4638 sid_copy( &d->sid, &dom_list[i].sid );
4639 d->trust_flags = dom_list[i].trust_flags;
4640 d->trust_type = dom_list[i].trust_type;
4641 d->trust_attribs = dom_list[i].trust_attribs;
4643 break;
4647 TALLOC_FREE( dom_list );
4649 return d;
4652 /*********************************************************************
4653 ********************************************************************/
4655 struct winbindd_tdc_domain*
4656 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4657 const struct dom_sid *sid)
4659 struct winbindd_tdc_domain *dom_list = NULL;
4660 size_t num_domains = 0;
4661 int i;
4662 struct winbindd_tdc_domain *d = NULL;
4664 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4665 sid_string_dbg(sid)));
4667 if (!init_wcache()) {
4668 return NULL;
4671 /* fetch the list */
4673 wcache_tdc_fetch_list(&dom_list, &num_domains);
4675 for (i = 0; i<num_domains; i++) {
4676 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4677 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4678 "Found domain %s for SID %s\n",
4679 dom_list[i].domain_name,
4680 sid_string_dbg(sid)));
4682 d = talloc(ctx, struct winbindd_tdc_domain);
4683 if (!d)
4684 break;
4686 d->domain_name = talloc_strdup(d,
4687 dom_list[i].domain_name);
4689 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4690 sid_copy(&d->sid, &dom_list[i].sid);
4691 d->trust_flags = dom_list[i].trust_flags;
4692 d->trust_type = dom_list[i].trust_type;
4693 d->trust_attribs = dom_list[i].trust_attribs;
4695 break;
4699 TALLOC_FREE(dom_list);
4701 return d;
4705 /*********************************************************************
4706 ********************************************************************/
4708 void wcache_tdc_clear( void )
4710 if ( !init_wcache() )
4711 return;
4713 wcache_tdc_store_list( NULL, 0 );
4715 return;
4719 /*********************************************************************
4720 ********************************************************************/
4722 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4723 NTSTATUS status,
4724 const struct dom_sid *user_sid,
4725 const char *homedir,
4726 const char *shell,
4727 const char *gecos,
4728 uint32 gid)
4730 struct cache_entry *centry;
4731 fstring tmp;
4733 if ( (centry = centry_start(domain, status)) == NULL )
4734 return;
4736 centry_put_string( centry, homedir );
4737 centry_put_string( centry, shell );
4738 centry_put_string( centry, gecos );
4739 centry_put_uint32( centry, gid );
4741 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4743 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4745 centry_free(centry);
4748 #ifdef HAVE_ADS
4750 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4751 const struct dom_sid *user_sid,
4752 TALLOC_CTX *ctx,
4753 const char **homedir, const char **shell,
4754 const char **gecos, gid_t *p_gid)
4756 struct winbind_cache *cache = get_cache(domain);
4757 struct cache_entry *centry = NULL;
4758 NTSTATUS nt_status;
4759 fstring tmp;
4761 if (!cache->tdb)
4762 goto do_query;
4764 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4765 sid_to_fstring(tmp, user_sid));
4767 if (!centry)
4768 goto do_query;
4770 *homedir = centry_string( centry, ctx );
4771 *shell = centry_string( centry, ctx );
4772 *gecos = centry_string( centry, ctx );
4773 *p_gid = centry_uint32( centry );
4775 centry_free(centry);
4777 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4778 sid_string_dbg(user_sid)));
4780 return NT_STATUS_OK;
4782 do_query:
4784 nt_status = nss_get_info( domain->name, user_sid, ctx,
4785 homedir, shell, gecos, p_gid );
4787 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4789 if ( NT_STATUS_IS_OK(nt_status) ) {
4790 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4791 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4792 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4793 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4795 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4796 *homedir, *shell, *gecos, *p_gid );
4799 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4800 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4801 domain->name ));
4802 set_domain_offline( domain );
4805 return nt_status;
4808 #endif
4810 /* the cache backend methods are exposed via this structure */
4811 struct winbindd_methods cache_methods = {
4812 true,
4813 query_user_list,
4814 enum_dom_groups,
4815 enum_local_groups,
4816 name_to_sid,
4817 sid_to_name,
4818 rids_to_names,
4819 query_user,
4820 lookup_usergroups,
4821 lookup_useraliases,
4822 lookup_groupmem,
4823 sequence_number,
4824 lockout_policy,
4825 password_policy,
4826 trusted_domains
4829 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4830 uint32_t opnum, const DATA_BLOB *req,
4831 TDB_DATA *pkey)
4833 char *key;
4834 size_t keylen;
4836 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4837 if (key == NULL) {
4838 return false;
4840 keylen = talloc_get_size(key) - 1;
4842 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4843 if (key == NULL) {
4844 return false;
4846 memcpy(key + keylen, req->data, req->length);
4848 pkey->dptr = (uint8_t *)key;
4849 pkey->dsize = talloc_get_size(key);
4850 return true;
4853 static bool wcache_opnum_cacheable(uint32_t opnum)
4855 switch (opnum) {
4856 case NDR_WBINT_PING:
4857 case NDR_WBINT_QUERYSEQUENCENUMBER:
4858 case NDR_WBINT_ALLOCATEUID:
4859 case NDR_WBINT_ALLOCATEGID:
4860 case NDR_WBINT_CHECKMACHINEACCOUNT:
4861 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4862 case NDR_WBINT_PINGDC:
4863 return false;
4865 return true;
4868 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4869 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4871 TDB_DATA key, data;
4872 bool ret = false;
4874 if (!wcache_opnum_cacheable(opnum) ||
4875 is_my_own_sam_domain(domain) ||
4876 is_builtin_domain(domain)) {
4877 return false;
4880 if (wcache->tdb == NULL) {
4881 return false;
4884 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4885 return false;
4887 data = tdb_fetch_compat(wcache->tdb, key);
4888 TALLOC_FREE(key.dptr);
4890 if (data.dptr == NULL) {
4891 return false;
4893 if (data.dsize < 12) {
4894 goto fail;
4897 if (!is_domain_offline(domain)) {
4898 uint32_t entry_seqnum, dom_seqnum, last_check;
4899 uint64_t entry_timeout;
4901 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4902 &last_check)) {
4903 goto fail;
4905 entry_seqnum = IVAL(data.dptr, 0);
4906 if (entry_seqnum != dom_seqnum) {
4907 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4908 (int)entry_seqnum));
4909 goto fail;
4911 entry_timeout = BVAL(data.dptr, 4);
4912 if (time(NULL) > entry_timeout) {
4913 DEBUG(10, ("Entry has timed out\n"));
4914 goto fail;
4918 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4919 data.dsize - 12);
4920 if (resp->data == NULL) {
4921 DEBUG(10, ("talloc failed\n"));
4922 goto fail;
4924 resp->length = data.dsize - 12;
4926 ret = true;
4927 fail:
4928 SAFE_FREE(data.dptr);
4929 return ret;
4932 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4933 const DATA_BLOB *req, const DATA_BLOB *resp)
4935 TDB_DATA key, data;
4936 uint32_t dom_seqnum, last_check;
4937 uint64_t timeout;
4939 if (!wcache_opnum_cacheable(opnum) ||
4940 is_my_own_sam_domain(domain) ||
4941 is_builtin_domain(domain)) {
4942 return;
4945 if (wcache->tdb == NULL) {
4946 return;
4949 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4950 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4951 domain->name));
4952 return;
4955 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4956 return;
4959 timeout = time(NULL) + lp_winbind_cache_time();
4961 data.dsize = resp->length + 12;
4962 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4963 if (data.dptr == NULL) {
4964 goto done;
4967 SIVAL(data.dptr, 0, dom_seqnum);
4968 SBVAL(data.dptr, 4, timeout);
4969 memcpy(data.dptr + 12, resp->data, resp->length);
4971 tdb_store(wcache->tdb, key, data, 0);
4973 done:
4974 TALLOC_FREE(key.dptr);
4975 return;