s3:winbindd fix use of uninitialized variables
[Samba.git] / source3 / winbindd / winbindd_cache.c
blobec7ddb7ed8881b4fdba5c89b99743c00e37aa060
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
947 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
948 struct winbindd_domain *mydomain =
949 find_domain_from_sid_noinit(sid);
950 if (mydomain != NULL) {
951 domain_name = mydomain->name;
955 centry_put_uint32(centry, type);
956 centry_put_sid(centry, sid);
957 fstrcpy(uname, name);
958 (void)strupper_m(uname);
959 centry_end(centry, "NS/%s/%s", domain_name, uname);
960 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
961 uname, sid_string_dbg(sid), nt_errstr(status)));
962 centry_free(centry);
965 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
966 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
968 struct cache_entry *centry;
969 fstring sid_string;
971 centry = centry_start(domain, status);
972 if (!centry)
973 return;
975 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
976 struct winbindd_domain *mydomain =
977 find_domain_from_sid_noinit(sid);
978 if (mydomain != NULL) {
979 domain_name = mydomain->name;
983 if (NT_STATUS_IS_OK(status)) {
984 centry_put_uint32(centry, type);
985 centry_put_string(centry, domain_name);
986 centry_put_string(centry, name);
989 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
990 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
991 domain_name, name, nt_errstr(status)));
992 centry_free(centry);
996 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
997 struct wbint_userinfo *info)
999 struct cache_entry *centry;
1000 fstring sid_string;
1002 if (is_null_sid(&info->user_sid)) {
1003 return;
1006 centry = centry_start(domain, status);
1007 if (!centry)
1008 return;
1009 centry_put_string(centry, info->acct_name);
1010 centry_put_string(centry, info->full_name);
1011 centry_put_string(centry, info->homedir);
1012 centry_put_string(centry, info->shell);
1013 centry_put_uint32(centry, info->primary_gid);
1014 centry_put_sid(centry, &info->user_sid);
1015 centry_put_sid(centry, &info->group_sid);
1016 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1017 &info->user_sid));
1018 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1019 centry_free(centry);
1022 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1023 NTSTATUS status,
1024 struct samr_DomInfo12 *lockout_policy)
1026 struct cache_entry *centry;
1028 centry = centry_start(domain, status);
1029 if (!centry)
1030 return;
1032 centry_put_nttime(centry, lockout_policy->lockout_duration);
1033 centry_put_nttime(centry, lockout_policy->lockout_window);
1034 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1036 centry_end(centry, "LOC_POL/%s", domain->name);
1038 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1040 centry_free(centry);
1045 static void wcache_save_password_policy(struct winbindd_domain *domain,
1046 NTSTATUS status,
1047 struct samr_DomInfo1 *policy)
1049 struct cache_entry *centry;
1051 centry = centry_start(domain, status);
1052 if (!centry)
1053 return;
1055 centry_put_uint16(centry, policy->min_password_length);
1056 centry_put_uint16(centry, policy->password_history_length);
1057 centry_put_uint32(centry, policy->password_properties);
1058 centry_put_nttime(centry, policy->max_password_age);
1059 centry_put_nttime(centry, policy->min_password_age);
1061 centry_end(centry, "PWD_POL/%s", domain->name);
1063 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1065 centry_free(centry);
1068 /***************************************************************************
1069 ***************************************************************************/
1071 static void wcache_save_username_alias(struct winbindd_domain *domain,
1072 NTSTATUS status,
1073 const char *name, const char *alias)
1075 struct cache_entry *centry;
1076 fstring uname;
1078 if ( (centry = centry_start(domain, status)) == NULL )
1079 return;
1081 centry_put_string( centry, alias );
1083 fstrcpy(uname, name);
1084 (void)strupper_m(uname);
1085 centry_end(centry, "NSS/NA/%s", uname);
1087 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1089 centry_free(centry);
1092 static void wcache_save_alias_username(struct winbindd_domain *domain,
1093 NTSTATUS status,
1094 const char *alias, const char *name)
1096 struct cache_entry *centry;
1097 fstring uname;
1099 if ( (centry = centry_start(domain, status)) == NULL )
1100 return;
1102 centry_put_string( centry, name );
1104 fstrcpy(uname, alias);
1105 (void)strupper_m(uname);
1106 centry_end(centry, "NSS/AN/%s", uname);
1108 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1110 centry_free(centry);
1113 /***************************************************************************
1114 ***************************************************************************/
1116 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1117 struct winbindd_domain *domain,
1118 const char *name, char **alias )
1120 struct winbind_cache *cache = get_cache(domain);
1121 struct cache_entry *centry = NULL;
1122 NTSTATUS status;
1123 char *upper_name;
1125 if ( domain->internal )
1126 return NT_STATUS_NOT_SUPPORTED;
1128 if (!cache->tdb)
1129 goto do_query;
1131 upper_name = talloc_strdup(mem_ctx, name);
1132 if (upper_name == NULL) {
1133 return NT_STATUS_NO_MEMORY;
1135 if (!strupper_m(upper_name)) {
1136 talloc_free(upper_name);
1137 return NT_STATUS_INVALID_PARAMETER;
1140 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1142 talloc_free(upper_name);
1144 if (!centry)
1145 goto do_query;
1147 status = centry->status;
1149 if (!NT_STATUS_IS_OK(status)) {
1150 centry_free(centry);
1151 return status;
1154 *alias = centry_string( centry, mem_ctx );
1156 centry_free(centry);
1158 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1159 name, *alias ? *alias : "(none)"));
1161 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1163 do_query:
1165 /* If its not in cache and we are offline, then fail */
1167 if ( get_global_winbindd_state_offline() || !domain->online ) {
1168 DEBUG(8,("resolve_username_to_alias: rejecting query "
1169 "in offline mode\n"));
1170 return NT_STATUS_NOT_FOUND;
1173 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1175 if ( NT_STATUS_IS_OK( status ) ) {
1176 wcache_save_username_alias(domain, status, name, *alias);
1179 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1180 wcache_save_username_alias(domain, status, name, "(NULL)");
1183 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1184 nt_errstr(status)));
1186 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1187 set_domain_offline( domain );
1190 return status;
1193 /***************************************************************************
1194 ***************************************************************************/
1196 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1197 struct winbindd_domain *domain,
1198 const char *alias, char **name )
1200 struct winbind_cache *cache = get_cache(domain);
1201 struct cache_entry *centry = NULL;
1202 NTSTATUS status;
1203 char *upper_name;
1205 if ( domain->internal )
1206 return NT_STATUS_NOT_SUPPORTED;
1208 if (!cache->tdb)
1209 goto do_query;
1211 upper_name = talloc_strdup(mem_ctx, alias);
1212 if (upper_name == NULL) {
1213 return NT_STATUS_NO_MEMORY;
1215 if (!strupper_m(upper_name)) {
1216 talloc_free(upper_name);
1217 return NT_STATUS_INVALID_PARAMETER;
1220 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1222 talloc_free(upper_name);
1224 if (!centry)
1225 goto do_query;
1227 status = centry->status;
1229 if (!NT_STATUS_IS_OK(status)) {
1230 centry_free(centry);
1231 return status;
1234 *name = centry_string( centry, mem_ctx );
1236 centry_free(centry);
1238 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1239 alias, *name ? *name : "(none)"));
1241 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1243 do_query:
1245 /* If its not in cache and we are offline, then fail */
1247 if ( get_global_winbindd_state_offline() || !domain->online ) {
1248 DEBUG(8,("resolve_alias_to_username: rejecting query "
1249 "in offline mode\n"));
1250 return NT_STATUS_NOT_FOUND;
1253 /* an alias cannot contain a domain prefix or '@' */
1255 if (strchr(alias, '\\') || strchr(alias, '@')) {
1256 DEBUG(10,("resolve_alias_to_username: skipping fully "
1257 "qualified name %s\n", alias));
1258 return NT_STATUS_OBJECT_NAME_INVALID;
1261 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1263 if ( NT_STATUS_IS_OK( status ) ) {
1264 wcache_save_alias_username( domain, status, alias, *name );
1267 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1268 wcache_save_alias_username(domain, status, alias, "(NULL)");
1271 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1272 nt_errstr(status)));
1274 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1275 set_domain_offline( domain );
1278 return status;
1281 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1283 struct winbind_cache *cache = get_cache(domain);
1284 TDB_DATA data;
1285 fstring key_str, tmp;
1286 uint32 rid;
1288 if (!cache->tdb) {
1289 return NT_STATUS_INTERNAL_DB_ERROR;
1292 if (is_null_sid(sid)) {
1293 return NT_STATUS_INVALID_SID;
1296 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1297 return NT_STATUS_INVALID_SID;
1300 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1302 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1303 if (!data.dptr) {
1304 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1307 SAFE_FREE(data.dptr);
1308 return NT_STATUS_OK;
1311 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1312 as new salted ones. */
1314 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1315 TALLOC_CTX *mem_ctx,
1316 const struct dom_sid *sid,
1317 const uint8 **cached_nt_pass,
1318 const uint8 **cached_salt)
1320 struct winbind_cache *cache = get_cache(domain);
1321 struct cache_entry *centry = NULL;
1322 NTSTATUS status;
1323 uint32 rid;
1324 fstring tmp;
1326 if (!cache->tdb) {
1327 return NT_STATUS_INTERNAL_DB_ERROR;
1330 if (is_null_sid(sid)) {
1331 return NT_STATUS_INVALID_SID;
1334 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1335 return NT_STATUS_INVALID_SID;
1338 /* Try and get a salted cred first. If we can't
1339 fall back to an unsalted cred. */
1341 centry = wcache_fetch(cache, domain, "CRED/%s",
1342 sid_to_fstring(tmp, sid));
1343 if (!centry) {
1344 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1345 sid_string_dbg(sid)));
1346 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1350 * We don't use the time element at this moment,
1351 * but we have to consume it, so that we don't
1352 * neet to change the disk format of the cache.
1354 (void)centry_time(centry);
1356 /* In the salted case this isn't actually the nt_hash itself,
1357 but the MD5 of the salt + nt_hash. Let the caller
1358 sort this out. It can tell as we only return the cached_salt
1359 if we are returning a salted cred. */
1361 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1362 if (*cached_nt_pass == NULL) {
1363 fstring sidstr;
1365 sid_to_fstring(sidstr, sid);
1367 /* Bad (old) cred cache. Delete and pretend we
1368 don't have it. */
1369 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1370 sidstr));
1371 wcache_delete("CRED/%s", sidstr);
1372 centry_free(centry);
1373 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1376 /* We only have 17 bytes more data in the salted cred case. */
1377 if (centry->len - centry->ofs == 17) {
1378 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1379 } else {
1380 *cached_salt = NULL;
1383 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1384 if (*cached_salt) {
1385 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1388 status = centry->status;
1390 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1391 sid_string_dbg(sid), nt_errstr(status) ));
1393 centry_free(centry);
1394 return status;
1397 /* Store creds for a SID - only writes out new salted ones. */
1399 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1400 const struct dom_sid *sid,
1401 const uint8 nt_pass[NT_HASH_LEN])
1403 struct cache_entry *centry;
1404 fstring sid_string;
1405 uint32 rid;
1406 uint8 cred_salt[NT_HASH_LEN];
1407 uint8 salted_hash[NT_HASH_LEN];
1409 if (is_null_sid(sid)) {
1410 return NT_STATUS_INVALID_SID;
1413 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1414 return NT_STATUS_INVALID_SID;
1417 centry = centry_start(domain, NT_STATUS_OK);
1418 if (!centry) {
1419 return NT_STATUS_INTERNAL_DB_ERROR;
1422 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1424 centry_put_time(centry, time(NULL));
1426 /* Create a salt and then salt the hash. */
1427 generate_random_buffer(cred_salt, NT_HASH_LEN);
1428 E_md5hash(cred_salt, nt_pass, salted_hash);
1430 centry_put_hash16(centry, salted_hash);
1431 centry_put_hash16(centry, cred_salt);
1432 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1434 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1436 centry_free(centry);
1438 return NT_STATUS_OK;
1442 /* Query display info. This is the basic user list fn */
1443 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1444 TALLOC_CTX *mem_ctx,
1445 uint32 *num_entries,
1446 struct wbint_userinfo **info)
1448 struct winbind_cache *cache = get_cache(domain);
1449 struct cache_entry *centry = NULL;
1450 NTSTATUS status;
1451 unsigned int i, retry;
1452 bool old_status = domain->online;
1454 if (!cache->tdb)
1455 goto do_query;
1457 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1458 if (!centry)
1459 goto do_query;
1461 do_fetch_cache:
1462 *num_entries = centry_uint32(centry);
1464 if (*num_entries == 0)
1465 goto do_cached;
1467 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1468 if (! (*info)) {
1469 smb_panic_fn("query_user_list out of memory");
1471 for (i=0; i<(*num_entries); i++) {
1472 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1473 (*info)[i].full_name = centry_string(centry, mem_ctx);
1474 (*info)[i].homedir = centry_string(centry, mem_ctx);
1475 (*info)[i].shell = centry_string(centry, mem_ctx);
1476 centry_sid(centry, &(*info)[i].user_sid);
1477 centry_sid(centry, &(*info)[i].group_sid);
1480 do_cached:
1481 status = centry->status;
1483 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1484 domain->name, nt_errstr(status) ));
1486 centry_free(centry);
1487 return status;
1489 do_query:
1490 *num_entries = 0;
1491 *info = NULL;
1493 /* Return status value returned by seq number check */
1495 if (!NT_STATUS_IS_OK(domain->last_status))
1496 return domain->last_status;
1498 /* Put the query_user_list() in a retry loop. There appears to be
1499 * some bug either with Windows 2000 or Samba's handling of large
1500 * rpc replies. This manifests itself as sudden disconnection
1501 * at a random point in the enumeration of a large (60k) user list.
1502 * The retry loop simply tries the operation again. )-: It's not
1503 * pretty but an acceptable workaround until we work out what the
1504 * real problem is. */
1506 retry = 0;
1507 do {
1509 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1510 domain->name ));
1512 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1513 if (!NT_STATUS_IS_OK(status)) {
1514 DEBUG(3, ("query_user_list: returned 0x%08x, "
1515 "retrying\n", NT_STATUS_V(status)));
1517 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1518 DEBUG(3, ("query_user_list: flushing "
1519 "connection cache\n"));
1520 invalidate_cm_connection(&domain->conn);
1522 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1523 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1524 if (!domain->internal && old_status) {
1525 set_domain_offline(domain);
1527 /* store partial response. */
1528 if (*num_entries > 0) {
1530 * humm, what about the status used for cache?
1531 * Should it be NT_STATUS_OK?
1533 break;
1536 * domain is offline now, and there is no user entries,
1537 * try to fetch from cache again.
1539 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1540 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1541 /* partial response... */
1542 if (!centry) {
1543 goto skip_save;
1544 } else {
1545 goto do_fetch_cache;
1547 } else {
1548 goto skip_save;
1552 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1553 (retry++ < 5));
1555 /* and save it */
1556 refresh_sequence_number(domain, false);
1557 if (!NT_STATUS_IS_OK(status)) {
1558 return status;
1560 centry = centry_start(domain, status);
1561 if (!centry)
1562 goto skip_save;
1563 centry_put_uint32(centry, *num_entries);
1564 for (i=0; i<(*num_entries); i++) {
1565 centry_put_string(centry, (*info)[i].acct_name);
1566 centry_put_string(centry, (*info)[i].full_name);
1567 centry_put_string(centry, (*info)[i].homedir);
1568 centry_put_string(centry, (*info)[i].shell);
1569 centry_put_sid(centry, &(*info)[i].user_sid);
1570 centry_put_sid(centry, &(*info)[i].group_sid);
1571 if (domain->backend && domain->backend->consistent) {
1572 /* when the backend is consistent we can pre-prime some mappings */
1573 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1574 domain->name,
1575 (*info)[i].acct_name,
1576 &(*info)[i].user_sid,
1577 SID_NAME_USER);
1578 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1579 &(*info)[i].user_sid,
1580 domain->name,
1581 (*info)[i].acct_name,
1582 SID_NAME_USER);
1583 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1586 centry_end(centry, "UL/%s", domain->name);
1587 centry_free(centry);
1589 skip_save:
1590 return status;
1593 /* list all domain groups */
1594 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1595 TALLOC_CTX *mem_ctx,
1596 uint32 *num_entries,
1597 struct wb_acct_info **info)
1599 struct winbind_cache *cache = get_cache(domain);
1600 struct cache_entry *centry = NULL;
1601 NTSTATUS status;
1602 unsigned int i;
1603 bool old_status;
1605 old_status = domain->online;
1606 if (!cache->tdb)
1607 goto do_query;
1609 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1610 if (!centry)
1611 goto do_query;
1613 do_fetch_cache:
1614 *num_entries = centry_uint32(centry);
1616 if (*num_entries == 0)
1617 goto do_cached;
1619 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1620 if (! (*info)) {
1621 smb_panic_fn("enum_dom_groups out of memory");
1623 for (i=0; i<(*num_entries); i++) {
1624 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1625 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1626 (*info)[i].rid = centry_uint32(centry);
1629 do_cached:
1630 status = centry->status;
1632 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1633 domain->name, nt_errstr(status) ));
1635 centry_free(centry);
1636 return status;
1638 do_query:
1639 *num_entries = 0;
1640 *info = NULL;
1642 /* Return status value returned by seq number check */
1644 if (!NT_STATUS_IS_OK(domain->last_status))
1645 return domain->last_status;
1647 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1648 domain->name ));
1650 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1652 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1653 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1654 if (!domain->internal && old_status) {
1655 set_domain_offline(domain);
1657 if (cache->tdb &&
1658 !domain->online &&
1659 !domain->internal &&
1660 old_status) {
1661 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1662 if (centry) {
1663 goto do_fetch_cache;
1667 /* and save it */
1668 refresh_sequence_number(domain, false);
1669 if (!NT_STATUS_IS_OK(status)) {
1670 return status;
1672 centry = centry_start(domain, status);
1673 if (!centry)
1674 goto skip_save;
1675 centry_put_uint32(centry, *num_entries);
1676 for (i=0; i<(*num_entries); i++) {
1677 centry_put_string(centry, (*info)[i].acct_name);
1678 centry_put_string(centry, (*info)[i].acct_desc);
1679 centry_put_uint32(centry, (*info)[i].rid);
1681 centry_end(centry, "GL/%s/domain", domain->name);
1682 centry_free(centry);
1684 skip_save:
1685 return status;
1688 /* list all domain groups */
1689 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1690 TALLOC_CTX *mem_ctx,
1691 uint32 *num_entries,
1692 struct wb_acct_info **info)
1694 struct winbind_cache *cache = get_cache(domain);
1695 struct cache_entry *centry = NULL;
1696 NTSTATUS status;
1697 unsigned int i;
1698 bool old_status;
1700 old_status = domain->online;
1701 if (!cache->tdb)
1702 goto do_query;
1704 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1705 if (!centry)
1706 goto do_query;
1708 do_fetch_cache:
1709 *num_entries = centry_uint32(centry);
1711 if (*num_entries == 0)
1712 goto do_cached;
1714 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1715 if (! (*info)) {
1716 smb_panic_fn("enum_dom_groups out of memory");
1718 for (i=0; i<(*num_entries); i++) {
1719 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1720 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1721 (*info)[i].rid = centry_uint32(centry);
1724 do_cached:
1726 /* If we are returning cached data and the domain controller
1727 is down then we don't know whether the data is up to date
1728 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1729 indicate this. */
1731 if (wcache_server_down(domain)) {
1732 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1733 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1734 } else
1735 status = centry->status;
1737 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1738 domain->name, nt_errstr(status) ));
1740 centry_free(centry);
1741 return status;
1743 do_query:
1744 *num_entries = 0;
1745 *info = NULL;
1747 /* Return status value returned by seq number check */
1749 if (!NT_STATUS_IS_OK(domain->last_status))
1750 return domain->last_status;
1752 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1753 domain->name ));
1755 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1757 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1758 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1759 if (!domain->internal && old_status) {
1760 set_domain_offline(domain);
1762 if (cache->tdb &&
1763 !domain->internal &&
1764 !domain->online &&
1765 old_status) {
1766 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1767 if (centry) {
1768 goto do_fetch_cache;
1772 /* and save it */
1773 refresh_sequence_number(domain, false);
1774 if (!NT_STATUS_IS_OK(status)) {
1775 return status;
1777 centry = centry_start(domain, status);
1778 if (!centry)
1779 goto skip_save;
1780 centry_put_uint32(centry, *num_entries);
1781 for (i=0; i<(*num_entries); i++) {
1782 centry_put_string(centry, (*info)[i].acct_name);
1783 centry_put_string(centry, (*info)[i].acct_desc);
1784 centry_put_uint32(centry, (*info)[i].rid);
1786 centry_end(centry, "GL/%s/local", domain->name);
1787 centry_free(centry);
1789 skip_save:
1790 return status;
1793 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1794 const char *domain_name,
1795 const char *name,
1796 struct dom_sid *sid,
1797 enum lsa_SidType *type)
1799 struct winbind_cache *cache = get_cache(domain);
1800 struct cache_entry *centry;
1801 NTSTATUS status;
1802 char *uname;
1804 if (cache->tdb == NULL) {
1805 return NT_STATUS_NOT_FOUND;
1808 uname = talloc_strdup_upper(talloc_tos(), name);
1809 if (uname == NULL) {
1810 return NT_STATUS_NO_MEMORY;
1813 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1814 domain_name = domain->name;
1817 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1818 TALLOC_FREE(uname);
1819 if (centry == NULL) {
1820 return NT_STATUS_NOT_FOUND;
1823 status = centry->status;
1824 if (NT_STATUS_IS_OK(status)) {
1825 *type = (enum lsa_SidType)centry_uint32(centry);
1826 centry_sid(centry, sid);
1829 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1830 "%s\n", domain->name, nt_errstr(status) ));
1832 centry_free(centry);
1833 return status;
1836 /* convert a single name to a sid in a domain */
1837 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1838 TALLOC_CTX *mem_ctx,
1839 const char *domain_name,
1840 const char *name,
1841 uint32_t flags,
1842 struct dom_sid *sid,
1843 enum lsa_SidType *type)
1845 NTSTATUS status;
1846 bool old_status;
1848 old_status = domain->online;
1850 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1851 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1852 return status;
1855 ZERO_STRUCTP(sid);
1857 /* If the seq number check indicated that there is a problem
1858 * with this DC, then return that status... except for
1859 * access_denied. This is special because the dc may be in
1860 * "restrict anonymous = 1" mode, in which case it will deny
1861 * most unauthenticated operations, but *will* allow the LSA
1862 * name-to-sid that we try as a fallback. */
1864 if (!(NT_STATUS_IS_OK(domain->last_status)
1865 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1866 return domain->last_status;
1868 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1869 domain->name ));
1871 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1872 name, flags, sid, type);
1874 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1875 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1876 if (!domain->internal && old_status) {
1877 set_domain_offline(domain);
1879 if (!domain->internal &&
1880 !domain->online &&
1881 old_status) {
1882 NTSTATUS cache_status;
1883 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1884 return cache_status;
1887 /* and save it */
1888 refresh_sequence_number(domain, false);
1890 if (domain->online &&
1891 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1892 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1894 /* Only save the reverse mapping if this was not a UPN */
1895 if (!strchr(name, '@')) {
1896 if (!strupper_m(discard_const_p(char, domain_name))) {
1897 return NT_STATUS_INVALID_PARAMETER;
1899 (void)strlower_m(discard_const_p(char, name));
1900 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1904 return status;
1907 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1908 const struct dom_sid *sid,
1909 TALLOC_CTX *mem_ctx,
1910 char **domain_name,
1911 char **name,
1912 enum lsa_SidType *type)
1914 struct winbind_cache *cache = get_cache(domain);
1915 struct cache_entry *centry;
1916 char *sid_string;
1917 NTSTATUS status;
1919 if (cache->tdb == NULL) {
1920 return NT_STATUS_NOT_FOUND;
1923 sid_string = sid_string_tos(sid);
1924 if (sid_string == NULL) {
1925 return NT_STATUS_NO_MEMORY;
1928 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1929 TALLOC_FREE(sid_string);
1930 if (centry == NULL) {
1931 return NT_STATUS_NOT_FOUND;
1934 if (NT_STATUS_IS_OK(centry->status)) {
1935 *type = (enum lsa_SidType)centry_uint32(centry);
1936 *domain_name = centry_string(centry, mem_ctx);
1937 *name = centry_string(centry, mem_ctx);
1940 status = centry->status;
1941 centry_free(centry);
1943 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1944 "%s\n", domain->name, nt_errstr(status) ));
1946 return status;
1949 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1950 given */
1951 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1952 TALLOC_CTX *mem_ctx,
1953 const struct dom_sid *sid,
1954 char **domain_name,
1955 char **name,
1956 enum lsa_SidType *type)
1958 NTSTATUS status;
1959 bool old_status;
1961 old_status = domain->online;
1962 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1963 type);
1964 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1965 return status;
1968 *name = NULL;
1969 *domain_name = NULL;
1971 /* If the seq number check indicated that there is a problem
1972 * with this DC, then return that status... except for
1973 * access_denied. This is special because the dc may be in
1974 * "restrict anonymous = 1" mode, in which case it will deny
1975 * most unauthenticated operations, but *will* allow the LSA
1976 * sid-to-name that we try as a fallback. */
1978 if (!(NT_STATUS_IS_OK(domain->last_status)
1979 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1980 return domain->last_status;
1982 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1983 domain->name ));
1985 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1987 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1988 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1989 if (!domain->internal && old_status) {
1990 set_domain_offline(domain);
1992 if (!domain->internal &&
1993 !domain->online &&
1994 old_status) {
1995 NTSTATUS cache_status;
1996 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1997 domain_name, name, type);
1998 return cache_status;
2001 /* and save it */
2002 refresh_sequence_number(domain, false);
2003 if (!NT_STATUS_IS_OK(status)) {
2004 return status;
2006 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2008 /* We can't save the name to sid mapping here, as with sid history a
2009 * later name2sid would give the wrong sid. */
2011 return status;
2014 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2015 TALLOC_CTX *mem_ctx,
2016 const struct dom_sid *domain_sid,
2017 uint32 *rids,
2018 size_t num_rids,
2019 char **domain_name,
2020 char ***names,
2021 enum lsa_SidType **types)
2023 struct winbind_cache *cache = get_cache(domain);
2024 size_t i;
2025 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2026 bool have_mapped;
2027 bool have_unmapped;
2028 bool old_status;
2030 old_status = domain->online;
2031 *domain_name = NULL;
2032 *names = NULL;
2033 *types = NULL;
2035 if (!cache->tdb) {
2036 goto do_query;
2039 if (num_rids == 0) {
2040 return NT_STATUS_OK;
2043 *names = talloc_array(mem_ctx, char *, num_rids);
2044 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2046 if ((*names == NULL) || (*types == NULL)) {
2047 result = NT_STATUS_NO_MEMORY;
2048 goto error;
2051 have_mapped = have_unmapped = false;
2053 for (i=0; i<num_rids; i++) {
2054 struct dom_sid sid;
2055 struct cache_entry *centry;
2056 fstring tmp;
2058 if (!sid_compose(&sid, domain_sid, rids[i])) {
2059 result = NT_STATUS_INTERNAL_ERROR;
2060 goto error;
2063 centry = wcache_fetch(cache, domain, "SN/%s",
2064 sid_to_fstring(tmp, &sid));
2065 if (!centry) {
2066 goto do_query;
2069 (*types)[i] = SID_NAME_UNKNOWN;
2070 (*names)[i] = talloc_strdup(*names, "");
2072 if (NT_STATUS_IS_OK(centry->status)) {
2073 char *dom;
2074 have_mapped = true;
2075 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2077 dom = centry_string(centry, mem_ctx);
2078 if (*domain_name == NULL) {
2079 *domain_name = dom;
2080 } else {
2081 talloc_free(dom);
2084 (*names)[i] = centry_string(centry, *names);
2086 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2087 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2088 have_unmapped = true;
2090 } else {
2091 /* something's definitely wrong */
2092 result = centry->status;
2093 goto error;
2096 centry_free(centry);
2099 if (!have_mapped) {
2100 return NT_STATUS_NONE_MAPPED;
2102 if (!have_unmapped) {
2103 return NT_STATUS_OK;
2105 return STATUS_SOME_UNMAPPED;
2107 do_query:
2109 TALLOC_FREE(*names);
2110 TALLOC_FREE(*types);
2112 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2113 rids, num_rids, domain_name,
2114 names, types);
2116 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2117 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2118 if (!domain->internal && old_status) {
2119 set_domain_offline(domain);
2121 if (cache->tdb &&
2122 !domain->internal &&
2123 !domain->online &&
2124 old_status) {
2125 have_mapped = have_unmapped = false;
2127 *names = talloc_array(mem_ctx, char *, num_rids);
2128 if (*names != NULL) {
2129 result = NT_STATUS_NO_MEMORY;
2130 goto error;
2133 *types = talloc_array(mem_ctx, enum lsa_SidType,
2134 num_rids);
2135 if (*types != NULL) {
2136 result = NT_STATUS_NO_MEMORY;
2137 goto error;
2140 for (i=0; i<num_rids; i++) {
2141 struct dom_sid sid;
2142 struct cache_entry *centry;
2143 fstring tmp;
2145 if (!sid_compose(&sid, domain_sid, rids[i])) {
2146 result = NT_STATUS_INTERNAL_ERROR;
2147 goto error;
2150 centry = wcache_fetch(cache, domain, "SN/%s",
2151 sid_to_fstring(tmp, &sid));
2152 if (!centry) {
2153 (*types)[i] = SID_NAME_UNKNOWN;
2154 (*names)[i] = talloc_strdup(*names, "");
2155 continue;
2158 (*types)[i] = SID_NAME_UNKNOWN;
2159 (*names)[i] = talloc_strdup(*names, "");
2161 if (NT_STATUS_IS_OK(centry->status)) {
2162 char *dom;
2163 have_mapped = true;
2164 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2166 dom = centry_string(centry, mem_ctx);
2167 if (*domain_name == NULL) {
2168 *domain_name = dom;
2169 } else {
2170 talloc_free(dom);
2173 (*names)[i] = centry_string(centry, *names);
2175 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2176 have_unmapped = true;
2178 } else {
2179 /* something's definitely wrong */
2180 result = centry->status;
2181 centry_free(centry);
2182 goto error;
2185 centry_free(centry);
2188 if (!have_mapped) {
2189 return NT_STATUS_NONE_MAPPED;
2191 if (!have_unmapped) {
2192 return NT_STATUS_OK;
2194 return STATUS_SOME_UNMAPPED;
2198 None of the queried rids has been found so save all negative entries
2200 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2201 for (i = 0; i < num_rids; i++) {
2202 struct dom_sid sid;
2203 const char *name = "";
2204 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2205 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2207 if (!sid_compose(&sid, domain_sid, rids[i])) {
2208 return NT_STATUS_INTERNAL_ERROR;
2211 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2212 name, type);
2215 return result;
2219 Some or all of the queried rids have been found.
2221 if (!NT_STATUS_IS_OK(result) &&
2222 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2223 return result;
2226 refresh_sequence_number(domain, false);
2228 for (i=0; i<num_rids; i++) {
2229 struct dom_sid sid;
2230 NTSTATUS status;
2232 if (!sid_compose(&sid, domain_sid, rids[i])) {
2233 result = NT_STATUS_INTERNAL_ERROR;
2234 goto error;
2237 status = (*types)[i] == SID_NAME_UNKNOWN ?
2238 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2240 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2241 (*names)[i], (*types)[i]);
2244 return result;
2246 error:
2247 TALLOC_FREE(*names);
2248 TALLOC_FREE(*types);
2249 return result;
2252 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2253 TALLOC_CTX *mem_ctx,
2254 const struct dom_sid *user_sid,
2255 struct wbint_userinfo *info)
2257 struct winbind_cache *cache = get_cache(domain);
2258 struct cache_entry *centry = NULL;
2259 NTSTATUS status;
2260 char *sid_string;
2262 if (cache->tdb == NULL) {
2263 return NT_STATUS_NOT_FOUND;
2266 sid_string = sid_string_tos(user_sid);
2267 if (sid_string == NULL) {
2268 return NT_STATUS_NO_MEMORY;
2271 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2272 TALLOC_FREE(sid_string);
2273 if (centry == NULL) {
2274 return NT_STATUS_NOT_FOUND;
2278 * If we have an access denied cache entry and a cached info3
2279 * in the samlogon cache then do a query. This will force the
2280 * rpc back end to return the info3 data.
2283 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2284 netsamlogon_cache_have(user_sid)) {
2285 DEBUG(10, ("query_user: cached access denied and have cached "
2286 "info3\n"));
2287 domain->last_status = NT_STATUS_OK;
2288 centry_free(centry);
2289 return NT_STATUS_NOT_FOUND;
2292 /* if status is not ok then this is a negative hit
2293 and the rest of the data doesn't matter */
2294 status = centry->status;
2295 if (NT_STATUS_IS_OK(status)) {
2296 info->acct_name = centry_string(centry, mem_ctx);
2297 info->full_name = centry_string(centry, mem_ctx);
2298 info->homedir = centry_string(centry, mem_ctx);
2299 info->shell = centry_string(centry, mem_ctx);
2300 info->primary_gid = centry_uint32(centry);
2301 centry_sid(centry, &info->user_sid);
2302 centry_sid(centry, &info->group_sid);
2305 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2306 "%s\n", domain->name, nt_errstr(status) ));
2308 centry_free(centry);
2309 return status;
2312 /* Lookup user information from a rid */
2313 static NTSTATUS query_user(struct winbindd_domain *domain,
2314 TALLOC_CTX *mem_ctx,
2315 const struct dom_sid *user_sid,
2316 struct wbint_userinfo *info)
2318 NTSTATUS status;
2319 bool old_status;
2321 old_status = domain->online;
2322 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2323 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2324 return status;
2327 ZERO_STRUCTP(info);
2329 /* Return status value returned by seq number check */
2331 if (!NT_STATUS_IS_OK(domain->last_status))
2332 return domain->last_status;
2334 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2335 domain->name ));
2337 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2339 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2340 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2341 if (!domain->internal && old_status) {
2342 set_domain_offline(domain);
2344 if (!domain->internal &&
2345 !domain->online &&
2346 old_status) {
2347 NTSTATUS cache_status;
2348 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2349 return cache_status;
2352 /* and save it */
2353 refresh_sequence_number(domain, false);
2354 if (!NT_STATUS_IS_OK(status)) {
2355 return status;
2357 wcache_save_user(domain, status, info);
2359 return status;
2362 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2363 TALLOC_CTX *mem_ctx,
2364 const struct dom_sid *user_sid,
2365 uint32_t *pnum_sids,
2366 struct dom_sid **psids)
2368 struct winbind_cache *cache = get_cache(domain);
2369 struct cache_entry *centry = NULL;
2370 NTSTATUS status;
2371 uint32_t i, num_sids;
2372 struct dom_sid *sids;
2373 fstring sid_string;
2375 if (cache->tdb == NULL) {
2376 return NT_STATUS_NOT_FOUND;
2379 centry = wcache_fetch(cache, domain, "UG/%s",
2380 sid_to_fstring(sid_string, user_sid));
2381 if (centry == NULL) {
2382 return NT_STATUS_NOT_FOUND;
2385 /* If we have an access denied cache entry and a cached info3 in the
2386 samlogon cache then do a query. This will force the rpc back end
2387 to return the info3 data. */
2389 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2390 && netsamlogon_cache_have(user_sid)) {
2391 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2392 "cached info3\n"));
2393 domain->last_status = NT_STATUS_OK;
2394 centry_free(centry);
2395 return NT_STATUS_NOT_FOUND;
2398 num_sids = centry_uint32(centry);
2399 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2400 if (sids == NULL) {
2401 centry_free(centry);
2402 return NT_STATUS_NO_MEMORY;
2405 for (i=0; i<num_sids; i++) {
2406 centry_sid(centry, &sids[i]);
2409 status = centry->status;
2411 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2412 "status: %s\n", domain->name, nt_errstr(status)));
2414 centry_free(centry);
2416 *pnum_sids = num_sids;
2417 *psids = sids;
2418 return status;
2421 /* Lookup groups a user is a member of. */
2422 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2423 TALLOC_CTX *mem_ctx,
2424 const struct dom_sid *user_sid,
2425 uint32 *num_groups, struct dom_sid **user_gids)
2427 struct cache_entry *centry = NULL;
2428 NTSTATUS status;
2429 unsigned int i;
2430 fstring sid_string;
2431 bool old_status;
2433 old_status = domain->online;
2434 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2435 num_groups, user_gids);
2436 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2437 return status;
2440 (*num_groups) = 0;
2441 (*user_gids) = NULL;
2443 /* Return status value returned by seq number check */
2445 if (!NT_STATUS_IS_OK(domain->last_status))
2446 return domain->last_status;
2448 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2449 domain->name ));
2451 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2453 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2454 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2455 if (!domain->internal && old_status) {
2456 set_domain_offline(domain);
2458 if (!domain->internal &&
2459 !domain->online &&
2460 old_status) {
2461 NTSTATUS cache_status;
2462 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2463 num_groups, user_gids);
2464 return cache_status;
2467 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2468 goto skip_save;
2470 /* and save it */
2471 refresh_sequence_number(domain, false);
2472 if (!NT_STATUS_IS_OK(status)) {
2473 return status;
2475 centry = centry_start(domain, status);
2476 if (!centry)
2477 goto skip_save;
2479 centry_put_uint32(centry, *num_groups);
2480 for (i=0; i<(*num_groups); i++) {
2481 centry_put_sid(centry, &(*user_gids)[i]);
2484 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2485 centry_free(centry);
2487 skip_save:
2488 return status;
2491 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2492 const struct dom_sid *sids)
2494 uint32_t i;
2495 char *sidlist;
2497 sidlist = talloc_strdup(mem_ctx, "");
2498 if (sidlist == NULL) {
2499 return NULL;
2501 for (i=0; i<num_sids; i++) {
2502 fstring tmp;
2503 sidlist = talloc_asprintf_append_buffer(
2504 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2505 if (sidlist == NULL) {
2506 return NULL;
2509 return sidlist;
2512 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2513 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2514 const struct dom_sid *sids,
2515 uint32_t *pnum_aliases, uint32_t **paliases)
2517 struct winbind_cache *cache = get_cache(domain);
2518 struct cache_entry *centry = NULL;
2519 uint32_t num_aliases;
2520 uint32_t *aliases;
2521 NTSTATUS status;
2522 char *sidlist;
2523 int i;
2525 if (cache->tdb == NULL) {
2526 return NT_STATUS_NOT_FOUND;
2529 if (num_sids == 0) {
2530 *pnum_aliases = 0;
2531 *paliases = NULL;
2532 return NT_STATUS_OK;
2535 /* We need to cache indexed by the whole list of SIDs, the aliases
2536 * resulting might come from any of the SIDs. */
2538 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2539 if (sidlist == NULL) {
2540 return NT_STATUS_NO_MEMORY;
2543 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2544 TALLOC_FREE(sidlist);
2545 if (centry == NULL) {
2546 return NT_STATUS_NOT_FOUND;
2549 num_aliases = centry_uint32(centry);
2550 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2551 if (aliases == NULL) {
2552 centry_free(centry);
2553 return NT_STATUS_NO_MEMORY;
2556 for (i=0; i<num_aliases; i++) {
2557 aliases[i] = centry_uint32(centry);
2560 status = centry->status;
2562 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2563 "status %s\n", domain->name, nt_errstr(status)));
2565 centry_free(centry);
2567 *pnum_aliases = num_aliases;
2568 *paliases = aliases;
2570 return status;
2573 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2574 TALLOC_CTX *mem_ctx,
2575 uint32 num_sids, const struct dom_sid *sids,
2576 uint32 *num_aliases, uint32 **alias_rids)
2578 struct cache_entry *centry = NULL;
2579 NTSTATUS status;
2580 char *sidlist;
2581 int i;
2582 bool old_status;
2584 old_status = domain->online;
2585 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2586 num_aliases, alias_rids);
2587 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2588 return status;
2591 (*num_aliases) = 0;
2592 (*alias_rids) = NULL;
2594 if (!NT_STATUS_IS_OK(domain->last_status))
2595 return domain->last_status;
2597 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2598 "for domain %s\n", domain->name ));
2600 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2601 if (sidlist == NULL) {
2602 return NT_STATUS_NO_MEMORY;
2605 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2606 num_sids, sids,
2607 num_aliases, alias_rids);
2609 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2610 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2611 if (!domain->internal && old_status) {
2612 set_domain_offline(domain);
2614 if (!domain->internal &&
2615 !domain->online &&
2616 old_status) {
2617 NTSTATUS cache_status;
2618 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2619 sids, num_aliases, alias_rids);
2620 return cache_status;
2623 /* and save it */
2624 refresh_sequence_number(domain, false);
2625 if (!NT_STATUS_IS_OK(status)) {
2626 return status;
2628 centry = centry_start(domain, status);
2629 if (!centry)
2630 goto skip_save;
2631 centry_put_uint32(centry, *num_aliases);
2632 for (i=0; i<(*num_aliases); i++)
2633 centry_put_uint32(centry, (*alias_rids)[i]);
2634 centry_end(centry, "UA%s", sidlist);
2635 centry_free(centry);
2637 skip_save:
2638 return status;
2641 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2642 TALLOC_CTX *mem_ctx,
2643 const struct dom_sid *group_sid,
2644 uint32_t *num_names,
2645 struct dom_sid **sid_mem, char ***names,
2646 uint32_t **name_types)
2648 struct winbind_cache *cache = get_cache(domain);
2649 struct cache_entry *centry = NULL;
2650 NTSTATUS status;
2651 unsigned int i;
2652 char *sid_string;
2654 if (cache->tdb == NULL) {
2655 return NT_STATUS_NOT_FOUND;
2658 sid_string = sid_string_tos(group_sid);
2659 if (sid_string == NULL) {
2660 return NT_STATUS_NO_MEMORY;
2663 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2664 TALLOC_FREE(sid_string);
2665 if (centry == NULL) {
2666 return NT_STATUS_NOT_FOUND;
2669 *sid_mem = NULL;
2670 *names = NULL;
2671 *name_types = NULL;
2673 *num_names = centry_uint32(centry);
2674 if (*num_names == 0) {
2675 centry_free(centry);
2676 return NT_STATUS_OK;
2679 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2680 *names = talloc_array(mem_ctx, char *, *num_names);
2681 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2683 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2684 TALLOC_FREE(*sid_mem);
2685 TALLOC_FREE(*names);
2686 TALLOC_FREE(*name_types);
2687 centry_free(centry);
2688 return NT_STATUS_NO_MEMORY;
2691 for (i=0; i<(*num_names); i++) {
2692 centry_sid(centry, &(*sid_mem)[i]);
2693 (*names)[i] = centry_string(centry, mem_ctx);
2694 (*name_types)[i] = centry_uint32(centry);
2697 status = centry->status;
2699 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2700 "status: %s\n", domain->name, nt_errstr(status)));
2702 centry_free(centry);
2703 return status;
2706 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2707 TALLOC_CTX *mem_ctx,
2708 const struct dom_sid *group_sid,
2709 enum lsa_SidType type,
2710 uint32 *num_names,
2711 struct dom_sid **sid_mem, char ***names,
2712 uint32 **name_types)
2714 struct cache_entry *centry = NULL;
2715 NTSTATUS status;
2716 unsigned int i;
2717 fstring sid_string;
2718 bool old_status;
2720 old_status = domain->online;
2721 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2722 sid_mem, names, name_types);
2723 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2724 return status;
2727 (*num_names) = 0;
2728 (*sid_mem) = NULL;
2729 (*names) = NULL;
2730 (*name_types) = NULL;
2732 /* Return status value returned by seq number check */
2734 if (!NT_STATUS_IS_OK(domain->last_status))
2735 return domain->last_status;
2737 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2738 domain->name ));
2740 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2741 type, num_names,
2742 sid_mem, names, name_types);
2744 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2745 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2746 if (!domain->internal && old_status) {
2747 set_domain_offline(domain);
2749 if (!domain->internal &&
2750 !domain->online &&
2751 old_status) {
2752 NTSTATUS cache_status;
2753 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2754 num_names, sid_mem, names,
2755 name_types);
2756 return cache_status;
2759 /* and save it */
2760 refresh_sequence_number(domain, false);
2761 if (!NT_STATUS_IS_OK(status)) {
2762 return status;
2764 centry = centry_start(domain, status);
2765 if (!centry)
2766 goto skip_save;
2767 centry_put_uint32(centry, *num_names);
2768 for (i=0; i<(*num_names); i++) {
2769 centry_put_sid(centry, &(*sid_mem)[i]);
2770 centry_put_string(centry, (*names)[i]);
2771 centry_put_uint32(centry, (*name_types)[i]);
2773 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2774 centry_free(centry);
2776 skip_save:
2777 return status;
2780 /* find the sequence number for a domain */
2781 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2783 refresh_sequence_number(domain, false);
2785 *seq = domain->sequence_number;
2787 return NT_STATUS_OK;
2790 /* enumerate trusted domains
2791 * (we need to have the list of trustdoms in the cache when we go offline) -
2792 * Guenther */
2793 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2794 TALLOC_CTX *mem_ctx,
2795 struct netr_DomainTrustList *trusts)
2797 NTSTATUS status;
2798 struct winbind_cache *cache;
2799 struct winbindd_tdc_domain *dom_list = NULL;
2800 size_t num_domains = 0;
2801 bool retval = false;
2802 int i;
2803 bool old_status;
2805 old_status = domain->online;
2806 trusts->count = 0;
2807 trusts->array = NULL;
2809 cache = get_cache(domain);
2810 if (!cache || !cache->tdb) {
2811 goto do_query;
2814 if (domain->online) {
2815 goto do_query;
2818 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2819 if (!retval || !num_domains || !dom_list) {
2820 TALLOC_FREE(dom_list);
2821 goto do_query;
2824 do_fetch_cache:
2825 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2826 if (!trusts->array) {
2827 TALLOC_FREE(dom_list);
2828 return NT_STATUS_NO_MEMORY;
2831 for (i = 0; i < num_domains; i++) {
2832 struct netr_DomainTrust *trust;
2833 struct dom_sid *sid;
2834 struct winbindd_domain *dom;
2836 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2837 if (dom && dom->internal) {
2838 continue;
2841 trust = &trusts->array[trusts->count];
2842 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2843 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2844 sid = talloc(trusts->array, struct dom_sid);
2845 if (!trust->netbios_name || !trust->dns_name ||
2846 !sid) {
2847 TALLOC_FREE(dom_list);
2848 TALLOC_FREE(trusts->array);
2849 return NT_STATUS_NO_MEMORY;
2852 trust->trust_flags = dom_list[i].trust_flags;
2853 trust->trust_attributes = dom_list[i].trust_attribs;
2854 trust->trust_type = dom_list[i].trust_type;
2855 sid_copy(sid, &dom_list[i].sid);
2856 trust->sid = sid;
2857 trusts->count++;
2860 TALLOC_FREE(dom_list);
2861 return NT_STATUS_OK;
2863 do_query:
2864 /* Return status value returned by seq number check */
2866 if (!NT_STATUS_IS_OK(domain->last_status))
2867 return domain->last_status;
2869 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2870 domain->name ));
2872 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2874 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2875 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2876 if (!domain->internal && old_status) {
2877 set_domain_offline(domain);
2879 if (!domain->internal &&
2880 !domain->online &&
2881 old_status) {
2882 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2883 if (retval && num_domains && dom_list) {
2884 TALLOC_FREE(trusts->array);
2885 trusts->count = 0;
2886 goto do_fetch_cache;
2890 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2891 * so that the generic centry handling still applies correctly -
2892 * Guenther*/
2894 if (!NT_STATUS_IS_ERR(status)) {
2895 status = NT_STATUS_OK;
2897 return status;
2900 /* get lockout policy */
2901 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2902 TALLOC_CTX *mem_ctx,
2903 struct samr_DomInfo12 *policy)
2905 struct winbind_cache *cache = get_cache(domain);
2906 struct cache_entry *centry = NULL;
2907 NTSTATUS status;
2908 bool old_status;
2910 old_status = domain->online;
2911 if (!cache->tdb)
2912 goto do_query;
2914 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2916 if (!centry)
2917 goto do_query;
2919 do_fetch_cache:
2920 policy->lockout_duration = centry_nttime(centry);
2921 policy->lockout_window = centry_nttime(centry);
2922 policy->lockout_threshold = centry_uint16(centry);
2924 status = centry->status;
2926 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2927 domain->name, nt_errstr(status) ));
2929 centry_free(centry);
2930 return status;
2932 do_query:
2933 ZERO_STRUCTP(policy);
2935 /* Return status value returned by seq number check */
2937 if (!NT_STATUS_IS_OK(domain->last_status))
2938 return domain->last_status;
2940 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2941 domain->name ));
2943 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2945 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2946 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2947 if (!domain->internal && old_status) {
2948 set_domain_offline(domain);
2950 if (cache->tdb &&
2951 !domain->internal &&
2952 !domain->online &&
2953 old_status) {
2954 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2955 if (centry) {
2956 goto do_fetch_cache;
2960 /* and save it */
2961 refresh_sequence_number(domain, false);
2962 if (!NT_STATUS_IS_OK(status)) {
2963 return status;
2965 wcache_save_lockout_policy(domain, status, policy);
2967 return status;
2970 /* get password policy */
2971 static NTSTATUS password_policy(struct winbindd_domain *domain,
2972 TALLOC_CTX *mem_ctx,
2973 struct samr_DomInfo1 *policy)
2975 struct winbind_cache *cache = get_cache(domain);
2976 struct cache_entry *centry = NULL;
2977 NTSTATUS status;
2978 bool old_status;
2980 old_status = domain->online;
2981 if (!cache->tdb)
2982 goto do_query;
2984 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2986 if (!centry)
2987 goto do_query;
2989 do_fetch_cache:
2990 policy->min_password_length = centry_uint16(centry);
2991 policy->password_history_length = centry_uint16(centry);
2992 policy->password_properties = centry_uint32(centry);
2993 policy->max_password_age = centry_nttime(centry);
2994 policy->min_password_age = centry_nttime(centry);
2996 status = centry->status;
2998 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2999 domain->name, nt_errstr(status) ));
3001 centry_free(centry);
3002 return status;
3004 do_query:
3005 ZERO_STRUCTP(policy);
3007 /* Return status value returned by seq number check */
3009 if (!NT_STATUS_IS_OK(domain->last_status))
3010 return domain->last_status;
3012 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3013 domain->name ));
3015 status = domain->backend->password_policy(domain, mem_ctx, policy);
3017 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3018 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3019 if (!domain->internal && old_status) {
3020 set_domain_offline(domain);
3022 if (cache->tdb &&
3023 !domain->internal &&
3024 !domain->online &&
3025 old_status) {
3026 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3027 if (centry) {
3028 goto do_fetch_cache;
3032 /* and save it */
3033 refresh_sequence_number(domain, false);
3034 if (!NT_STATUS_IS_OK(status)) {
3035 return status;
3037 wcache_save_password_policy(domain, status, policy);
3039 return status;
3043 /* Invalidate cached user and group lists coherently */
3045 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3046 void *state)
3048 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3049 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3050 tdb_delete(the_tdb, kbuf);
3052 return 0;
3055 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3057 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3058 const struct dom_sid *sid)
3060 fstring key_str, sid_string;
3061 struct winbind_cache *cache;
3063 /* dont clear cached U/SID and UG/SID entries when we want to logon
3064 * offline - gd */
3066 if (lp_winbind_offline_logon()) {
3067 return;
3070 if (!domain)
3071 return;
3073 cache = get_cache(domain);
3075 if (!cache->tdb) {
3076 return;
3079 /* Clear U/SID cache entry */
3080 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3081 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3082 tdb_delete(cache->tdb, string_tdb_data(key_str));
3084 /* Clear UG/SID cache entry */
3085 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3086 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3087 tdb_delete(cache->tdb, string_tdb_data(key_str));
3089 /* Samba/winbindd never needs this. */
3090 netsamlogon_clear_cached_user(sid);
3093 bool wcache_invalidate_cache(void)
3095 struct winbindd_domain *domain;
3097 for (domain = domain_list(); domain; domain = domain->next) {
3098 struct winbind_cache *cache = get_cache(domain);
3100 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3101 "entries for %s\n", domain->name));
3102 if (cache) {
3103 if (cache->tdb) {
3104 tdb_traverse(cache->tdb, traverse_fn, NULL);
3105 } else {
3106 return false;
3110 return true;
3113 bool wcache_invalidate_cache_noinit(void)
3115 struct winbindd_domain *domain;
3117 for (domain = domain_list(); domain; domain = domain->next) {
3118 struct winbind_cache *cache;
3120 /* Skip uninitialized domains. */
3121 if (!domain->initialized && !domain->internal) {
3122 continue;
3125 cache = get_cache(domain);
3127 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3128 "entries for %s\n", domain->name));
3129 if (cache) {
3130 if (cache->tdb) {
3131 tdb_traverse(cache->tdb, traverse_fn, NULL);
3133 * Flushing cache has nothing to with domains.
3134 * return here if we successfully flushed once.
3135 * To avoid unnecessary traversing the cache.
3137 return true;
3138 } else {
3139 return false;
3143 return true;
3146 bool init_wcache(void)
3148 if (wcache == NULL) {
3149 wcache = SMB_XMALLOC_P(struct winbind_cache);
3150 ZERO_STRUCTP(wcache);
3153 if (wcache->tdb != NULL)
3154 return true;
3156 /* when working offline we must not clear the cache on restart */
3157 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3158 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3159 TDB_INCOMPATIBLE_HASH |
3160 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3161 O_RDWR|O_CREAT, 0600);
3163 if (wcache->tdb == NULL) {
3164 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3165 return false;
3168 return true;
3171 /************************************************************************
3172 This is called by the parent to initialize the cache file.
3173 We don't need sophisticated locking here as we know we're the
3174 only opener.
3175 ************************************************************************/
3177 bool initialize_winbindd_cache(void)
3179 bool cache_bad = true;
3180 uint32 vers;
3182 if (!init_wcache()) {
3183 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3184 return false;
3187 /* Check version number. */
3188 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3189 vers == WINBINDD_CACHE_VERSION) {
3190 cache_bad = false;
3193 if (cache_bad) {
3194 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3195 "and re-creating with version number %d\n",
3196 WINBINDD_CACHE_VERSION ));
3198 tdb_close(wcache->tdb);
3199 wcache->tdb = NULL;
3201 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3202 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3203 state_path("winbindd_cache.tdb"),
3204 strerror(errno) ));
3205 return false;
3207 if (!init_wcache()) {
3208 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3209 "init_wcache failed.\n"));
3210 return false;
3213 /* Write the version. */
3214 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3215 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3216 tdb_errorstr_compat(wcache->tdb) ));
3217 return false;
3221 tdb_close(wcache->tdb);
3222 wcache->tdb = NULL;
3223 return true;
3226 void close_winbindd_cache(void)
3228 if (!wcache) {
3229 return;
3231 if (wcache->tdb) {
3232 tdb_close(wcache->tdb);
3233 wcache->tdb = NULL;
3237 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3238 char **domain_name, char **name,
3239 enum lsa_SidType *type)
3241 struct winbindd_domain *domain;
3242 NTSTATUS status;
3244 domain = find_lookup_domain_from_sid(sid);
3245 if (domain == NULL) {
3246 return false;
3248 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3249 type);
3250 return NT_STATUS_IS_OK(status);
3253 bool lookup_cached_name(const char *domain_name,
3254 const char *name,
3255 struct dom_sid *sid,
3256 enum lsa_SidType *type)
3258 struct winbindd_domain *domain;
3259 NTSTATUS status;
3260 bool original_online_state;
3262 domain = find_lookup_domain_from_name(domain_name);
3263 if (domain == NULL) {
3264 return false;
3267 /* If we are doing a cached logon, temporarily set the domain
3268 offline so the cache won't expire the entry */
3270 original_online_state = domain->online;
3271 domain->online = false;
3272 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3273 domain->online = original_online_state;
3275 return NT_STATUS_IS_OK(status);
3278 void cache_name2sid(struct winbindd_domain *domain,
3279 const char *domain_name, const char *name,
3280 enum lsa_SidType type, const struct dom_sid *sid)
3282 refresh_sequence_number(domain, false);
3283 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3284 sid, type);
3288 * The original idea that this cache only contains centries has
3289 * been blurred - now other stuff gets put in here. Ensure we
3290 * ignore these things on cleanup.
3293 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3294 TDB_DATA dbuf, void *state)
3296 struct cache_entry *centry;
3298 if (is_non_centry_key(kbuf)) {
3299 return 0;
3302 centry = wcache_fetch_raw((char *)kbuf.dptr);
3303 if (!centry) {
3304 return 0;
3307 if (!NT_STATUS_IS_OK(centry->status)) {
3308 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3309 tdb_delete(the_tdb, kbuf);
3312 centry_free(centry);
3313 return 0;
3316 /* flush the cache */
3317 void wcache_flush_cache(void)
3319 if (!wcache)
3320 return;
3321 if (wcache->tdb) {
3322 tdb_close(wcache->tdb);
3323 wcache->tdb = NULL;
3325 if (!winbindd_use_cache()) {
3326 return;
3329 /* when working offline we must not clear the cache on restart */
3330 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3331 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3332 TDB_INCOMPATIBLE_HASH |
3333 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3334 O_RDWR|O_CREAT, 0600);
3336 if (!wcache->tdb) {
3337 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3338 return;
3341 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3343 DEBUG(10,("wcache_flush_cache success\n"));
3346 /* Count cached creds */
3348 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3349 void *state)
3351 int *cred_count = (int*)state;
3353 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3354 (*cred_count)++;
3356 return 0;
3359 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3361 struct winbind_cache *cache = get_cache(domain);
3363 *count = 0;
3365 if (!cache->tdb) {
3366 return NT_STATUS_INTERNAL_DB_ERROR;
3369 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3371 return NT_STATUS_OK;
3374 struct cred_list {
3375 struct cred_list *prev, *next;
3376 TDB_DATA key;
3377 fstring name;
3378 time_t created;
3380 static struct cred_list *wcache_cred_list;
3382 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3383 void *state)
3385 struct cred_list *cred;
3387 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3389 cred = SMB_MALLOC_P(struct cred_list);
3390 if (cred == NULL) {
3391 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3392 return -1;
3395 ZERO_STRUCTP(cred);
3397 /* save a copy of the key */
3399 fstrcpy(cred->name, (const char *)kbuf.dptr);
3400 DLIST_ADD(wcache_cred_list, cred);
3403 return 0;
3406 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3408 struct winbind_cache *cache = get_cache(domain);
3409 NTSTATUS status;
3410 int ret;
3411 struct cred_list *cred, *oldest = NULL;
3413 if (!cache->tdb) {
3414 return NT_STATUS_INTERNAL_DB_ERROR;
3417 /* we possibly already have an entry */
3418 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3420 fstring key_str, tmp;
3422 DEBUG(11,("we already have an entry, deleting that\n"));
3424 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3426 tdb_delete(cache->tdb, string_tdb_data(key_str));
3428 return NT_STATUS_OK;
3431 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3432 if (ret == 0) {
3433 return NT_STATUS_OK;
3434 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3435 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3438 ZERO_STRUCTP(oldest);
3440 for (cred = wcache_cred_list; cred; cred = cred->next) {
3442 TDB_DATA data;
3443 time_t t;
3445 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3446 if (!data.dptr) {
3447 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3448 cred->name));
3449 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3450 goto done;
3453 t = IVAL(data.dptr, 0);
3454 SAFE_FREE(data.dptr);
3456 if (!oldest) {
3457 oldest = SMB_MALLOC_P(struct cred_list);
3458 if (oldest == NULL) {
3459 status = NT_STATUS_NO_MEMORY;
3460 goto done;
3463 fstrcpy(oldest->name, cred->name);
3464 oldest->created = t;
3465 continue;
3468 if (t < oldest->created) {
3469 fstrcpy(oldest->name, cred->name);
3470 oldest->created = t;
3474 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3475 status = NT_STATUS_OK;
3476 } else {
3477 status = NT_STATUS_UNSUCCESSFUL;
3479 done:
3480 SAFE_FREE(wcache_cred_list);
3481 SAFE_FREE(oldest);
3483 return status;
3486 /* Change the global online/offline state. */
3487 bool set_global_winbindd_state_offline(void)
3489 TDB_DATA data;
3491 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3493 /* Only go offline if someone has created
3494 the key "WINBINDD_OFFLINE" in the cache tdb. */
3496 if (wcache == NULL || wcache->tdb == NULL) {
3497 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3498 return false;
3501 if (!lp_winbind_offline_logon()) {
3502 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3503 return false;
3506 if (global_winbindd_offline_state) {
3507 /* Already offline. */
3508 return true;
3511 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3513 if (!data.dptr || data.dsize != 4) {
3514 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3515 SAFE_FREE(data.dptr);
3516 return false;
3517 } else {
3518 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3519 global_winbindd_offline_state = true;
3520 SAFE_FREE(data.dptr);
3521 return true;
3525 void set_global_winbindd_state_online(void)
3527 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3529 if (!lp_winbind_offline_logon()) {
3530 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3531 return;
3534 if (!global_winbindd_offline_state) {
3535 /* Already online. */
3536 return;
3538 global_winbindd_offline_state = false;
3540 if (!wcache->tdb) {
3541 return;
3544 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3545 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3548 bool get_global_winbindd_state_offline(void)
3550 return global_winbindd_offline_state;
3553 /***********************************************************************
3554 Validate functions for all possible cache tdb keys.
3555 ***********************************************************************/
3557 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3558 struct tdb_validation_status *state)
3560 struct cache_entry *centry;
3562 centry = SMB_XMALLOC_P(struct cache_entry);
3563 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3564 if (!centry->data) {
3565 SAFE_FREE(centry);
3566 return NULL;
3568 centry->len = data.dsize;
3569 centry->ofs = 0;
3571 if (centry->len < 16) {
3572 /* huh? corrupt cache? */
3573 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3574 "(len < 16) ?\n", kstr));
3575 centry_free(centry);
3576 state->bad_entry = true;
3577 state->success = false;
3578 return NULL;
3581 centry->status = NT_STATUS(centry_uint32(centry));
3582 centry->sequence_number = centry_uint32(centry);
3583 centry->timeout = centry_uint64_t(centry);
3584 return centry;
3587 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3588 struct tdb_validation_status *state)
3590 if (dbuf.dsize != 8) {
3591 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3592 keystr, (unsigned int)dbuf.dsize ));
3593 state->bad_entry = true;
3594 return 1;
3596 return 0;
3599 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3600 struct tdb_validation_status *state)
3602 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3603 if (!centry) {
3604 return 1;
3607 (void)centry_uint32(centry);
3608 if (NT_STATUS_IS_OK(centry->status)) {
3609 struct dom_sid sid;
3610 (void)centry_sid(centry, &sid);
3613 centry_free(centry);
3615 if (!(state->success)) {
3616 return 1;
3618 DEBUG(10,("validate_ns: %s ok\n", keystr));
3619 return 0;
3622 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3623 struct tdb_validation_status *state)
3625 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3626 if (!centry) {
3627 return 1;
3630 if (NT_STATUS_IS_OK(centry->status)) {
3631 (void)centry_uint32(centry);
3632 (void)centry_string(centry, mem_ctx);
3633 (void)centry_string(centry, mem_ctx);
3636 centry_free(centry);
3638 if (!(state->success)) {
3639 return 1;
3641 DEBUG(10,("validate_sn: %s ok\n", keystr));
3642 return 0;
3645 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3646 struct tdb_validation_status *state)
3648 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649 struct dom_sid sid;
3651 if (!centry) {
3652 return 1;
3655 (void)centry_string(centry, mem_ctx);
3656 (void)centry_string(centry, mem_ctx);
3657 (void)centry_string(centry, mem_ctx);
3658 (void)centry_string(centry, mem_ctx);
3659 (void)centry_uint32(centry);
3660 (void)centry_sid(centry, &sid);
3661 (void)centry_sid(centry, &sid);
3663 centry_free(centry);
3665 if (!(state->success)) {
3666 return 1;
3668 DEBUG(10,("validate_u: %s ok\n", keystr));
3669 return 0;
3672 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3673 struct tdb_validation_status *state)
3675 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3677 if (!centry) {
3678 return 1;
3681 (void)centry_nttime(centry);
3682 (void)centry_nttime(centry);
3683 (void)centry_uint16(centry);
3685 centry_free(centry);
3687 if (!(state->success)) {
3688 return 1;
3690 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3691 return 0;
3694 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3695 struct tdb_validation_status *state)
3697 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3699 if (!centry) {
3700 return 1;
3703 (void)centry_uint16(centry);
3704 (void)centry_uint16(centry);
3705 (void)centry_uint32(centry);
3706 (void)centry_nttime(centry);
3707 (void)centry_nttime(centry);
3709 centry_free(centry);
3711 if (!(state->success)) {
3712 return 1;
3714 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3715 return 0;
3718 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3719 struct tdb_validation_status *state)
3721 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3723 if (!centry) {
3724 return 1;
3727 (void)centry_time(centry);
3728 (void)centry_hash16(centry, mem_ctx);
3730 /* We only have 17 bytes more data in the salted cred case. */
3731 if (centry->len - centry->ofs == 17) {
3732 (void)centry_hash16(centry, mem_ctx);
3735 centry_free(centry);
3737 if (!(state->success)) {
3738 return 1;
3740 DEBUG(10,("validate_cred: %s ok\n", keystr));
3741 return 0;
3744 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3745 struct tdb_validation_status *state)
3747 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3748 int32 num_entries, i;
3750 if (!centry) {
3751 return 1;
3754 num_entries = (int32)centry_uint32(centry);
3756 for (i=0; i< num_entries; i++) {
3757 struct dom_sid sid;
3758 (void)centry_string(centry, mem_ctx);
3759 (void)centry_string(centry, mem_ctx);
3760 (void)centry_string(centry, mem_ctx);
3761 (void)centry_string(centry, mem_ctx);
3762 (void)centry_sid(centry, &sid);
3763 (void)centry_sid(centry, &sid);
3766 centry_free(centry);
3768 if (!(state->success)) {
3769 return 1;
3771 DEBUG(10,("validate_ul: %s ok\n", keystr));
3772 return 0;
3775 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3776 struct tdb_validation_status *state)
3778 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3779 int32 num_entries, i;
3781 if (!centry) {
3782 return 1;
3785 num_entries = centry_uint32(centry);
3787 for (i=0; i< num_entries; i++) {
3788 (void)centry_string(centry, mem_ctx);
3789 (void)centry_string(centry, mem_ctx);
3790 (void)centry_uint32(centry);
3793 centry_free(centry);
3795 if (!(state->success)) {
3796 return 1;
3798 DEBUG(10,("validate_gl: %s ok\n", keystr));
3799 return 0;
3802 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3803 struct tdb_validation_status *state)
3805 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3806 int32 num_groups, i;
3808 if (!centry) {
3809 return 1;
3812 num_groups = centry_uint32(centry);
3814 for (i=0; i< num_groups; i++) {
3815 struct dom_sid sid;
3816 centry_sid(centry, &sid);
3819 centry_free(centry);
3821 if (!(state->success)) {
3822 return 1;
3824 DEBUG(10,("validate_ug: %s ok\n", keystr));
3825 return 0;
3828 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3829 struct tdb_validation_status *state)
3831 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3832 int32 num_aliases, i;
3834 if (!centry) {
3835 return 1;
3838 num_aliases = centry_uint32(centry);
3840 for (i=0; i < num_aliases; i++) {
3841 (void)centry_uint32(centry);
3844 centry_free(centry);
3846 if (!(state->success)) {
3847 return 1;
3849 DEBUG(10,("validate_ua: %s ok\n", keystr));
3850 return 0;
3853 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3854 struct tdb_validation_status *state)
3856 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3857 int32 num_names, i;
3859 if (!centry) {
3860 return 1;
3863 num_names = centry_uint32(centry);
3865 for (i=0; i< num_names; i++) {
3866 struct dom_sid sid;
3867 centry_sid(centry, &sid);
3868 (void)centry_string(centry, mem_ctx);
3869 (void)centry_uint32(centry);
3872 centry_free(centry);
3874 if (!(state->success)) {
3875 return 1;
3877 DEBUG(10,("validate_gm: %s ok\n", keystr));
3878 return 0;
3881 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3882 struct tdb_validation_status *state)
3884 /* Can't say anything about this other than must be nonzero. */
3885 if (dbuf.dsize == 0) {
3886 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3887 keystr));
3888 state->bad_entry = true;
3889 state->success = false;
3890 return 1;
3893 DEBUG(10,("validate_dr: %s ok\n", keystr));
3894 return 0;
3897 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3898 struct tdb_validation_status *state)
3900 /* Can't say anything about this other than must be nonzero. */
3901 if (dbuf.dsize == 0) {
3902 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3903 keystr));
3904 state->bad_entry = true;
3905 state->success = false;
3906 return 1;
3909 DEBUG(10,("validate_de: %s ok\n", keystr));
3910 return 0;
3913 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3914 TDB_DATA dbuf, struct tdb_validation_status *state)
3916 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3918 if (!centry) {
3919 return 1;
3922 (void)centry_string(centry, mem_ctx);
3923 (void)centry_string(centry, mem_ctx);
3924 (void)centry_string(centry, mem_ctx);
3925 (void)centry_uint32(centry);
3927 centry_free(centry);
3929 if (!(state->success)) {
3930 return 1;
3932 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3933 return 0;
3936 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3937 TDB_DATA dbuf,
3938 struct tdb_validation_status *state)
3940 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3942 if (!centry) {
3943 return 1;
3946 (void)centry_string( centry, mem_ctx );
3948 centry_free(centry);
3950 if (!(state->success)) {
3951 return 1;
3953 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3954 return 0;
3957 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3958 TDB_DATA dbuf,
3959 struct tdb_validation_status *state)
3961 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3963 if (!centry) {
3964 return 1;
3967 (void)centry_string( centry, mem_ctx );
3969 centry_free(centry);
3971 if (!(state->success)) {
3972 return 1;
3974 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3975 return 0;
3978 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3979 TDB_DATA dbuf,
3980 struct tdb_validation_status *state)
3982 if (dbuf.dsize == 0) {
3983 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3984 "key %s (len ==0) ?\n", keystr));
3985 state->bad_entry = true;
3986 state->success = false;
3987 return 1;
3990 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3991 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3992 return 0;
3995 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3996 struct tdb_validation_status *state)
3998 if (dbuf.dsize != 4) {
3999 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
4000 keystr, (unsigned int)dbuf.dsize ));
4001 state->bad_entry = true;
4002 state->success = false;
4003 return 1;
4005 DEBUG(10,("validate_offline: %s ok\n", keystr));
4006 return 0;
4009 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4010 struct tdb_validation_status *state)
4013 * Ignore validation for now. The proper way to do this is with a
4014 * checksum. Just pure parsing does not really catch much.
4016 return 0;
4019 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
4020 struct tdb_validation_status *state)
4022 if (dbuf.dsize != 4) {
4023 DEBUG(0, ("validate_cache_version: Corrupt cache for "
4024 "key %s (len %u != 4) ?\n",
4025 keystr, (unsigned int)dbuf.dsize));
4026 state->bad_entry = true;
4027 state->success = false;
4028 return 1;
4031 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4032 return 0;
4035 /***********************************************************************
4036 A list of all possible cache tdb keys with associated validation
4037 functions.
4038 ***********************************************************************/
4040 struct key_val_struct {
4041 const char *keyname;
4042 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4043 } key_val[] = {
4044 {"SEQNUM/", validate_seqnum},
4045 {"NS/", validate_ns},
4046 {"SN/", validate_sn},
4047 {"U/", validate_u},
4048 {"LOC_POL/", validate_loc_pol},
4049 {"PWD_POL/", validate_pwd_pol},
4050 {"CRED/", validate_cred},
4051 {"UL/", validate_ul},
4052 {"GL/", validate_gl},
4053 {"UG/", validate_ug},
4054 {"UA", validate_ua},
4055 {"GM/", validate_gm},
4056 {"DR/", validate_dr},
4057 {"DE/", validate_de},
4058 {"NSS/PWINFO/", validate_pwinfo},
4059 {"TRUSTDOMCACHE/", validate_trustdomcache},
4060 {"NSS/NA/", validate_nss_na},
4061 {"NSS/AN/", validate_nss_an},
4062 {"WINBINDD_OFFLINE", validate_offline},
4063 {"NDR/", validate_ndr},
4064 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4065 {NULL, NULL}
4068 /***********************************************************************
4069 Function to look at every entry in the tdb and validate it as far as
4070 possible.
4071 ***********************************************************************/
4073 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4075 int i;
4076 unsigned int max_key_len = 1024;
4077 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4079 /* Paranoia check. */
4080 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
4081 strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
4082 max_key_len = 1024 * 1024;
4084 if (kbuf.dsize > max_key_len) {
4085 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4086 "(%u) > (%u)\n\n",
4087 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4088 return 1;
4091 for (i = 0; key_val[i].keyname; i++) {
4092 size_t namelen = strlen(key_val[i].keyname);
4093 if (kbuf.dsize >= namelen && (
4094 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4095 TALLOC_CTX *mem_ctx;
4096 char *keystr;
4097 int ret;
4099 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4100 if (!keystr) {
4101 return 1;
4103 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4104 keystr[kbuf.dsize] = '\0';
4106 mem_ctx = talloc_init("validate_ctx");
4107 if (!mem_ctx) {
4108 SAFE_FREE(keystr);
4109 return 1;
4112 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4113 v_state);
4115 SAFE_FREE(keystr);
4116 talloc_destroy(mem_ctx);
4117 return ret;
4121 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4122 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4123 DEBUG(0,("data :\n"));
4124 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4125 v_state->unknown_key = true;
4126 v_state->success = false;
4127 return 1; /* terminate. */
4130 static void validate_panic(const char *const why)
4132 DEBUG(0,("validating cache: would panic %s\n", why ));
4133 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4134 exit(47);
4137 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4138 TDB_DATA key,
4139 TDB_DATA data,
4140 void *state)
4142 uint64_t ctimeout;
4143 TDB_DATA blob;
4145 if (is_non_centry_key(key)) {
4146 return 0;
4149 if (data.dptr == NULL || data.dsize == 0) {
4150 if (tdb_delete(tdb, key) < 0) {
4151 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4152 key.dptr));
4153 return 1;
4157 /* add timeout to blob (uint64_t) */
4158 blob.dsize = data.dsize + 8;
4160 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4161 if (blob.dptr == NULL) {
4162 return 1;
4164 memset(blob.dptr, 0, blob.dsize);
4166 /* copy status and seqnum */
4167 memcpy(blob.dptr, data.dptr, 8);
4169 /* add timeout */
4170 ctimeout = lp_winbind_cache_time() + time(NULL);
4171 SBVAL(blob.dptr, 8, ctimeout);
4173 /* copy the rest */
4174 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4176 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4177 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4178 key.dptr));
4179 SAFE_FREE(blob.dptr);
4180 return 1;
4183 SAFE_FREE(blob.dptr);
4184 return 0;
4187 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4189 int rc;
4191 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4193 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4194 if (rc < 0) {
4195 return false;
4198 return true;
4201 /***********************************************************************
4202 Try and validate every entry in the winbindd cache. If we fail here,
4203 delete the cache tdb and return non-zero.
4204 ***********************************************************************/
4206 int winbindd_validate_cache(void)
4208 int ret = -1;
4209 const char *tdb_path = state_path("winbindd_cache.tdb");
4210 TDB_CONTEXT *tdb = NULL;
4211 uint32_t vers_id;
4212 bool ok;
4214 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4215 smb_panic_fn = validate_panic;
4217 tdb = tdb_open_log(tdb_path,
4218 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4219 TDB_INCOMPATIBLE_HASH |
4220 ( lp_winbind_offline_logon()
4221 ? TDB_DEFAULT
4222 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4223 O_RDWR|O_CREAT,
4224 0600);
4225 if (!tdb) {
4226 DEBUG(0, ("winbindd_validate_cache: "
4227 "error opening/initializing tdb\n"));
4228 goto done;
4231 /* Version check and upgrade code. */
4232 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4233 DEBUG(10, ("Fresh database\n"));
4234 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4235 vers_id = WINBINDD_CACHE_VERSION;
4238 if (vers_id != WINBINDD_CACHE_VERSION) {
4239 if (vers_id == WINBINDD_CACHE_VER1) {
4240 ok = wbcache_upgrade_v1_to_v2(tdb);
4241 if (!ok) {
4242 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4243 unlink(tdb_path);
4244 goto done;
4247 tdb_store_uint32(tdb,
4248 WINBINDD_CACHE_VERSION_KEYSTR,
4249 WINBINDD_CACHE_VERSION);
4250 vers_id = WINBINDD_CACHE_VER2;
4254 tdb_close(tdb);
4256 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4258 if (ret != 0) {
4259 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4260 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4261 unlink(tdb_path);
4264 done:
4265 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4266 smb_panic_fn = smb_panic;
4267 return ret;
4270 /***********************************************************************
4271 Try and validate every entry in the winbindd cache.
4272 ***********************************************************************/
4274 int winbindd_validate_cache_nobackup(void)
4276 int ret = -1;
4277 const char *tdb_path = state_path("winbindd_cache.tdb");
4279 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4280 smb_panic_fn = validate_panic;
4283 if (wcache == NULL || wcache->tdb == NULL) {
4284 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4285 } else {
4286 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4289 if (ret != 0) {
4290 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4291 "successful.\n"));
4294 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4295 "function\n"));
4296 smb_panic_fn = smb_panic;
4297 return ret;
4300 bool winbindd_cache_validate_and_initialize(void)
4302 close_winbindd_cache();
4304 if (lp_winbind_offline_logon()) {
4305 if (winbindd_validate_cache() < 0) {
4306 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4307 "could be restored.\n"));
4311 return initialize_winbindd_cache();
4314 /*********************************************************************
4315 ********************************************************************/
4317 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4318 struct winbindd_tdc_domain **domains,
4319 size_t *num_domains )
4321 struct winbindd_tdc_domain *list = NULL;
4322 size_t idx;
4323 int i;
4324 bool set_only = false;
4326 /* don't allow duplicates */
4328 idx = *num_domains;
4329 list = *domains;
4331 for ( i=0; i< (*num_domains); i++ ) {
4332 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4333 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4334 new_dom->name));
4335 idx = i;
4336 set_only = true;
4338 break;
4342 if ( !set_only ) {
4343 if ( !*domains ) {
4344 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4345 idx = 0;
4346 } else {
4347 list = talloc_realloc( *domains, *domains,
4348 struct winbindd_tdc_domain,
4349 (*num_domains)+1);
4350 idx = *num_domains;
4353 ZERO_STRUCT( list[idx] );
4356 if ( !list )
4357 return false;
4359 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4360 if (list[idx].domain_name == NULL) {
4361 return false;
4363 if (new_dom->alt_name != NULL) {
4364 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4365 if (list[idx].dns_name == NULL) {
4366 return false;
4370 if ( !is_null_sid( &new_dom->sid ) ) {
4371 sid_copy( &list[idx].sid, &new_dom->sid );
4372 } else {
4373 sid_copy(&list[idx].sid, &global_sid_NULL);
4376 if ( new_dom->domain_flags != 0x0 )
4377 list[idx].trust_flags = new_dom->domain_flags;
4379 if ( new_dom->domain_type != 0x0 )
4380 list[idx].trust_type = new_dom->domain_type;
4382 if ( new_dom->domain_trust_attribs != 0x0 )
4383 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4385 if ( !set_only ) {
4386 *domains = list;
4387 *num_domains = idx + 1;
4390 return true;
4393 /*********************************************************************
4394 ********************************************************************/
4396 static TDB_DATA make_tdc_key( const char *domain_name )
4398 char *keystr = NULL;
4399 TDB_DATA key = { NULL, 0 };
4401 if ( !domain_name ) {
4402 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4403 return key;
4406 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4407 return key;
4409 key = string_term_tdb_data(keystr);
4411 return key;
4414 /*********************************************************************
4415 ********************************************************************/
4417 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4418 size_t num_domains,
4419 unsigned char **buf )
4421 unsigned char *buffer = NULL;
4422 int len = 0;
4423 int buflen = 0;
4424 int i = 0;
4426 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4427 (int)num_domains));
4429 buflen = 0;
4431 again:
4432 len = 0;
4434 /* Store the number of array items first */
4435 len += tdb_pack( buffer+len, buflen-len, "d",
4436 num_domains );
4438 /* now pack each domain trust record */
4439 for ( i=0; i<num_domains; i++ ) {
4441 fstring tmp;
4443 if ( buflen > 0 ) {
4444 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4445 domains[i].domain_name,
4446 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4449 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4450 domains[i].domain_name,
4451 domains[i].dns_name ? domains[i].dns_name : "",
4452 sid_to_fstring(tmp, &domains[i].sid),
4453 domains[i].trust_flags,
4454 domains[i].trust_attribs,
4455 domains[i].trust_type );
4458 if ( buflen < len ) {
4459 SAFE_FREE(buffer);
4460 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4461 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4462 buflen = -1;
4463 goto done;
4465 buflen = len;
4466 goto again;
4469 *buf = buffer;
4471 done:
4472 return buflen;
4475 /*********************************************************************
4476 ********************************************************************/
4478 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4479 struct winbindd_tdc_domain **domains )
4481 fstring domain_name, dns_name, sid_string;
4482 uint32 type, attribs, flags;
4483 int num_domains;
4484 int len = 0;
4485 int i;
4486 struct winbindd_tdc_domain *list = NULL;
4488 /* get the number of domains */
4489 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4490 if ( len == -1 ) {
4491 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4492 return 0;
4495 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4496 if ( !list ) {
4497 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4498 return 0;
4501 for ( i=0; i<num_domains; i++ ) {
4502 int this_len;
4504 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4505 domain_name,
4506 dns_name,
4507 sid_string,
4508 &flags,
4509 &attribs,
4510 &type );
4512 if ( this_len == -1 ) {
4513 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4514 TALLOC_FREE( list );
4515 return 0;
4517 len += this_len;
4519 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4520 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4521 domain_name, dns_name, sid_string,
4522 flags, attribs, type));
4524 list[i].domain_name = talloc_strdup( list, domain_name );
4525 list[i].dns_name = NULL;
4526 if (dns_name[0] != '\0') {
4527 list[i].dns_name = talloc_strdup(list, dns_name);
4529 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4530 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4531 domain_name));
4533 list[i].trust_flags = flags;
4534 list[i].trust_attribs = attribs;
4535 list[i].trust_type = type;
4538 *domains = list;
4540 return num_domains;
4543 /*********************************************************************
4544 ********************************************************************/
4546 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4548 TDB_DATA key = make_tdc_key( lp_workgroup() );
4549 TDB_DATA data = { NULL, 0 };
4550 int ret;
4552 if ( !key.dptr )
4553 return false;
4555 /* See if we were asked to delete the cache entry */
4557 if ( !domains ) {
4558 ret = tdb_delete( wcache->tdb, key );
4559 goto done;
4562 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4564 if ( !data.dptr ) {
4565 ret = -1;
4566 goto done;
4569 ret = tdb_store( wcache->tdb, key, data, 0 );
4571 done:
4572 SAFE_FREE( data.dptr );
4573 SAFE_FREE( key.dptr );
4575 return ( ret == 0 );
4578 /*********************************************************************
4579 ********************************************************************/
4581 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4583 TDB_DATA key = make_tdc_key( lp_workgroup() );
4584 TDB_DATA data = { NULL, 0 };
4586 *domains = NULL;
4587 *num_domains = 0;
4589 if ( !key.dptr )
4590 return false;
4592 data = tdb_fetch_compat( wcache->tdb, key );
4594 SAFE_FREE( key.dptr );
4596 if ( !data.dptr )
4597 return false;
4599 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4601 SAFE_FREE( data.dptr );
4603 if ( !*domains )
4604 return false;
4606 return true;
4609 /*********************************************************************
4610 ********************************************************************/
4612 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4614 struct winbindd_tdc_domain *dom_list = NULL;
4615 size_t num_domains = 0;
4616 bool ret = false;
4618 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4619 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4620 domain->name, domain->alt_name,
4621 sid_string_dbg(&domain->sid),
4622 domain->domain_flags,
4623 domain->domain_trust_attribs,
4624 domain->domain_type));
4626 if ( !init_wcache() ) {
4627 return false;
4630 /* fetch the list */
4632 wcache_tdc_fetch_list( &dom_list, &num_domains );
4634 /* add the new domain */
4636 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4637 goto done;
4640 /* pack the domain */
4642 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4643 goto done;
4646 /* Success */
4648 ret = true;
4649 done:
4650 TALLOC_FREE( dom_list );
4652 return ret;
4655 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4656 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4658 struct winbindd_tdc_domain *dst;
4660 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4661 if (dst == NULL) {
4662 goto fail;
4664 dst->domain_name = talloc_strdup(dst, src->domain_name);
4665 if (dst->domain_name == NULL) {
4666 goto fail;
4669 dst->dns_name = NULL;
4670 if (src->dns_name != NULL) {
4671 dst->dns_name = talloc_strdup(dst, src->dns_name);
4672 if (dst->dns_name == NULL) {
4673 goto fail;
4677 sid_copy(&dst->sid, &src->sid);
4678 dst->trust_flags = src->trust_flags;
4679 dst->trust_type = src->trust_type;
4680 dst->trust_attribs = src->trust_attribs;
4681 return dst;
4682 fail:
4683 TALLOC_FREE(dst);
4684 return NULL;
4687 /*********************************************************************
4688 ********************************************************************/
4690 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4692 struct winbindd_tdc_domain *dom_list = NULL;
4693 size_t num_domains = 0;
4694 int i;
4695 struct winbindd_tdc_domain *d = NULL;
4697 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4699 if ( !init_wcache() ) {
4700 return NULL;
4703 /* fetch the list */
4705 wcache_tdc_fetch_list( &dom_list, &num_domains );
4707 for ( i=0; i<num_domains; i++ ) {
4708 if ( strequal(name, dom_list[i].domain_name) ||
4709 strequal(name, dom_list[i].dns_name) )
4711 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4712 name));
4714 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4715 break;
4719 TALLOC_FREE( dom_list );
4721 return d;
4724 /*********************************************************************
4725 ********************************************************************/
4727 struct winbindd_tdc_domain*
4728 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4729 const struct dom_sid *sid)
4731 struct winbindd_tdc_domain *dom_list = NULL;
4732 size_t num_domains = 0;
4733 int i;
4734 struct winbindd_tdc_domain *d = NULL;
4736 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4737 sid_string_dbg(sid)));
4739 if (!init_wcache()) {
4740 return NULL;
4743 /* fetch the list */
4745 wcache_tdc_fetch_list(&dom_list, &num_domains);
4747 for (i = 0; i<num_domains; i++) {
4748 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4749 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4750 "Found domain %s for SID %s\n",
4751 dom_list[i].domain_name,
4752 sid_string_dbg(sid)));
4754 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4755 break;
4759 TALLOC_FREE(dom_list);
4761 return d;
4765 /*********************************************************************
4766 ********************************************************************/
4768 void wcache_tdc_clear( void )
4770 if ( !init_wcache() )
4771 return;
4773 wcache_tdc_store_list( NULL, 0 );
4775 return;
4779 /*********************************************************************
4780 ********************************************************************/
4782 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4783 NTSTATUS status,
4784 const struct dom_sid *user_sid,
4785 const char *homedir,
4786 const char *shell,
4787 const char *gecos,
4788 uint32 gid)
4790 struct cache_entry *centry;
4791 fstring tmp;
4793 if ( (centry = centry_start(domain, status)) == NULL )
4794 return;
4796 centry_put_string( centry, homedir );
4797 centry_put_string( centry, shell );
4798 centry_put_string( centry, gecos );
4799 centry_put_uint32( centry, gid );
4801 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4803 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4805 centry_free(centry);
4808 #ifdef HAVE_ADS
4810 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4811 const struct dom_sid *user_sid,
4812 TALLOC_CTX *ctx,
4813 const char **homedir, const char **shell,
4814 const char **gecos, gid_t *p_gid)
4816 struct winbind_cache *cache = get_cache(domain);
4817 struct cache_entry *centry = NULL;
4818 NTSTATUS nt_status;
4819 fstring tmp;
4821 if (!cache->tdb)
4822 goto do_query;
4824 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4825 sid_to_fstring(tmp, user_sid));
4827 if (!centry)
4828 goto do_query;
4830 *homedir = centry_string( centry, ctx );
4831 *shell = centry_string( centry, ctx );
4832 *gecos = centry_string( centry, ctx );
4833 *p_gid = centry_uint32( centry );
4835 centry_free(centry);
4837 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4838 sid_string_dbg(user_sid)));
4840 return NT_STATUS_OK;
4842 do_query:
4844 nt_status = nss_get_info( domain->name, user_sid, ctx,
4845 homedir, shell, gecos, p_gid );
4847 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4849 if ( NT_STATUS_IS_OK(nt_status) ) {
4850 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4851 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4852 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4853 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4855 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4856 *homedir, *shell, *gecos, *p_gid );
4859 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4860 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4861 domain->name ));
4862 set_domain_offline( domain );
4865 return nt_status;
4868 #endif
4870 /* the cache backend methods are exposed via this structure */
4871 struct winbindd_methods cache_methods = {
4872 true,
4873 query_user_list,
4874 enum_dom_groups,
4875 enum_local_groups,
4876 name_to_sid,
4877 sid_to_name,
4878 rids_to_names,
4879 query_user,
4880 lookup_usergroups,
4881 lookup_useraliases,
4882 lookup_groupmem,
4883 sequence_number,
4884 lockout_policy,
4885 password_policy,
4886 trusted_domains
4889 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4890 uint32_t opnum, const DATA_BLOB *req,
4891 TDB_DATA *pkey)
4893 char *key;
4894 size_t keylen;
4896 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4897 if (key == NULL) {
4898 return false;
4900 keylen = talloc_get_size(key) - 1;
4902 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4903 if (key == NULL) {
4904 return false;
4906 memcpy(key + keylen, req->data, req->length);
4908 pkey->dptr = (uint8_t *)key;
4909 pkey->dsize = talloc_get_size(key);
4910 return true;
4913 static bool wcache_opnum_cacheable(uint32_t opnum)
4915 switch (opnum) {
4916 case NDR_WBINT_PING:
4917 case NDR_WBINT_QUERYSEQUENCENUMBER:
4918 case NDR_WBINT_ALLOCATEUID:
4919 case NDR_WBINT_ALLOCATEGID:
4920 case NDR_WBINT_CHECKMACHINEACCOUNT:
4921 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4922 case NDR_WBINT_PINGDC:
4923 return false;
4925 return true;
4928 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4929 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4931 TDB_DATA key, data;
4932 bool ret = false;
4934 if (!wcache_opnum_cacheable(opnum) ||
4935 is_my_own_sam_domain(domain) ||
4936 is_builtin_domain(domain)) {
4937 return false;
4940 if (wcache->tdb == NULL) {
4941 return false;
4944 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4945 return false;
4947 data = tdb_fetch_compat(wcache->tdb, key);
4948 TALLOC_FREE(key.dptr);
4950 if (data.dptr == NULL) {
4951 return false;
4953 if (data.dsize < 12) {
4954 goto fail;
4957 if (!is_domain_offline(domain)) {
4958 uint32_t entry_seqnum, dom_seqnum, last_check;
4959 uint64_t entry_timeout;
4961 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4962 &last_check)) {
4963 goto fail;
4965 entry_seqnum = IVAL(data.dptr, 0);
4966 if (entry_seqnum != dom_seqnum) {
4967 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4968 (int)entry_seqnum));
4969 goto fail;
4971 entry_timeout = BVAL(data.dptr, 4);
4972 if (time(NULL) > entry_timeout) {
4973 DEBUG(10, ("Entry has timed out\n"));
4974 goto fail;
4978 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4979 data.dsize - 12);
4980 if (resp->data == NULL) {
4981 DEBUG(10, ("talloc failed\n"));
4982 goto fail;
4984 resp->length = data.dsize - 12;
4986 ret = true;
4987 fail:
4988 SAFE_FREE(data.dptr);
4989 return ret;
4992 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4993 const DATA_BLOB *req, const DATA_BLOB *resp)
4995 TDB_DATA key, data;
4996 uint32_t dom_seqnum, last_check;
4997 uint64_t timeout;
4999 if (!wcache_opnum_cacheable(opnum) ||
5000 is_my_own_sam_domain(domain) ||
5001 is_builtin_domain(domain)) {
5002 return;
5005 if (wcache->tdb == NULL) {
5006 return;
5009 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
5010 DEBUG(10, ("could not fetch seqnum for domain %s\n",
5011 domain->name));
5012 return;
5015 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
5016 return;
5019 timeout = time(NULL) + lp_winbind_cache_time();
5021 data.dsize = resp->length + 12;
5022 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
5023 if (data.dptr == NULL) {
5024 goto done;
5027 SIVAL(data.dptr, 0, dom_seqnum);
5028 SBVAL(data.dptr, 4, timeout);
5029 memcpy(data.dptr + 12, resp->data, resp->length);
5031 tdb_store(wcache->tdb, key, data, 0);
5033 done:
5034 TALLOC_FREE(key.dptr);
5035 return;