s3:libsmb: let cli_read_andx_create() accept any length
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blobd7499df673ec3026e9cf1d85bdebbc510d566a93
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
946 centry_put_uint32(centry, type);
947 centry_put_sid(centry, sid);
948 fstrcpy(uname, name);
949 (void)strupper_m(uname);
950 centry_end(centry, "NS/%s/%s", domain_name, uname);
951 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
952 uname, sid_string_dbg(sid), nt_errstr(status)));
953 centry_free(centry);
956 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
957 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring sid_string;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if (NT_STATUS_IS_OK(status)) {
967 centry_put_uint32(centry, type);
968 centry_put_string(centry, domain_name);
969 centry_put_string(centry, name);
972 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
973 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
974 domain_name, name, nt_errstr(status)));
975 centry_free(centry);
979 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
980 struct wbint_userinfo *info)
982 struct cache_entry *centry;
983 fstring sid_string;
985 if (is_null_sid(&info->user_sid)) {
986 return;
989 centry = centry_start(domain, status);
990 if (!centry)
991 return;
992 centry_put_string(centry, info->acct_name);
993 centry_put_string(centry, info->full_name);
994 centry_put_string(centry, info->homedir);
995 centry_put_string(centry, info->shell);
996 centry_put_uint32(centry, info->primary_gid);
997 centry_put_sid(centry, &info->user_sid);
998 centry_put_sid(centry, &info->group_sid);
999 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1000 &info->user_sid));
1001 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 centry_free(centry);
1005 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1006 NTSTATUS status,
1007 struct samr_DomInfo12 *lockout_policy)
1009 struct cache_entry *centry;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1015 centry_put_nttime(centry, lockout_policy->lockout_duration);
1016 centry_put_nttime(centry, lockout_policy->lockout_window);
1017 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1019 centry_end(centry, "LOC_POL/%s", domain->name);
1021 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1023 centry_free(centry);
1028 static void wcache_save_password_policy(struct winbindd_domain *domain,
1029 NTSTATUS status,
1030 struct samr_DomInfo1 *policy)
1032 struct cache_entry *centry;
1034 centry = centry_start(domain, status);
1035 if (!centry)
1036 return;
1038 centry_put_uint16(centry, policy->min_password_length);
1039 centry_put_uint16(centry, policy->password_history_length);
1040 centry_put_uint32(centry, policy->password_properties);
1041 centry_put_nttime(centry, policy->max_password_age);
1042 centry_put_nttime(centry, policy->min_password_age);
1044 centry_end(centry, "PWD_POL/%s", domain->name);
1046 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1048 centry_free(centry);
1051 /***************************************************************************
1052 ***************************************************************************/
1054 static void wcache_save_username_alias(struct winbindd_domain *domain,
1055 NTSTATUS status,
1056 const char *name, const char *alias)
1058 struct cache_entry *centry;
1059 fstring uname;
1061 if ( (centry = centry_start(domain, status)) == NULL )
1062 return;
1064 centry_put_string( centry, alias );
1066 fstrcpy(uname, name);
1067 (void)strupper_m(uname);
1068 centry_end(centry, "NSS/NA/%s", uname);
1070 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1072 centry_free(centry);
1075 static void wcache_save_alias_username(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *alias, const char *name)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, name );
1087 fstrcpy(uname, alias);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/AN/%s", uname);
1091 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1093 centry_free(centry);
1096 /***************************************************************************
1097 ***************************************************************************/
1099 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1100 struct winbindd_domain *domain,
1101 const char *name, char **alias )
1103 struct winbind_cache *cache = get_cache(domain);
1104 struct cache_entry *centry = NULL;
1105 NTSTATUS status;
1106 char *upper_name;
1108 if ( domain->internal )
1109 return NT_STATUS_NOT_SUPPORTED;
1111 if (!cache->tdb)
1112 goto do_query;
1114 upper_name = talloc_strdup(mem_ctx, name);
1115 if (upper_name == NULL) {
1116 return NT_STATUS_NO_MEMORY;
1118 if (!strupper_m(upper_name)) {
1119 talloc_free(upper_name);
1120 return NT_STATUS_INVALID_PARAMETER;
1123 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1125 talloc_free(upper_name);
1127 if (!centry)
1128 goto do_query;
1130 status = centry->status;
1132 if (!NT_STATUS_IS_OK(status)) {
1133 centry_free(centry);
1134 return status;
1137 *alias = centry_string( centry, mem_ctx );
1139 centry_free(centry);
1141 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1142 name, *alias ? *alias : "(none)"));
1144 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1146 do_query:
1148 /* If its not in cache and we are offline, then fail */
1150 if ( get_global_winbindd_state_offline() || !domain->online ) {
1151 DEBUG(8,("resolve_username_to_alias: rejecting query "
1152 "in offline mode\n"));
1153 return NT_STATUS_NOT_FOUND;
1156 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1158 if ( NT_STATUS_IS_OK( status ) ) {
1159 wcache_save_username_alias(domain, status, name, *alias);
1162 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1163 wcache_save_username_alias(domain, status, name, "(NULL)");
1166 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1167 nt_errstr(status)));
1169 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1170 set_domain_offline( domain );
1173 return status;
1176 /***************************************************************************
1177 ***************************************************************************/
1179 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1180 struct winbindd_domain *domain,
1181 const char *alias, char **name )
1183 struct winbind_cache *cache = get_cache(domain);
1184 struct cache_entry *centry = NULL;
1185 NTSTATUS status;
1186 char *upper_name;
1188 if ( domain->internal )
1189 return NT_STATUS_NOT_SUPPORTED;
1191 if (!cache->tdb)
1192 goto do_query;
1194 upper_name = talloc_strdup(mem_ctx, alias);
1195 if (upper_name == NULL) {
1196 return NT_STATUS_NO_MEMORY;
1198 if (!strupper_m(upper_name)) {
1199 talloc_free(upper_name);
1200 return NT_STATUS_INVALID_PARAMETER;
1203 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1205 talloc_free(upper_name);
1207 if (!centry)
1208 goto do_query;
1210 status = centry->status;
1212 if (!NT_STATUS_IS_OK(status)) {
1213 centry_free(centry);
1214 return status;
1217 *name = centry_string( centry, mem_ctx );
1219 centry_free(centry);
1221 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1222 alias, *name ? *name : "(none)"));
1224 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1226 do_query:
1228 /* If its not in cache and we are offline, then fail */
1230 if ( get_global_winbindd_state_offline() || !domain->online ) {
1231 DEBUG(8,("resolve_alias_to_username: rejecting query "
1232 "in offline mode\n"));
1233 return NT_STATUS_NOT_FOUND;
1236 /* an alias cannot contain a domain prefix or '@' */
1238 if (strchr(alias, '\\') || strchr(alias, '@')) {
1239 DEBUG(10,("resolve_alias_to_username: skipping fully "
1240 "qualified name %s\n", alias));
1241 return NT_STATUS_OBJECT_NAME_INVALID;
1244 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1246 if ( NT_STATUS_IS_OK( status ) ) {
1247 wcache_save_alias_username( domain, status, alias, *name );
1250 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1251 wcache_save_alias_username(domain, status, alias, "(NULL)");
1254 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1255 nt_errstr(status)));
1257 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1258 set_domain_offline( domain );
1261 return status;
1264 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1266 struct winbind_cache *cache = get_cache(domain);
1267 TDB_DATA data;
1268 fstring key_str, tmp;
1269 uint32 rid;
1271 if (!cache->tdb) {
1272 return NT_STATUS_INTERNAL_DB_ERROR;
1275 if (is_null_sid(sid)) {
1276 return NT_STATUS_INVALID_SID;
1279 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1280 return NT_STATUS_INVALID_SID;
1283 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1285 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1286 if (!data.dptr) {
1287 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1290 SAFE_FREE(data.dptr);
1291 return NT_STATUS_OK;
1294 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1295 as new salted ones. */
1297 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1298 TALLOC_CTX *mem_ctx,
1299 const struct dom_sid *sid,
1300 const uint8 **cached_nt_pass,
1301 const uint8 **cached_salt)
1303 struct winbind_cache *cache = get_cache(domain);
1304 struct cache_entry *centry = NULL;
1305 NTSTATUS status;
1306 uint32 rid;
1307 fstring tmp;
1309 if (!cache->tdb) {
1310 return NT_STATUS_INTERNAL_DB_ERROR;
1313 if (is_null_sid(sid)) {
1314 return NT_STATUS_INVALID_SID;
1317 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1318 return NT_STATUS_INVALID_SID;
1321 /* Try and get a salted cred first. If we can't
1322 fall back to an unsalted cred. */
1324 centry = wcache_fetch(cache, domain, "CRED/%s",
1325 sid_to_fstring(tmp, sid));
1326 if (!centry) {
1327 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1328 sid_string_dbg(sid)));
1329 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1333 * We don't use the time element at this moment,
1334 * but we have to consume it, so that we don't
1335 * neet to change the disk format of the cache.
1337 (void)centry_time(centry);
1339 /* In the salted case this isn't actually the nt_hash itself,
1340 but the MD5 of the salt + nt_hash. Let the caller
1341 sort this out. It can tell as we only return the cached_salt
1342 if we are returning a salted cred. */
1344 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1345 if (*cached_nt_pass == NULL) {
1346 fstring sidstr;
1348 sid_to_fstring(sidstr, sid);
1350 /* Bad (old) cred cache. Delete and pretend we
1351 don't have it. */
1352 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1353 sidstr));
1354 wcache_delete("CRED/%s", sidstr);
1355 centry_free(centry);
1356 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1359 /* We only have 17 bytes more data in the salted cred case. */
1360 if (centry->len - centry->ofs == 17) {
1361 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1362 } else {
1363 *cached_salt = NULL;
1366 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1367 if (*cached_salt) {
1368 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1371 status = centry->status;
1373 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1374 sid_string_dbg(sid), nt_errstr(status) ));
1376 centry_free(centry);
1377 return status;
1380 /* Store creds for a SID - only writes out new salted ones. */
1382 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1383 const struct dom_sid *sid,
1384 const uint8 nt_pass[NT_HASH_LEN])
1386 struct cache_entry *centry;
1387 fstring sid_string;
1388 uint32 rid;
1389 uint8 cred_salt[NT_HASH_LEN];
1390 uint8 salted_hash[NT_HASH_LEN];
1392 if (is_null_sid(sid)) {
1393 return NT_STATUS_INVALID_SID;
1396 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1397 return NT_STATUS_INVALID_SID;
1400 centry = centry_start(domain, NT_STATUS_OK);
1401 if (!centry) {
1402 return NT_STATUS_INTERNAL_DB_ERROR;
1405 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1407 centry_put_time(centry, time(NULL));
1409 /* Create a salt and then salt the hash. */
1410 generate_random_buffer(cred_salt, NT_HASH_LEN);
1411 E_md5hash(cred_salt, nt_pass, salted_hash);
1413 centry_put_hash16(centry, salted_hash);
1414 centry_put_hash16(centry, cred_salt);
1415 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1417 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1419 centry_free(centry);
1421 return NT_STATUS_OK;
1425 /* Query display info. This is the basic user list fn */
1426 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1427 TALLOC_CTX *mem_ctx,
1428 uint32 *num_entries,
1429 struct wbint_userinfo **info)
1431 struct winbind_cache *cache = get_cache(domain);
1432 struct cache_entry *centry = NULL;
1433 NTSTATUS status;
1434 unsigned int i, retry;
1435 bool old_status = domain->online;
1437 if (!cache->tdb)
1438 goto do_query;
1440 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1441 if (!centry)
1442 goto do_query;
1444 do_fetch_cache:
1445 *num_entries = centry_uint32(centry);
1447 if (*num_entries == 0)
1448 goto do_cached;
1450 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1451 if (! (*info)) {
1452 smb_panic_fn("query_user_list out of memory");
1454 for (i=0; i<(*num_entries); i++) {
1455 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1456 (*info)[i].full_name = centry_string(centry, mem_ctx);
1457 (*info)[i].homedir = centry_string(centry, mem_ctx);
1458 (*info)[i].shell = centry_string(centry, mem_ctx);
1459 centry_sid(centry, &(*info)[i].user_sid);
1460 centry_sid(centry, &(*info)[i].group_sid);
1463 do_cached:
1464 status = centry->status;
1466 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1467 domain->name, nt_errstr(status) ));
1469 centry_free(centry);
1470 return status;
1472 do_query:
1473 *num_entries = 0;
1474 *info = NULL;
1476 /* Return status value returned by seq number check */
1478 if (!NT_STATUS_IS_OK(domain->last_status))
1479 return domain->last_status;
1481 /* Put the query_user_list() in a retry loop. There appears to be
1482 * some bug either with Windows 2000 or Samba's handling of large
1483 * rpc replies. This manifests itself as sudden disconnection
1484 * at a random point in the enumeration of a large (60k) user list.
1485 * The retry loop simply tries the operation again. )-: It's not
1486 * pretty but an acceptable workaround until we work out what the
1487 * real problem is. */
1489 retry = 0;
1490 do {
1492 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1493 domain->name ));
1495 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1496 if (!NT_STATUS_IS_OK(status)) {
1497 DEBUG(3, ("query_user_list: returned 0x%08x, "
1498 "retrying\n", NT_STATUS_V(status)));
1500 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1501 DEBUG(3, ("query_user_list: flushing "
1502 "connection cache\n"));
1503 invalidate_cm_connection(&domain->conn);
1505 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1506 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1507 if (!domain->internal && old_status) {
1508 set_domain_offline(domain);
1510 /* store partial response. */
1511 if (*num_entries > 0) {
1513 * humm, what about the status used for cache?
1514 * Should it be NT_STATUS_OK?
1516 break;
1519 * domain is offline now, and there is no user entries,
1520 * try to fetch from cache again.
1522 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1523 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1524 /* partial response... */
1525 if (!centry) {
1526 goto skip_save;
1527 } else {
1528 goto do_fetch_cache;
1530 } else {
1531 goto skip_save;
1535 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1536 (retry++ < 5));
1538 /* and save it */
1539 refresh_sequence_number(domain, false);
1540 if (!NT_STATUS_IS_OK(status)) {
1541 return status;
1543 centry = centry_start(domain, status);
1544 if (!centry)
1545 goto skip_save;
1546 centry_put_uint32(centry, *num_entries);
1547 for (i=0; i<(*num_entries); i++) {
1548 centry_put_string(centry, (*info)[i].acct_name);
1549 centry_put_string(centry, (*info)[i].full_name);
1550 centry_put_string(centry, (*info)[i].homedir);
1551 centry_put_string(centry, (*info)[i].shell);
1552 centry_put_sid(centry, &(*info)[i].user_sid);
1553 centry_put_sid(centry, &(*info)[i].group_sid);
1554 if (domain->backend && domain->backend->consistent) {
1555 /* when the backend is consistent we can pre-prime some mappings */
1556 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1557 domain->name,
1558 (*info)[i].acct_name,
1559 &(*info)[i].user_sid,
1560 SID_NAME_USER);
1561 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1562 &(*info)[i].user_sid,
1563 domain->name,
1564 (*info)[i].acct_name,
1565 SID_NAME_USER);
1566 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1569 centry_end(centry, "UL/%s", domain->name);
1570 centry_free(centry);
1572 skip_save:
1573 return status;
1576 /* list all domain groups */
1577 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1578 TALLOC_CTX *mem_ctx,
1579 uint32 *num_entries,
1580 struct wb_acct_info **info)
1582 struct winbind_cache *cache = get_cache(domain);
1583 struct cache_entry *centry = NULL;
1584 NTSTATUS status;
1585 unsigned int i;
1586 bool old_status;
1588 old_status = domain->online;
1589 if (!cache->tdb)
1590 goto do_query;
1592 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1593 if (!centry)
1594 goto do_query;
1596 do_fetch_cache:
1597 *num_entries = centry_uint32(centry);
1599 if (*num_entries == 0)
1600 goto do_cached;
1602 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1603 if (! (*info)) {
1604 smb_panic_fn("enum_dom_groups out of memory");
1606 for (i=0; i<(*num_entries); i++) {
1607 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1608 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1609 (*info)[i].rid = centry_uint32(centry);
1612 do_cached:
1613 status = centry->status;
1615 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1616 domain->name, nt_errstr(status) ));
1618 centry_free(centry);
1619 return status;
1621 do_query:
1622 *num_entries = 0;
1623 *info = NULL;
1625 /* Return status value returned by seq number check */
1627 if (!NT_STATUS_IS_OK(domain->last_status))
1628 return domain->last_status;
1630 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1631 domain->name ));
1633 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1635 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1636 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1637 if (!domain->internal && old_status) {
1638 set_domain_offline(domain);
1640 if (cache->tdb &&
1641 !domain->online &&
1642 !domain->internal &&
1643 old_status) {
1644 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1645 if (centry) {
1646 goto do_fetch_cache;
1650 /* and save it */
1651 refresh_sequence_number(domain, false);
1652 if (!NT_STATUS_IS_OK(status)) {
1653 return status;
1655 centry = centry_start(domain, status);
1656 if (!centry)
1657 goto skip_save;
1658 centry_put_uint32(centry, *num_entries);
1659 for (i=0; i<(*num_entries); i++) {
1660 centry_put_string(centry, (*info)[i].acct_name);
1661 centry_put_string(centry, (*info)[i].acct_desc);
1662 centry_put_uint32(centry, (*info)[i].rid);
1664 centry_end(centry, "GL/%s/domain", domain->name);
1665 centry_free(centry);
1667 skip_save:
1668 return status;
1671 /* list all domain groups */
1672 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1673 TALLOC_CTX *mem_ctx,
1674 uint32 *num_entries,
1675 struct wb_acct_info **info)
1677 struct winbind_cache *cache = get_cache(domain);
1678 struct cache_entry *centry = NULL;
1679 NTSTATUS status;
1680 unsigned int i;
1681 bool old_status;
1683 old_status = domain->online;
1684 if (!cache->tdb)
1685 goto do_query;
1687 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1688 if (!centry)
1689 goto do_query;
1691 do_fetch_cache:
1692 *num_entries = centry_uint32(centry);
1694 if (*num_entries == 0)
1695 goto do_cached;
1697 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1698 if (! (*info)) {
1699 smb_panic_fn("enum_dom_groups out of memory");
1701 for (i=0; i<(*num_entries); i++) {
1702 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1703 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1704 (*info)[i].rid = centry_uint32(centry);
1707 do_cached:
1709 /* If we are returning cached data and the domain controller
1710 is down then we don't know whether the data is up to date
1711 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1712 indicate this. */
1714 if (wcache_server_down(domain)) {
1715 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1716 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1717 } else
1718 status = centry->status;
1720 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1721 domain->name, nt_errstr(status) ));
1723 centry_free(centry);
1724 return status;
1726 do_query:
1727 *num_entries = 0;
1728 *info = NULL;
1730 /* Return status value returned by seq number check */
1732 if (!NT_STATUS_IS_OK(domain->last_status))
1733 return domain->last_status;
1735 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1736 domain->name ));
1738 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1740 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1741 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1742 if (!domain->internal && old_status) {
1743 set_domain_offline(domain);
1745 if (cache->tdb &&
1746 !domain->internal &&
1747 !domain->online &&
1748 old_status) {
1749 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1750 if (centry) {
1751 goto do_fetch_cache;
1755 /* and save it */
1756 refresh_sequence_number(domain, false);
1757 if (!NT_STATUS_IS_OK(status)) {
1758 return status;
1760 centry = centry_start(domain, status);
1761 if (!centry)
1762 goto skip_save;
1763 centry_put_uint32(centry, *num_entries);
1764 for (i=0; i<(*num_entries); i++) {
1765 centry_put_string(centry, (*info)[i].acct_name);
1766 centry_put_string(centry, (*info)[i].acct_desc);
1767 centry_put_uint32(centry, (*info)[i].rid);
1769 centry_end(centry, "GL/%s/local", domain->name);
1770 centry_free(centry);
1772 skip_save:
1773 return status;
1776 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1777 const char *domain_name,
1778 const char *name,
1779 struct dom_sid *sid,
1780 enum lsa_SidType *type)
1782 struct winbind_cache *cache = get_cache(domain);
1783 struct cache_entry *centry;
1784 NTSTATUS status;
1785 char *uname;
1787 if (cache->tdb == NULL) {
1788 return NT_STATUS_NOT_FOUND;
1791 uname = talloc_strdup_upper(talloc_tos(), name);
1792 if (uname == NULL) {
1793 return NT_STATUS_NO_MEMORY;
1796 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1797 TALLOC_FREE(uname);
1798 if (centry == NULL) {
1799 return NT_STATUS_NOT_FOUND;
1802 status = centry->status;
1803 if (NT_STATUS_IS_OK(status)) {
1804 *type = (enum lsa_SidType)centry_uint32(centry);
1805 centry_sid(centry, sid);
1808 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1809 "%s\n", domain->name, nt_errstr(status) ));
1811 centry_free(centry);
1812 return status;
1815 /* convert a single name to a sid in a domain */
1816 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1817 TALLOC_CTX *mem_ctx,
1818 const char *domain_name,
1819 const char *name,
1820 uint32_t flags,
1821 struct dom_sid *sid,
1822 enum lsa_SidType *type)
1824 NTSTATUS status;
1825 bool old_status;
1827 old_status = domain->online;
1829 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1830 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1831 return status;
1834 ZERO_STRUCTP(sid);
1836 /* If the seq number check indicated that there is a problem
1837 * with this DC, then return that status... except for
1838 * access_denied. This is special because the dc may be in
1839 * "restrict anonymous = 1" mode, in which case it will deny
1840 * most unauthenticated operations, but *will* allow the LSA
1841 * name-to-sid that we try as a fallback. */
1843 if (!(NT_STATUS_IS_OK(domain->last_status)
1844 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1845 return domain->last_status;
1847 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1848 domain->name ));
1850 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1851 name, flags, sid, type);
1853 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1854 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1855 if (!domain->internal && old_status) {
1856 set_domain_offline(domain);
1858 if (!domain->internal &&
1859 !domain->online &&
1860 old_status) {
1861 NTSTATUS cache_status;
1862 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1863 return cache_status;
1866 /* and save it */
1867 refresh_sequence_number(domain, false);
1869 if (domain->online &&
1870 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1871 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1873 /* Only save the reverse mapping if this was not a UPN */
1874 if (!strchr(name, '@')) {
1875 if (!strupper_m(discard_const_p(char, domain_name))) {
1876 return NT_STATUS_INVALID_PARAMETER;
1878 (void)strlower_m(discard_const_p(char, name));
1879 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1883 return status;
1886 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1887 const struct dom_sid *sid,
1888 TALLOC_CTX *mem_ctx,
1889 char **domain_name,
1890 char **name,
1891 enum lsa_SidType *type)
1893 struct winbind_cache *cache = get_cache(domain);
1894 struct cache_entry *centry;
1895 char *sid_string;
1896 NTSTATUS status;
1898 if (cache->tdb == NULL) {
1899 return NT_STATUS_NOT_FOUND;
1902 sid_string = sid_string_tos(sid);
1903 if (sid_string == NULL) {
1904 return NT_STATUS_NO_MEMORY;
1907 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1908 TALLOC_FREE(sid_string);
1909 if (centry == NULL) {
1910 return NT_STATUS_NOT_FOUND;
1913 if (NT_STATUS_IS_OK(centry->status)) {
1914 *type = (enum lsa_SidType)centry_uint32(centry);
1915 *domain_name = centry_string(centry, mem_ctx);
1916 *name = centry_string(centry, mem_ctx);
1919 status = centry->status;
1920 centry_free(centry);
1922 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1923 "%s\n", domain->name, nt_errstr(status) ));
1925 return status;
1928 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1929 given */
1930 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1931 TALLOC_CTX *mem_ctx,
1932 const struct dom_sid *sid,
1933 char **domain_name,
1934 char **name,
1935 enum lsa_SidType *type)
1937 NTSTATUS status;
1938 bool old_status;
1940 old_status = domain->online;
1941 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1942 type);
1943 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1944 return status;
1947 *name = NULL;
1948 *domain_name = NULL;
1950 /* If the seq number check indicated that there is a problem
1951 * with this DC, then return that status... except for
1952 * access_denied. This is special because the dc may be in
1953 * "restrict anonymous = 1" mode, in which case it will deny
1954 * most unauthenticated operations, but *will* allow the LSA
1955 * sid-to-name that we try as a fallback. */
1957 if (!(NT_STATUS_IS_OK(domain->last_status)
1958 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1959 return domain->last_status;
1961 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1962 domain->name ));
1964 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1966 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1967 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1968 if (!domain->internal && old_status) {
1969 set_domain_offline(domain);
1971 if (!domain->internal &&
1972 !domain->online &&
1973 old_status) {
1974 NTSTATUS cache_status;
1975 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1976 domain_name, name, type);
1977 return cache_status;
1980 /* and save it */
1981 refresh_sequence_number(domain, false);
1982 if (!NT_STATUS_IS_OK(status)) {
1983 return status;
1985 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1987 /* We can't save the name to sid mapping here, as with sid history a
1988 * later name2sid would give the wrong sid. */
1990 return status;
1993 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1994 TALLOC_CTX *mem_ctx,
1995 const struct dom_sid *domain_sid,
1996 uint32 *rids,
1997 size_t num_rids,
1998 char **domain_name,
1999 char ***names,
2000 enum lsa_SidType **types)
2002 struct winbind_cache *cache = get_cache(domain);
2003 size_t i;
2004 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2005 bool have_mapped;
2006 bool have_unmapped;
2007 bool old_status;
2009 old_status = domain->online;
2010 *domain_name = NULL;
2011 *names = NULL;
2012 *types = NULL;
2014 if (!cache->tdb) {
2015 goto do_query;
2018 if (num_rids == 0) {
2019 return NT_STATUS_OK;
2022 *names = talloc_array(mem_ctx, char *, num_rids);
2023 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2025 if ((*names == NULL) || (*types == NULL)) {
2026 result = NT_STATUS_NO_MEMORY;
2027 goto error;
2030 have_mapped = have_unmapped = false;
2032 for (i=0; i<num_rids; i++) {
2033 struct dom_sid sid;
2034 struct cache_entry *centry;
2035 fstring tmp;
2037 if (!sid_compose(&sid, domain_sid, rids[i])) {
2038 result = NT_STATUS_INTERNAL_ERROR;
2039 goto error;
2042 centry = wcache_fetch(cache, domain, "SN/%s",
2043 sid_to_fstring(tmp, &sid));
2044 if (!centry) {
2045 goto do_query;
2048 (*types)[i] = SID_NAME_UNKNOWN;
2049 (*names)[i] = talloc_strdup(*names, "");
2051 if (NT_STATUS_IS_OK(centry->status)) {
2052 char *dom;
2053 have_mapped = true;
2054 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2056 dom = centry_string(centry, mem_ctx);
2057 if (*domain_name == NULL) {
2058 *domain_name = dom;
2059 } else {
2060 talloc_free(dom);
2063 (*names)[i] = centry_string(centry, *names);
2065 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2066 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2067 have_unmapped = true;
2069 } else {
2070 /* something's definitely wrong */
2071 result = centry->status;
2072 goto error;
2075 centry_free(centry);
2078 if (!have_mapped) {
2079 return NT_STATUS_NONE_MAPPED;
2081 if (!have_unmapped) {
2082 return NT_STATUS_OK;
2084 return STATUS_SOME_UNMAPPED;
2086 do_query:
2088 TALLOC_FREE(*names);
2089 TALLOC_FREE(*types);
2091 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2092 rids, num_rids, domain_name,
2093 names, types);
2095 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2096 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2097 if (!domain->internal && old_status) {
2098 set_domain_offline(domain);
2100 if (cache->tdb &&
2101 !domain->internal &&
2102 !domain->online &&
2103 old_status) {
2104 have_mapped = have_unmapped = false;
2106 for (i=0; i<num_rids; i++) {
2107 struct dom_sid sid;
2108 struct cache_entry *centry;
2109 fstring tmp;
2111 if (!sid_compose(&sid, domain_sid, rids[i])) {
2112 result = NT_STATUS_INTERNAL_ERROR;
2113 goto error;
2116 centry = wcache_fetch(cache, domain, "SN/%s",
2117 sid_to_fstring(tmp, &sid));
2118 if (!centry) {
2119 (*types)[i] = SID_NAME_UNKNOWN;
2120 (*names)[i] = talloc_strdup(*names, "");
2121 continue;
2124 (*types)[i] = SID_NAME_UNKNOWN;
2125 (*names)[i] = talloc_strdup(*names, "");
2127 if (NT_STATUS_IS_OK(centry->status)) {
2128 char *dom;
2129 have_mapped = true;
2130 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2132 dom = centry_string(centry, mem_ctx);
2133 if (*domain_name == NULL) {
2134 *domain_name = dom;
2135 } else {
2136 talloc_free(dom);
2139 (*names)[i] = centry_string(centry, *names);
2141 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2142 have_unmapped = true;
2144 } else {
2145 /* something's definitely wrong */
2146 result = centry->status;
2147 centry_free(centry);
2148 goto error;
2151 centry_free(centry);
2154 if (!have_mapped) {
2155 return NT_STATUS_NONE_MAPPED;
2157 if (!have_unmapped) {
2158 return NT_STATUS_OK;
2160 return STATUS_SOME_UNMAPPED;
2164 None of the queried rids has been found so save all negative entries
2166 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2167 for (i = 0; i < num_rids; i++) {
2168 struct dom_sid sid;
2169 const char *name = "";
2170 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2171 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2173 if (!sid_compose(&sid, domain_sid, rids[i])) {
2174 return NT_STATUS_INTERNAL_ERROR;
2177 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2178 name, type);
2181 return result;
2185 Some or all of the queried rids have been found.
2187 if (!NT_STATUS_IS_OK(result) &&
2188 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2189 return result;
2192 refresh_sequence_number(domain, false);
2194 for (i=0; i<num_rids; i++) {
2195 struct dom_sid sid;
2196 NTSTATUS status;
2198 if (!sid_compose(&sid, domain_sid, rids[i])) {
2199 result = NT_STATUS_INTERNAL_ERROR;
2200 goto error;
2203 status = (*types)[i] == SID_NAME_UNKNOWN ?
2204 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2206 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2207 (*names)[i], (*types)[i]);
2210 return result;
2212 error:
2213 TALLOC_FREE(*names);
2214 TALLOC_FREE(*types);
2215 return result;
2218 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2219 TALLOC_CTX *mem_ctx,
2220 const struct dom_sid *user_sid,
2221 struct wbint_userinfo *info)
2223 struct winbind_cache *cache = get_cache(domain);
2224 struct cache_entry *centry = NULL;
2225 NTSTATUS status;
2226 char *sid_string;
2228 if (cache->tdb == NULL) {
2229 return NT_STATUS_NOT_FOUND;
2232 sid_string = sid_string_tos(user_sid);
2233 if (sid_string == NULL) {
2234 return NT_STATUS_NO_MEMORY;
2237 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2238 TALLOC_FREE(sid_string);
2239 if (centry == NULL) {
2240 return NT_STATUS_NOT_FOUND;
2244 * If we have an access denied cache entry and a cached info3
2245 * in the samlogon cache then do a query. This will force the
2246 * rpc back end to return the info3 data.
2249 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2250 netsamlogon_cache_have(user_sid)) {
2251 DEBUG(10, ("query_user: cached access denied and have cached "
2252 "info3\n"));
2253 domain->last_status = NT_STATUS_OK;
2254 centry_free(centry);
2255 return NT_STATUS_NOT_FOUND;
2258 /* if status is not ok then this is a negative hit
2259 and the rest of the data doesn't matter */
2260 status = centry->status;
2261 if (NT_STATUS_IS_OK(status)) {
2262 info->acct_name = centry_string(centry, mem_ctx);
2263 info->full_name = centry_string(centry, mem_ctx);
2264 info->homedir = centry_string(centry, mem_ctx);
2265 info->shell = centry_string(centry, mem_ctx);
2266 info->primary_gid = centry_uint32(centry);
2267 centry_sid(centry, &info->user_sid);
2268 centry_sid(centry, &info->group_sid);
2271 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2272 "%s\n", domain->name, nt_errstr(status) ));
2274 centry_free(centry);
2275 return status;
2278 /* Lookup user information from a rid */
2279 static NTSTATUS query_user(struct winbindd_domain *domain,
2280 TALLOC_CTX *mem_ctx,
2281 const struct dom_sid *user_sid,
2282 struct wbint_userinfo *info)
2284 NTSTATUS status;
2285 bool old_status;
2287 old_status = domain->online;
2288 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2289 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2290 return status;
2293 ZERO_STRUCTP(info);
2295 /* Return status value returned by seq number check */
2297 if (!NT_STATUS_IS_OK(domain->last_status))
2298 return domain->last_status;
2300 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2301 domain->name ));
2303 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2305 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2306 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2307 if (!domain->internal && old_status) {
2308 set_domain_offline(domain);
2310 if (!domain->internal &&
2311 !domain->online &&
2312 old_status) {
2313 NTSTATUS cache_status;
2314 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2315 return cache_status;
2318 /* and save it */
2319 refresh_sequence_number(domain, false);
2320 if (!NT_STATUS_IS_OK(status)) {
2321 return status;
2323 wcache_save_user(domain, status, info);
2325 return status;
2328 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2329 TALLOC_CTX *mem_ctx,
2330 const struct dom_sid *user_sid,
2331 uint32_t *pnum_sids,
2332 struct dom_sid **psids)
2334 struct winbind_cache *cache = get_cache(domain);
2335 struct cache_entry *centry = NULL;
2336 NTSTATUS status;
2337 uint32_t i, num_sids;
2338 struct dom_sid *sids;
2339 fstring sid_string;
2341 if (cache->tdb == NULL) {
2342 return NT_STATUS_NOT_FOUND;
2345 centry = wcache_fetch(cache, domain, "UG/%s",
2346 sid_to_fstring(sid_string, user_sid));
2347 if (centry == NULL) {
2348 return NT_STATUS_NOT_FOUND;
2351 /* If we have an access denied cache entry and a cached info3 in the
2352 samlogon cache then do a query. This will force the rpc back end
2353 to return the info3 data. */
2355 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2356 && netsamlogon_cache_have(user_sid)) {
2357 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2358 "cached info3\n"));
2359 domain->last_status = NT_STATUS_OK;
2360 centry_free(centry);
2361 return NT_STATUS_NOT_FOUND;
2364 num_sids = centry_uint32(centry);
2365 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2366 if (sids == NULL) {
2367 centry_free(centry);
2368 return NT_STATUS_NO_MEMORY;
2371 for (i=0; i<num_sids; i++) {
2372 centry_sid(centry, &sids[i]);
2375 status = centry->status;
2377 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2378 "status: %s\n", domain->name, nt_errstr(status)));
2380 centry_free(centry);
2382 *pnum_sids = num_sids;
2383 *psids = sids;
2384 return status;
2387 /* Lookup groups a user is a member of. */
2388 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2389 TALLOC_CTX *mem_ctx,
2390 const struct dom_sid *user_sid,
2391 uint32 *num_groups, struct dom_sid **user_gids)
2393 struct cache_entry *centry = NULL;
2394 NTSTATUS status;
2395 unsigned int i;
2396 fstring sid_string;
2397 bool old_status;
2399 old_status = domain->online;
2400 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2401 num_groups, user_gids);
2402 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2403 return status;
2406 (*num_groups) = 0;
2407 (*user_gids) = NULL;
2409 /* Return status value returned by seq number check */
2411 if (!NT_STATUS_IS_OK(domain->last_status))
2412 return domain->last_status;
2414 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2415 domain->name ));
2417 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2419 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2420 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2421 if (!domain->internal && old_status) {
2422 set_domain_offline(domain);
2424 if (!domain->internal &&
2425 !domain->online &&
2426 old_status) {
2427 NTSTATUS cache_status;
2428 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2429 num_groups, user_gids);
2430 return cache_status;
2433 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2434 goto skip_save;
2436 /* and save it */
2437 refresh_sequence_number(domain, false);
2438 if (!NT_STATUS_IS_OK(status)) {
2439 return status;
2441 centry = centry_start(domain, status);
2442 if (!centry)
2443 goto skip_save;
2445 centry_put_uint32(centry, *num_groups);
2446 for (i=0; i<(*num_groups); i++) {
2447 centry_put_sid(centry, &(*user_gids)[i]);
2450 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2451 centry_free(centry);
2453 skip_save:
2454 return status;
2457 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2458 const struct dom_sid *sids)
2460 uint32_t i;
2461 char *sidlist;
2463 sidlist = talloc_strdup(mem_ctx, "");
2464 if (sidlist == NULL) {
2465 return NULL;
2467 for (i=0; i<num_sids; i++) {
2468 fstring tmp;
2469 sidlist = talloc_asprintf_append_buffer(
2470 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2471 if (sidlist == NULL) {
2472 return NULL;
2475 return sidlist;
2478 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2479 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2480 const struct dom_sid *sids,
2481 uint32_t *pnum_aliases, uint32_t **paliases)
2483 struct winbind_cache *cache = get_cache(domain);
2484 struct cache_entry *centry = NULL;
2485 uint32_t num_aliases;
2486 uint32_t *aliases;
2487 NTSTATUS status;
2488 char *sidlist;
2489 int i;
2491 if (cache->tdb == NULL) {
2492 return NT_STATUS_NOT_FOUND;
2495 if (num_sids == 0) {
2496 *pnum_aliases = 0;
2497 *paliases = NULL;
2498 return NT_STATUS_OK;
2501 /* We need to cache indexed by the whole list of SIDs, the aliases
2502 * resulting might come from any of the SIDs. */
2504 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2505 if (sidlist == NULL) {
2506 return NT_STATUS_NO_MEMORY;
2509 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2510 TALLOC_FREE(sidlist);
2511 if (centry == NULL) {
2512 return NT_STATUS_NOT_FOUND;
2515 num_aliases = centry_uint32(centry);
2516 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2517 if (aliases == NULL) {
2518 centry_free(centry);
2519 return NT_STATUS_NO_MEMORY;
2522 for (i=0; i<num_aliases; i++) {
2523 aliases[i] = centry_uint32(centry);
2526 status = centry->status;
2528 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2529 "status %s\n", domain->name, nt_errstr(status)));
2531 centry_free(centry);
2533 *pnum_aliases = num_aliases;
2534 *paliases = aliases;
2536 return status;
2539 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2540 TALLOC_CTX *mem_ctx,
2541 uint32 num_sids, const struct dom_sid *sids,
2542 uint32 *num_aliases, uint32 **alias_rids)
2544 struct cache_entry *centry = NULL;
2545 NTSTATUS status;
2546 char *sidlist;
2547 int i;
2548 bool old_status;
2550 old_status = domain->online;
2551 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2552 num_aliases, alias_rids);
2553 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2554 return status;
2557 (*num_aliases) = 0;
2558 (*alias_rids) = NULL;
2560 if (!NT_STATUS_IS_OK(domain->last_status))
2561 return domain->last_status;
2563 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2564 "for domain %s\n", domain->name ));
2566 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2567 if (sidlist == NULL) {
2568 return NT_STATUS_NO_MEMORY;
2571 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2572 num_sids, sids,
2573 num_aliases, alias_rids);
2575 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2576 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2577 if (!domain->internal && old_status) {
2578 set_domain_offline(domain);
2580 if (!domain->internal &&
2581 !domain->online &&
2582 old_status) {
2583 NTSTATUS cache_status;
2584 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2585 sids, num_aliases, alias_rids);
2586 return cache_status;
2589 /* and save it */
2590 refresh_sequence_number(domain, false);
2591 if (!NT_STATUS_IS_OK(status)) {
2592 return status;
2594 centry = centry_start(domain, status);
2595 if (!centry)
2596 goto skip_save;
2597 centry_put_uint32(centry, *num_aliases);
2598 for (i=0; i<(*num_aliases); i++)
2599 centry_put_uint32(centry, (*alias_rids)[i]);
2600 centry_end(centry, "UA%s", sidlist);
2601 centry_free(centry);
2603 skip_save:
2604 return status;
2607 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2608 TALLOC_CTX *mem_ctx,
2609 const struct dom_sid *group_sid,
2610 uint32_t *num_names,
2611 struct dom_sid **sid_mem, char ***names,
2612 uint32_t **name_types)
2614 struct winbind_cache *cache = get_cache(domain);
2615 struct cache_entry *centry = NULL;
2616 NTSTATUS status;
2617 unsigned int i;
2618 char *sid_string;
2620 if (cache->tdb == NULL) {
2621 return NT_STATUS_NOT_FOUND;
2624 sid_string = sid_string_tos(group_sid);
2625 if (sid_string == NULL) {
2626 return NT_STATUS_NO_MEMORY;
2629 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2630 TALLOC_FREE(sid_string);
2631 if (centry == NULL) {
2632 return NT_STATUS_NOT_FOUND;
2635 *sid_mem = NULL;
2636 *names = NULL;
2637 *name_types = NULL;
2639 *num_names = centry_uint32(centry);
2640 if (*num_names == 0) {
2641 centry_free(centry);
2642 return NT_STATUS_OK;
2645 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2646 *names = talloc_array(mem_ctx, char *, *num_names);
2647 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2649 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2650 TALLOC_FREE(*sid_mem);
2651 TALLOC_FREE(*names);
2652 TALLOC_FREE(*name_types);
2653 centry_free(centry);
2654 return NT_STATUS_NO_MEMORY;
2657 for (i=0; i<(*num_names); i++) {
2658 centry_sid(centry, &(*sid_mem)[i]);
2659 (*names)[i] = centry_string(centry, mem_ctx);
2660 (*name_types)[i] = centry_uint32(centry);
2663 status = centry->status;
2665 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2666 "status: %s\n", domain->name, nt_errstr(status)));
2668 centry_free(centry);
2669 return status;
2672 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2673 TALLOC_CTX *mem_ctx,
2674 const struct dom_sid *group_sid,
2675 enum lsa_SidType type,
2676 uint32 *num_names,
2677 struct dom_sid **sid_mem, char ***names,
2678 uint32 **name_types)
2680 struct cache_entry *centry = NULL;
2681 NTSTATUS status;
2682 unsigned int i;
2683 fstring sid_string;
2684 bool old_status;
2686 old_status = domain->online;
2687 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2688 sid_mem, names, name_types);
2689 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2690 return status;
2693 (*num_names) = 0;
2694 (*sid_mem) = NULL;
2695 (*names) = NULL;
2696 (*name_types) = NULL;
2698 /* Return status value returned by seq number check */
2700 if (!NT_STATUS_IS_OK(domain->last_status))
2701 return domain->last_status;
2703 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2704 domain->name ));
2706 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2707 type, num_names,
2708 sid_mem, names, name_types);
2710 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2711 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2712 if (!domain->internal && old_status) {
2713 set_domain_offline(domain);
2715 if (!domain->internal &&
2716 !domain->online &&
2717 old_status) {
2718 NTSTATUS cache_status;
2719 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2720 num_names, sid_mem, names,
2721 name_types);
2722 return cache_status;
2725 /* and save it */
2726 refresh_sequence_number(domain, false);
2727 if (!NT_STATUS_IS_OK(status)) {
2728 return status;
2730 centry = centry_start(domain, status);
2731 if (!centry)
2732 goto skip_save;
2733 centry_put_uint32(centry, *num_names);
2734 for (i=0; i<(*num_names); i++) {
2735 centry_put_sid(centry, &(*sid_mem)[i]);
2736 centry_put_string(centry, (*names)[i]);
2737 centry_put_uint32(centry, (*name_types)[i]);
2739 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2740 centry_free(centry);
2742 skip_save:
2743 return status;
2746 /* find the sequence number for a domain */
2747 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2749 refresh_sequence_number(domain, false);
2751 *seq = domain->sequence_number;
2753 return NT_STATUS_OK;
2756 /* enumerate trusted domains
2757 * (we need to have the list of trustdoms in the cache when we go offline) -
2758 * Guenther */
2759 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2760 TALLOC_CTX *mem_ctx,
2761 struct netr_DomainTrustList *trusts)
2763 NTSTATUS status;
2764 struct winbind_cache *cache;
2765 struct winbindd_tdc_domain *dom_list = NULL;
2766 size_t num_domains = 0;
2767 bool retval = false;
2768 int i;
2769 bool old_status;
2771 old_status = domain->online;
2772 trusts->count = 0;
2773 trusts->array = NULL;
2775 cache = get_cache(domain);
2776 if (!cache || !cache->tdb) {
2777 goto do_query;
2780 if (domain->online) {
2781 goto do_query;
2784 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2785 if (!retval || !num_domains || !dom_list) {
2786 TALLOC_FREE(dom_list);
2787 goto do_query;
2790 do_fetch_cache:
2791 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2792 if (!trusts->array) {
2793 TALLOC_FREE(dom_list);
2794 return NT_STATUS_NO_MEMORY;
2797 for (i = 0; i < num_domains; i++) {
2798 struct netr_DomainTrust *trust;
2799 struct dom_sid *sid;
2800 struct winbindd_domain *dom;
2802 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2803 if (dom && dom->internal) {
2804 continue;
2807 trust = &trusts->array[trusts->count];
2808 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2809 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2810 sid = talloc(trusts->array, struct dom_sid);
2811 if (!trust->netbios_name || !trust->dns_name ||
2812 !sid) {
2813 TALLOC_FREE(dom_list);
2814 TALLOC_FREE(trusts->array);
2815 return NT_STATUS_NO_MEMORY;
2818 trust->trust_flags = dom_list[i].trust_flags;
2819 trust->trust_attributes = dom_list[i].trust_attribs;
2820 trust->trust_type = dom_list[i].trust_type;
2821 sid_copy(sid, &dom_list[i].sid);
2822 trust->sid = sid;
2823 trusts->count++;
2826 TALLOC_FREE(dom_list);
2827 return NT_STATUS_OK;
2829 do_query:
2830 /* Return status value returned by seq number check */
2832 if (!NT_STATUS_IS_OK(domain->last_status))
2833 return domain->last_status;
2835 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2836 domain->name ));
2838 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2840 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2841 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2842 if (!domain->internal && old_status) {
2843 set_domain_offline(domain);
2845 if (!domain->internal &&
2846 !domain->online &&
2847 old_status) {
2848 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2849 if (retval && num_domains && dom_list) {
2850 TALLOC_FREE(trusts->array);
2851 trusts->count = 0;
2852 goto do_fetch_cache;
2856 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2857 * so that the generic centry handling still applies correctly -
2858 * Guenther*/
2860 if (!NT_STATUS_IS_ERR(status)) {
2861 status = NT_STATUS_OK;
2863 return status;
2866 /* get lockout policy */
2867 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2868 TALLOC_CTX *mem_ctx,
2869 struct samr_DomInfo12 *policy)
2871 struct winbind_cache *cache = get_cache(domain);
2872 struct cache_entry *centry = NULL;
2873 NTSTATUS status;
2874 bool old_status;
2876 old_status = domain->online;
2877 if (!cache->tdb)
2878 goto do_query;
2880 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2882 if (!centry)
2883 goto do_query;
2885 do_fetch_cache:
2886 policy->lockout_duration = centry_nttime(centry);
2887 policy->lockout_window = centry_nttime(centry);
2888 policy->lockout_threshold = centry_uint16(centry);
2890 status = centry->status;
2892 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2893 domain->name, nt_errstr(status) ));
2895 centry_free(centry);
2896 return status;
2898 do_query:
2899 ZERO_STRUCTP(policy);
2901 /* Return status value returned by seq number check */
2903 if (!NT_STATUS_IS_OK(domain->last_status))
2904 return domain->last_status;
2906 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2907 domain->name ));
2909 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2911 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2912 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2913 if (!domain->internal && old_status) {
2914 set_domain_offline(domain);
2916 if (cache->tdb &&
2917 !domain->internal &&
2918 !domain->online &&
2919 old_status) {
2920 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2921 if (centry) {
2922 goto do_fetch_cache;
2926 /* and save it */
2927 refresh_sequence_number(domain, false);
2928 if (!NT_STATUS_IS_OK(status)) {
2929 return status;
2931 wcache_save_lockout_policy(domain, status, policy);
2933 return status;
2936 /* get password policy */
2937 static NTSTATUS password_policy(struct winbindd_domain *domain,
2938 TALLOC_CTX *mem_ctx,
2939 struct samr_DomInfo1 *policy)
2941 struct winbind_cache *cache = get_cache(domain);
2942 struct cache_entry *centry = NULL;
2943 NTSTATUS status;
2944 bool old_status;
2946 old_status = domain->online;
2947 if (!cache->tdb)
2948 goto do_query;
2950 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2952 if (!centry)
2953 goto do_query;
2955 do_fetch_cache:
2956 policy->min_password_length = centry_uint16(centry);
2957 policy->password_history_length = centry_uint16(centry);
2958 policy->password_properties = centry_uint32(centry);
2959 policy->max_password_age = centry_nttime(centry);
2960 policy->min_password_age = centry_nttime(centry);
2962 status = centry->status;
2964 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2965 domain->name, nt_errstr(status) ));
2967 centry_free(centry);
2968 return status;
2970 do_query:
2971 ZERO_STRUCTP(policy);
2973 /* Return status value returned by seq number check */
2975 if (!NT_STATUS_IS_OK(domain->last_status))
2976 return domain->last_status;
2978 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2979 domain->name ));
2981 status = domain->backend->password_policy(domain, mem_ctx, policy);
2983 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2984 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2985 if (!domain->internal && old_status) {
2986 set_domain_offline(domain);
2988 if (cache->tdb &&
2989 !domain->internal &&
2990 !domain->online &&
2991 old_status) {
2992 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2993 if (centry) {
2994 goto do_fetch_cache;
2998 /* and save it */
2999 refresh_sequence_number(domain, false);
3000 if (!NT_STATUS_IS_OK(status)) {
3001 return status;
3003 wcache_save_password_policy(domain, status, policy);
3005 return status;
3009 /* Invalidate cached user and group lists coherently */
3011 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3012 void *state)
3014 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3015 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3016 tdb_delete(the_tdb, kbuf);
3018 return 0;
3021 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3023 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3024 const struct dom_sid *sid)
3026 fstring key_str, sid_string;
3027 struct winbind_cache *cache;
3029 /* dont clear cached U/SID and UG/SID entries when we want to logon
3030 * offline - gd */
3032 if (lp_winbind_offline_logon()) {
3033 return;
3036 if (!domain)
3037 return;
3039 cache = get_cache(domain);
3041 if (!cache->tdb) {
3042 return;
3045 /* Clear U/SID cache entry */
3046 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3047 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048 tdb_delete(cache->tdb, string_tdb_data(key_str));
3050 /* Clear UG/SID cache entry */
3051 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3052 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3053 tdb_delete(cache->tdb, string_tdb_data(key_str));
3055 /* Samba/winbindd never needs this. */
3056 netsamlogon_clear_cached_user(sid);
3059 bool wcache_invalidate_cache(void)
3061 struct winbindd_domain *domain;
3063 for (domain = domain_list(); domain; domain = domain->next) {
3064 struct winbind_cache *cache = get_cache(domain);
3066 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3067 "entries for %s\n", domain->name));
3068 if (cache) {
3069 if (cache->tdb) {
3070 tdb_traverse(cache->tdb, traverse_fn, NULL);
3071 } else {
3072 return false;
3076 return true;
3079 bool wcache_invalidate_cache_noinit(void)
3081 struct winbindd_domain *domain;
3083 for (domain = domain_list(); domain; domain = domain->next) {
3084 struct winbind_cache *cache;
3086 /* Skip uninitialized domains. */
3087 if (!domain->initialized && !domain->internal) {
3088 continue;
3091 cache = get_cache(domain);
3093 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3094 "entries for %s\n", domain->name));
3095 if (cache) {
3096 if (cache->tdb) {
3097 tdb_traverse(cache->tdb, traverse_fn, NULL);
3099 * Flushing cache has nothing to with domains.
3100 * return here if we successfully flushed once.
3101 * To avoid unnecessary traversing the cache.
3103 return true;
3104 } else {
3105 return false;
3109 return true;
3112 bool init_wcache(void)
3114 if (wcache == NULL) {
3115 wcache = SMB_XMALLOC_P(struct winbind_cache);
3116 ZERO_STRUCTP(wcache);
3119 if (wcache->tdb != NULL)
3120 return true;
3122 /* when working offline we must not clear the cache on restart */
3123 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3124 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3125 TDB_INCOMPATIBLE_HASH |
3126 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3127 O_RDWR|O_CREAT, 0600);
3129 if (wcache->tdb == NULL) {
3130 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3131 return false;
3134 return true;
3137 /************************************************************************
3138 This is called by the parent to initialize the cache file.
3139 We don't need sophisticated locking here as we know we're the
3140 only opener.
3141 ************************************************************************/
3143 bool initialize_winbindd_cache(void)
3145 bool cache_bad = true;
3146 uint32 vers;
3148 if (!init_wcache()) {
3149 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3150 return false;
3153 /* Check version number. */
3154 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3155 vers == WINBINDD_CACHE_VERSION) {
3156 cache_bad = false;
3159 if (cache_bad) {
3160 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3161 "and re-creating with version number %d\n",
3162 WINBINDD_CACHE_VERSION ));
3164 tdb_close(wcache->tdb);
3165 wcache->tdb = NULL;
3167 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3168 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3169 state_path("winbindd_cache.tdb"),
3170 strerror(errno) ));
3171 return false;
3173 if (!init_wcache()) {
3174 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3175 "init_wcache failed.\n"));
3176 return false;
3179 /* Write the version. */
3180 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3181 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3182 tdb_errorstr_compat(wcache->tdb) ));
3183 return false;
3187 tdb_close(wcache->tdb);
3188 wcache->tdb = NULL;
3189 return true;
3192 void close_winbindd_cache(void)
3194 if (!wcache) {
3195 return;
3197 if (wcache->tdb) {
3198 tdb_close(wcache->tdb);
3199 wcache->tdb = NULL;
3203 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3204 char **domain_name, char **name,
3205 enum lsa_SidType *type)
3207 struct winbindd_domain *domain;
3208 NTSTATUS status;
3210 domain = find_lookup_domain_from_sid(sid);
3211 if (domain == NULL) {
3212 return false;
3214 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3215 type);
3216 return NT_STATUS_IS_OK(status);
3219 bool lookup_cached_name(const char *domain_name,
3220 const char *name,
3221 struct dom_sid *sid,
3222 enum lsa_SidType *type)
3224 struct winbindd_domain *domain;
3225 NTSTATUS status;
3226 bool original_online_state;
3228 domain = find_lookup_domain_from_name(domain_name);
3229 if (domain == NULL) {
3230 return false;
3233 /* If we are doing a cached logon, temporarily set the domain
3234 offline so the cache won't expire the entry */
3236 original_online_state = domain->online;
3237 domain->online = false;
3238 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3239 domain->online = original_online_state;
3241 return NT_STATUS_IS_OK(status);
3244 void cache_name2sid(struct winbindd_domain *domain,
3245 const char *domain_name, const char *name,
3246 enum lsa_SidType type, const struct dom_sid *sid)
3248 refresh_sequence_number(domain, false);
3249 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3250 sid, type);
3254 * The original idea that this cache only contains centries has
3255 * been blurred - now other stuff gets put in here. Ensure we
3256 * ignore these things on cleanup.
3259 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3260 TDB_DATA dbuf, void *state)
3262 struct cache_entry *centry;
3264 if (is_non_centry_key(kbuf)) {
3265 return 0;
3268 centry = wcache_fetch_raw((char *)kbuf.dptr);
3269 if (!centry) {
3270 return 0;
3273 if (!NT_STATUS_IS_OK(centry->status)) {
3274 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3275 tdb_delete(the_tdb, kbuf);
3278 centry_free(centry);
3279 return 0;
3282 /* flush the cache */
3283 void wcache_flush_cache(void)
3285 if (!wcache)
3286 return;
3287 if (wcache->tdb) {
3288 tdb_close(wcache->tdb);
3289 wcache->tdb = NULL;
3291 if (!winbindd_use_cache()) {
3292 return;
3295 /* when working offline we must not clear the cache on restart */
3296 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3297 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3298 TDB_INCOMPATIBLE_HASH |
3299 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3300 O_RDWR|O_CREAT, 0600);
3302 if (!wcache->tdb) {
3303 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3304 return;
3307 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3309 DEBUG(10,("wcache_flush_cache success\n"));
3312 /* Count cached creds */
3314 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3315 void *state)
3317 int *cred_count = (int*)state;
3319 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3320 (*cred_count)++;
3322 return 0;
3325 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3327 struct winbind_cache *cache = get_cache(domain);
3329 *count = 0;
3331 if (!cache->tdb) {
3332 return NT_STATUS_INTERNAL_DB_ERROR;
3335 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3337 return NT_STATUS_OK;
3340 struct cred_list {
3341 struct cred_list *prev, *next;
3342 TDB_DATA key;
3343 fstring name;
3344 time_t created;
3346 static struct cred_list *wcache_cred_list;
3348 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3349 void *state)
3351 struct cred_list *cred;
3353 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3355 cred = SMB_MALLOC_P(struct cred_list);
3356 if (cred == NULL) {
3357 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3358 return -1;
3361 ZERO_STRUCTP(cred);
3363 /* save a copy of the key */
3365 fstrcpy(cred->name, (const char *)kbuf.dptr);
3366 DLIST_ADD(wcache_cred_list, cred);
3369 return 0;
3372 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3374 struct winbind_cache *cache = get_cache(domain);
3375 NTSTATUS status;
3376 int ret;
3377 struct cred_list *cred, *oldest = NULL;
3379 if (!cache->tdb) {
3380 return NT_STATUS_INTERNAL_DB_ERROR;
3383 /* we possibly already have an entry */
3384 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3386 fstring key_str, tmp;
3388 DEBUG(11,("we already have an entry, deleting that\n"));
3390 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3392 tdb_delete(cache->tdb, string_tdb_data(key_str));
3394 return NT_STATUS_OK;
3397 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3398 if (ret == 0) {
3399 return NT_STATUS_OK;
3400 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3401 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3404 ZERO_STRUCTP(oldest);
3406 for (cred = wcache_cred_list; cred; cred = cred->next) {
3408 TDB_DATA data;
3409 time_t t;
3411 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3412 if (!data.dptr) {
3413 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3414 cred->name));
3415 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3416 goto done;
3419 t = IVAL(data.dptr, 0);
3420 SAFE_FREE(data.dptr);
3422 if (!oldest) {
3423 oldest = SMB_MALLOC_P(struct cred_list);
3424 if (oldest == NULL) {
3425 status = NT_STATUS_NO_MEMORY;
3426 goto done;
3429 fstrcpy(oldest->name, cred->name);
3430 oldest->created = t;
3431 continue;
3434 if (t < oldest->created) {
3435 fstrcpy(oldest->name, cred->name);
3436 oldest->created = t;
3440 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3441 status = NT_STATUS_OK;
3442 } else {
3443 status = NT_STATUS_UNSUCCESSFUL;
3445 done:
3446 SAFE_FREE(wcache_cred_list);
3447 SAFE_FREE(oldest);
3449 return status;
3452 /* Change the global online/offline state. */
3453 bool set_global_winbindd_state_offline(void)
3455 TDB_DATA data;
3457 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3459 /* Only go offline if someone has created
3460 the key "WINBINDD_OFFLINE" in the cache tdb. */
3462 if (wcache == NULL || wcache->tdb == NULL) {
3463 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3464 return false;
3467 if (!lp_winbind_offline_logon()) {
3468 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3469 return false;
3472 if (global_winbindd_offline_state) {
3473 /* Already offline. */
3474 return true;
3477 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3479 if (!data.dptr || data.dsize != 4) {
3480 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3481 SAFE_FREE(data.dptr);
3482 return false;
3483 } else {
3484 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3485 global_winbindd_offline_state = true;
3486 SAFE_FREE(data.dptr);
3487 return true;
3491 void set_global_winbindd_state_online(void)
3493 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3495 if (!lp_winbind_offline_logon()) {
3496 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3497 return;
3500 if (!global_winbindd_offline_state) {
3501 /* Already online. */
3502 return;
3504 global_winbindd_offline_state = false;
3506 if (!wcache->tdb) {
3507 return;
3510 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3511 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3514 bool get_global_winbindd_state_offline(void)
3516 return global_winbindd_offline_state;
3519 /***********************************************************************
3520 Validate functions for all possible cache tdb keys.
3521 ***********************************************************************/
3523 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3524 struct tdb_validation_status *state)
3526 struct cache_entry *centry;
3528 centry = SMB_XMALLOC_P(struct cache_entry);
3529 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3530 if (!centry->data) {
3531 SAFE_FREE(centry);
3532 return NULL;
3534 centry->len = data.dsize;
3535 centry->ofs = 0;
3537 if (centry->len < 16) {
3538 /* huh? corrupt cache? */
3539 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3540 "(len < 16) ?\n", kstr));
3541 centry_free(centry);
3542 state->bad_entry = true;
3543 state->success = false;
3544 return NULL;
3547 centry->status = NT_STATUS(centry_uint32(centry));
3548 centry->sequence_number = centry_uint32(centry);
3549 centry->timeout = centry_uint64_t(centry);
3550 return centry;
3553 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3554 struct tdb_validation_status *state)
3556 if (dbuf.dsize != 8) {
3557 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3558 keystr, (unsigned int)dbuf.dsize ));
3559 state->bad_entry = true;
3560 return 1;
3562 return 0;
3565 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3566 struct tdb_validation_status *state)
3568 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3569 if (!centry) {
3570 return 1;
3573 (void)centry_uint32(centry);
3574 if (NT_STATUS_IS_OK(centry->status)) {
3575 struct dom_sid sid;
3576 (void)centry_sid(centry, &sid);
3579 centry_free(centry);
3581 if (!(state->success)) {
3582 return 1;
3584 DEBUG(10,("validate_ns: %s ok\n", keystr));
3585 return 0;
3588 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3589 struct tdb_validation_status *state)
3591 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3592 if (!centry) {
3593 return 1;
3596 if (NT_STATUS_IS_OK(centry->status)) {
3597 (void)centry_uint32(centry);
3598 (void)centry_string(centry, mem_ctx);
3599 (void)centry_string(centry, mem_ctx);
3602 centry_free(centry);
3604 if (!(state->success)) {
3605 return 1;
3607 DEBUG(10,("validate_sn: %s ok\n", keystr));
3608 return 0;
3611 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3612 struct tdb_validation_status *state)
3614 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3615 struct dom_sid sid;
3617 if (!centry) {
3618 return 1;
3621 (void)centry_string(centry, mem_ctx);
3622 (void)centry_string(centry, mem_ctx);
3623 (void)centry_string(centry, mem_ctx);
3624 (void)centry_string(centry, mem_ctx);
3625 (void)centry_uint32(centry);
3626 (void)centry_sid(centry, &sid);
3627 (void)centry_sid(centry, &sid);
3629 centry_free(centry);
3631 if (!(state->success)) {
3632 return 1;
3634 DEBUG(10,("validate_u: %s ok\n", keystr));
3635 return 0;
3638 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3639 struct tdb_validation_status *state)
3641 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3643 if (!centry) {
3644 return 1;
3647 (void)centry_nttime(centry);
3648 (void)centry_nttime(centry);
3649 (void)centry_uint16(centry);
3651 centry_free(centry);
3653 if (!(state->success)) {
3654 return 1;
3656 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3657 return 0;
3660 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3661 struct tdb_validation_status *state)
3663 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3665 if (!centry) {
3666 return 1;
3669 (void)centry_uint16(centry);
3670 (void)centry_uint16(centry);
3671 (void)centry_uint32(centry);
3672 (void)centry_nttime(centry);
3673 (void)centry_nttime(centry);
3675 centry_free(centry);
3677 if (!(state->success)) {
3678 return 1;
3680 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3681 return 0;
3684 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3685 struct tdb_validation_status *state)
3687 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3689 if (!centry) {
3690 return 1;
3693 (void)centry_time(centry);
3694 (void)centry_hash16(centry, mem_ctx);
3696 /* We only have 17 bytes more data in the salted cred case. */
3697 if (centry->len - centry->ofs == 17) {
3698 (void)centry_hash16(centry, mem_ctx);
3701 centry_free(centry);
3703 if (!(state->success)) {
3704 return 1;
3706 DEBUG(10,("validate_cred: %s ok\n", keystr));
3707 return 0;
3710 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3711 struct tdb_validation_status *state)
3713 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3714 int32 num_entries, i;
3716 if (!centry) {
3717 return 1;
3720 num_entries = (int32)centry_uint32(centry);
3722 for (i=0; i< num_entries; i++) {
3723 struct dom_sid sid;
3724 (void)centry_string(centry, mem_ctx);
3725 (void)centry_string(centry, mem_ctx);
3726 (void)centry_string(centry, mem_ctx);
3727 (void)centry_string(centry, mem_ctx);
3728 (void)centry_sid(centry, &sid);
3729 (void)centry_sid(centry, &sid);
3732 centry_free(centry);
3734 if (!(state->success)) {
3735 return 1;
3737 DEBUG(10,("validate_ul: %s ok\n", keystr));
3738 return 0;
3741 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3742 struct tdb_validation_status *state)
3744 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3745 int32 num_entries, i;
3747 if (!centry) {
3748 return 1;
3751 num_entries = centry_uint32(centry);
3753 for (i=0; i< num_entries; i++) {
3754 (void)centry_string(centry, mem_ctx);
3755 (void)centry_string(centry, mem_ctx);
3756 (void)centry_uint32(centry);
3759 centry_free(centry);
3761 if (!(state->success)) {
3762 return 1;
3764 DEBUG(10,("validate_gl: %s ok\n", keystr));
3765 return 0;
3768 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3769 struct tdb_validation_status *state)
3771 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3772 int32 num_groups, i;
3774 if (!centry) {
3775 return 1;
3778 num_groups = centry_uint32(centry);
3780 for (i=0; i< num_groups; i++) {
3781 struct dom_sid sid;
3782 centry_sid(centry, &sid);
3785 centry_free(centry);
3787 if (!(state->success)) {
3788 return 1;
3790 DEBUG(10,("validate_ug: %s ok\n", keystr));
3791 return 0;
3794 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3795 struct tdb_validation_status *state)
3797 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3798 int32 num_aliases, i;
3800 if (!centry) {
3801 return 1;
3804 num_aliases = centry_uint32(centry);
3806 for (i=0; i < num_aliases; i++) {
3807 (void)centry_uint32(centry);
3810 centry_free(centry);
3812 if (!(state->success)) {
3813 return 1;
3815 DEBUG(10,("validate_ua: %s ok\n", keystr));
3816 return 0;
3819 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3820 struct tdb_validation_status *state)
3822 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3823 int32 num_names, i;
3825 if (!centry) {
3826 return 1;
3829 num_names = centry_uint32(centry);
3831 for (i=0; i< num_names; i++) {
3832 struct dom_sid sid;
3833 centry_sid(centry, &sid);
3834 (void)centry_string(centry, mem_ctx);
3835 (void)centry_uint32(centry);
3838 centry_free(centry);
3840 if (!(state->success)) {
3841 return 1;
3843 DEBUG(10,("validate_gm: %s ok\n", keystr));
3844 return 0;
3847 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3848 struct tdb_validation_status *state)
3850 /* Can't say anything about this other than must be nonzero. */
3851 if (dbuf.dsize == 0) {
3852 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3853 keystr));
3854 state->bad_entry = true;
3855 state->success = false;
3856 return 1;
3859 DEBUG(10,("validate_dr: %s ok\n", keystr));
3860 return 0;
3863 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3864 struct tdb_validation_status *state)
3866 /* Can't say anything about this other than must be nonzero. */
3867 if (dbuf.dsize == 0) {
3868 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3869 keystr));
3870 state->bad_entry = true;
3871 state->success = false;
3872 return 1;
3875 DEBUG(10,("validate_de: %s ok\n", keystr));
3876 return 0;
3879 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3880 TDB_DATA dbuf, struct tdb_validation_status *state)
3882 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3884 if (!centry) {
3885 return 1;
3888 (void)centry_string(centry, mem_ctx);
3889 (void)centry_string(centry, mem_ctx);
3890 (void)centry_string(centry, mem_ctx);
3891 (void)centry_uint32(centry);
3893 centry_free(centry);
3895 if (!(state->success)) {
3896 return 1;
3898 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3899 return 0;
3902 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3903 TDB_DATA dbuf,
3904 struct tdb_validation_status *state)
3906 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3908 if (!centry) {
3909 return 1;
3912 (void)centry_string( centry, mem_ctx );
3914 centry_free(centry);
3916 if (!(state->success)) {
3917 return 1;
3919 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3920 return 0;
3923 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3924 TDB_DATA dbuf,
3925 struct tdb_validation_status *state)
3927 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3929 if (!centry) {
3930 return 1;
3933 (void)centry_string( centry, mem_ctx );
3935 centry_free(centry);
3937 if (!(state->success)) {
3938 return 1;
3940 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3941 return 0;
3944 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3945 TDB_DATA dbuf,
3946 struct tdb_validation_status *state)
3948 if (dbuf.dsize == 0) {
3949 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3950 "key %s (len ==0) ?\n", keystr));
3951 state->bad_entry = true;
3952 state->success = false;
3953 return 1;
3956 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3957 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3958 return 0;
3961 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3962 struct tdb_validation_status *state)
3964 if (dbuf.dsize != 4) {
3965 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3966 keystr, (unsigned int)dbuf.dsize ));
3967 state->bad_entry = true;
3968 state->success = false;
3969 return 1;
3971 DEBUG(10,("validate_offline: %s ok\n", keystr));
3972 return 0;
3975 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3976 struct tdb_validation_status *state)
3979 * Ignore validation for now. The proper way to do this is with a
3980 * checksum. Just pure parsing does not really catch much.
3982 return 0;
3985 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3986 struct tdb_validation_status *state)
3988 if (dbuf.dsize != 4) {
3989 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3990 "key %s (len %u != 4) ?\n",
3991 keystr, (unsigned int)dbuf.dsize));
3992 state->bad_entry = true;
3993 state->success = false;
3994 return 1;
3997 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3998 return 0;
4001 /***********************************************************************
4002 A list of all possible cache tdb keys with associated validation
4003 functions.
4004 ***********************************************************************/
4006 struct key_val_struct {
4007 const char *keyname;
4008 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4009 } key_val[] = {
4010 {"SEQNUM/", validate_seqnum},
4011 {"NS/", validate_ns},
4012 {"SN/", validate_sn},
4013 {"U/", validate_u},
4014 {"LOC_POL/", validate_loc_pol},
4015 {"PWD_POL/", validate_pwd_pol},
4016 {"CRED/", validate_cred},
4017 {"UL/", validate_ul},
4018 {"GL/", validate_gl},
4019 {"UG/", validate_ug},
4020 {"UA", validate_ua},
4021 {"GM/", validate_gm},
4022 {"DR/", validate_dr},
4023 {"DE/", validate_de},
4024 {"NSS/PWINFO/", validate_pwinfo},
4025 {"TRUSTDOMCACHE/", validate_trustdomcache},
4026 {"NSS/NA/", validate_nss_na},
4027 {"NSS/AN/", validate_nss_an},
4028 {"WINBINDD_OFFLINE", validate_offline},
4029 {"NDR/", validate_ndr},
4030 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4031 {NULL, NULL}
4034 /***********************************************************************
4035 Function to look at every entry in the tdb and validate it as far as
4036 possible.
4037 ***********************************************************************/
4039 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4041 int i;
4042 unsigned int max_key_len = 1024;
4043 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4045 /* Paranoia check. */
4046 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4047 max_key_len = 1024 * 1024;
4049 if (kbuf.dsize > max_key_len) {
4050 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4051 "(%u) > (%u)\n\n",
4052 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4053 return 1;
4056 for (i = 0; key_val[i].keyname; i++) {
4057 size_t namelen = strlen(key_val[i].keyname);
4058 if (kbuf.dsize >= namelen && (
4059 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4060 TALLOC_CTX *mem_ctx;
4061 char *keystr;
4062 int ret;
4064 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4065 if (!keystr) {
4066 return 1;
4068 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4069 keystr[kbuf.dsize] = '\0';
4071 mem_ctx = talloc_init("validate_ctx");
4072 if (!mem_ctx) {
4073 SAFE_FREE(keystr);
4074 return 1;
4077 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4078 v_state);
4080 SAFE_FREE(keystr);
4081 talloc_destroy(mem_ctx);
4082 return ret;
4086 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4087 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4088 DEBUG(0,("data :\n"));
4089 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4090 v_state->unknown_key = true;
4091 v_state->success = false;
4092 return 1; /* terminate. */
4095 static void validate_panic(const char *const why)
4097 DEBUG(0,("validating cache: would panic %s\n", why ));
4098 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4099 exit(47);
4102 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4103 TDB_DATA key,
4104 TDB_DATA data,
4105 void *state)
4107 uint64_t ctimeout;
4108 TDB_DATA blob;
4110 if (is_non_centry_key(key)) {
4111 return 0;
4114 if (data.dptr == NULL || data.dsize == 0) {
4115 if (tdb_delete(tdb, key) < 0) {
4116 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4117 key.dptr));
4118 return 1;
4122 /* add timeout to blob (uint64_t) */
4123 blob.dsize = data.dsize + 8;
4125 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4126 if (blob.dptr == NULL) {
4127 return 1;
4129 memset(blob.dptr, 0, blob.dsize);
4131 /* copy status and seqnum */
4132 memcpy(blob.dptr, data.dptr, 8);
4134 /* add timeout */
4135 ctimeout = lp_winbind_cache_time() + time(NULL);
4136 SBVAL(blob.dptr, 8, ctimeout);
4138 /* copy the rest */
4139 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4141 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4142 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4143 key.dptr));
4144 SAFE_FREE(blob.dptr);
4145 return 1;
4148 SAFE_FREE(blob.dptr);
4149 return 0;
4152 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4154 int rc;
4156 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4158 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4159 if (rc < 0) {
4160 return false;
4163 return true;
4166 /***********************************************************************
4167 Try and validate every entry in the winbindd cache. If we fail here,
4168 delete the cache tdb and return non-zero.
4169 ***********************************************************************/
4171 int winbindd_validate_cache(void)
4173 int ret = -1;
4174 const char *tdb_path = state_path("winbindd_cache.tdb");
4175 TDB_CONTEXT *tdb = NULL;
4176 uint32_t vers_id;
4177 bool ok;
4179 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4180 smb_panic_fn = validate_panic;
4182 tdb = tdb_open_log(tdb_path,
4183 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4184 TDB_INCOMPATIBLE_HASH |
4185 ( lp_winbind_offline_logon()
4186 ? TDB_DEFAULT
4187 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4188 O_RDWR|O_CREAT,
4189 0600);
4190 if (!tdb) {
4191 DEBUG(0, ("winbindd_validate_cache: "
4192 "error opening/initializing tdb\n"));
4193 goto done;
4196 /* Version check and upgrade code. */
4197 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4198 DEBUG(10, ("Fresh database\n"));
4199 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4200 vers_id = WINBINDD_CACHE_VERSION;
4203 if (vers_id != WINBINDD_CACHE_VERSION) {
4204 if (vers_id == WINBINDD_CACHE_VER1) {
4205 ok = wbcache_upgrade_v1_to_v2(tdb);
4206 if (!ok) {
4207 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4208 unlink(tdb_path);
4209 goto done;
4212 tdb_store_uint32(tdb,
4213 WINBINDD_CACHE_VERSION_KEYSTR,
4214 WINBINDD_CACHE_VERSION);
4215 vers_id = WINBINDD_CACHE_VER2;
4219 tdb_close(tdb);
4221 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4223 if (ret != 0) {
4224 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4225 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4226 unlink(tdb_path);
4229 done:
4230 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4231 smb_panic_fn = smb_panic;
4232 return ret;
4235 /***********************************************************************
4236 Try and validate every entry in the winbindd cache.
4237 ***********************************************************************/
4239 int winbindd_validate_cache_nobackup(void)
4241 int ret = -1;
4242 const char *tdb_path = state_path("winbindd_cache.tdb");
4244 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4245 smb_panic_fn = validate_panic;
4248 if (wcache == NULL || wcache->tdb == NULL) {
4249 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4250 } else {
4251 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4254 if (ret != 0) {
4255 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4256 "successful.\n"));
4259 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4260 "function\n"));
4261 smb_panic_fn = smb_panic;
4262 return ret;
4265 bool winbindd_cache_validate_and_initialize(void)
4267 close_winbindd_cache();
4269 if (lp_winbind_offline_logon()) {
4270 if (winbindd_validate_cache() < 0) {
4271 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4272 "could be restored.\n"));
4276 return initialize_winbindd_cache();
4279 /*********************************************************************
4280 ********************************************************************/
4282 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4283 struct winbindd_tdc_domain **domains,
4284 size_t *num_domains )
4286 struct winbindd_tdc_domain *list = NULL;
4287 size_t idx;
4288 int i;
4289 bool set_only = false;
4291 /* don't allow duplicates */
4293 idx = *num_domains;
4294 list = *domains;
4296 for ( i=0; i< (*num_domains); i++ ) {
4297 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4298 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4299 new_dom->name));
4300 idx = i;
4301 set_only = true;
4303 break;
4307 if ( !set_only ) {
4308 if ( !*domains ) {
4309 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4310 idx = 0;
4311 } else {
4312 list = talloc_realloc( *domains, *domains,
4313 struct winbindd_tdc_domain,
4314 (*num_domains)+1);
4315 idx = *num_domains;
4318 ZERO_STRUCT( list[idx] );
4321 if ( !list )
4322 return false;
4324 list[idx].domain_name = talloc_strdup(list, new_dom->name);
4325 if (list[idx].domain_name == NULL) {
4326 return false;
4328 if (new_dom->alt_name != NULL) {
4329 list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
4330 if (list[idx].dns_name == NULL) {
4331 return false;
4335 if ( !is_null_sid( &new_dom->sid ) ) {
4336 sid_copy( &list[idx].sid, &new_dom->sid );
4337 } else {
4338 sid_copy(&list[idx].sid, &global_sid_NULL);
4341 if ( new_dom->domain_flags != 0x0 )
4342 list[idx].trust_flags = new_dom->domain_flags;
4344 if ( new_dom->domain_type != 0x0 )
4345 list[idx].trust_type = new_dom->domain_type;
4347 if ( new_dom->domain_trust_attribs != 0x0 )
4348 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4350 if ( !set_only ) {
4351 *domains = list;
4352 *num_domains = idx + 1;
4355 return true;
4358 /*********************************************************************
4359 ********************************************************************/
4361 static TDB_DATA make_tdc_key( const char *domain_name )
4363 char *keystr = NULL;
4364 TDB_DATA key = { NULL, 0 };
4366 if ( !domain_name ) {
4367 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4368 return key;
4371 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4372 return key;
4374 key = string_term_tdb_data(keystr);
4376 return key;
4379 /*********************************************************************
4380 ********************************************************************/
4382 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4383 size_t num_domains,
4384 unsigned char **buf )
4386 unsigned char *buffer = NULL;
4387 int len = 0;
4388 int buflen = 0;
4389 int i = 0;
4391 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4392 (int)num_domains));
4394 buflen = 0;
4396 again:
4397 len = 0;
4399 /* Store the number of array items first */
4400 len += tdb_pack( buffer+len, buflen-len, "d",
4401 num_domains );
4403 /* now pack each domain trust record */
4404 for ( i=0; i<num_domains; i++ ) {
4406 fstring tmp;
4408 if ( buflen > 0 ) {
4409 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4410 domains[i].domain_name,
4411 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4414 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4415 domains[i].domain_name,
4416 domains[i].dns_name ? domains[i].dns_name : "",
4417 sid_to_fstring(tmp, &domains[i].sid),
4418 domains[i].trust_flags,
4419 domains[i].trust_attribs,
4420 domains[i].trust_type );
4423 if ( buflen < len ) {
4424 SAFE_FREE(buffer);
4425 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4426 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4427 buflen = -1;
4428 goto done;
4430 buflen = len;
4431 goto again;
4434 *buf = buffer;
4436 done:
4437 return buflen;
4440 /*********************************************************************
4441 ********************************************************************/
4443 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4444 struct winbindd_tdc_domain **domains )
4446 fstring domain_name, dns_name, sid_string;
4447 uint32 type, attribs, flags;
4448 int num_domains;
4449 int len = 0;
4450 int i;
4451 struct winbindd_tdc_domain *list = NULL;
4453 /* get the number of domains */
4454 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4455 if ( len == -1 ) {
4456 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4457 return 0;
4460 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4461 if ( !list ) {
4462 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4463 return 0;
4466 for ( i=0; i<num_domains; i++ ) {
4467 int this_len;
4469 this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
4470 domain_name,
4471 dns_name,
4472 sid_string,
4473 &flags,
4474 &attribs,
4475 &type );
4477 if ( this_len == -1 ) {
4478 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4479 TALLOC_FREE( list );
4480 return 0;
4482 len += this_len;
4484 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4485 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4486 domain_name, dns_name, sid_string,
4487 flags, attribs, type));
4489 list[i].domain_name = talloc_strdup( list, domain_name );
4490 list[i].dns_name = NULL;
4491 if (dns_name[0] != '\0') {
4492 list[i].dns_name = talloc_strdup(list, dns_name);
4494 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4495 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4496 domain_name));
4498 list[i].trust_flags = flags;
4499 list[i].trust_attribs = attribs;
4500 list[i].trust_type = type;
4503 *domains = list;
4505 return num_domains;
4508 /*********************************************************************
4509 ********************************************************************/
4511 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4513 TDB_DATA key = make_tdc_key( lp_workgroup() );
4514 TDB_DATA data = { NULL, 0 };
4515 int ret;
4517 if ( !key.dptr )
4518 return false;
4520 /* See if we were asked to delete the cache entry */
4522 if ( !domains ) {
4523 ret = tdb_delete( wcache->tdb, key );
4524 goto done;
4527 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4529 if ( !data.dptr ) {
4530 ret = -1;
4531 goto done;
4534 ret = tdb_store( wcache->tdb, key, data, 0 );
4536 done:
4537 SAFE_FREE( data.dptr );
4538 SAFE_FREE( key.dptr );
4540 return ( ret == 0 );
4543 /*********************************************************************
4544 ********************************************************************/
4546 bool wcache_tdc_fetch_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 };
4551 *domains = NULL;
4552 *num_domains = 0;
4554 if ( !key.dptr )
4555 return false;
4557 data = tdb_fetch_compat( wcache->tdb, key );
4559 SAFE_FREE( key.dptr );
4561 if ( !data.dptr )
4562 return false;
4564 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4566 SAFE_FREE( data.dptr );
4568 if ( !*domains )
4569 return false;
4571 return true;
4574 /*********************************************************************
4575 ********************************************************************/
4577 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4579 struct winbindd_tdc_domain *dom_list = NULL;
4580 size_t num_domains = 0;
4581 bool ret = false;
4583 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4584 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4585 domain->name, domain->alt_name,
4586 sid_string_dbg(&domain->sid),
4587 domain->domain_flags,
4588 domain->domain_trust_attribs,
4589 domain->domain_type));
4591 if ( !init_wcache() ) {
4592 return false;
4595 /* fetch the list */
4597 wcache_tdc_fetch_list( &dom_list, &num_domains );
4599 /* add the new domain */
4601 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4602 goto done;
4605 /* pack the domain */
4607 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4608 goto done;
4611 /* Success */
4613 ret = true;
4614 done:
4615 TALLOC_FREE( dom_list );
4617 return ret;
4620 static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
4621 TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
4623 struct winbindd_tdc_domain *dst;
4625 dst = talloc(mem_ctx, struct winbindd_tdc_domain);
4626 if (dst == NULL) {
4627 goto fail;
4629 dst->domain_name = talloc_strdup(dst, src->domain_name);
4630 if (dst->domain_name == NULL) {
4631 goto fail;
4634 dst->dns_name = NULL;
4635 if (src->dns_name != NULL) {
4636 dst->dns_name = talloc_strdup(dst, src->dns_name);
4637 if (dst->dns_name == NULL) {
4638 goto fail;
4642 sid_copy(&dst->sid, &src->sid);
4643 dst->trust_flags = src->trust_flags;
4644 dst->trust_type = src->trust_type;
4645 dst->trust_attribs = src->trust_attribs;
4646 return dst;
4647 fail:
4648 TALLOC_FREE(dst);
4649 return NULL;
4652 /*********************************************************************
4653 ********************************************************************/
4655 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4657 struct winbindd_tdc_domain *dom_list = NULL;
4658 size_t num_domains = 0;
4659 int i;
4660 struct winbindd_tdc_domain *d = NULL;
4662 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4664 if ( !init_wcache() ) {
4665 return NULL;
4668 /* fetch the list */
4670 wcache_tdc_fetch_list( &dom_list, &num_domains );
4672 for ( i=0; i<num_domains; i++ ) {
4673 if ( strequal(name, dom_list[i].domain_name) ||
4674 strequal(name, dom_list[i].dns_name) )
4676 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4677 name));
4679 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4680 break;
4684 TALLOC_FREE( dom_list );
4686 return d;
4689 /*********************************************************************
4690 ********************************************************************/
4692 struct winbindd_tdc_domain*
4693 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4694 const struct dom_sid *sid)
4696 struct winbindd_tdc_domain *dom_list = NULL;
4697 size_t num_domains = 0;
4698 int i;
4699 struct winbindd_tdc_domain *d = NULL;
4701 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4702 sid_string_dbg(sid)));
4704 if (!init_wcache()) {
4705 return NULL;
4708 /* fetch the list */
4710 wcache_tdc_fetch_list(&dom_list, &num_domains);
4712 for (i = 0; i<num_domains; i++) {
4713 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4714 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4715 "Found domain %s for SID %s\n",
4716 dom_list[i].domain_name,
4717 sid_string_dbg(sid)));
4719 d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
4720 break;
4724 TALLOC_FREE(dom_list);
4726 return d;
4730 /*********************************************************************
4731 ********************************************************************/
4733 void wcache_tdc_clear( void )
4735 if ( !init_wcache() )
4736 return;
4738 wcache_tdc_store_list( NULL, 0 );
4740 return;
4744 /*********************************************************************
4745 ********************************************************************/
4747 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4748 NTSTATUS status,
4749 const struct dom_sid *user_sid,
4750 const char *homedir,
4751 const char *shell,
4752 const char *gecos,
4753 uint32 gid)
4755 struct cache_entry *centry;
4756 fstring tmp;
4758 if ( (centry = centry_start(domain, status)) == NULL )
4759 return;
4761 centry_put_string( centry, homedir );
4762 centry_put_string( centry, shell );
4763 centry_put_string( centry, gecos );
4764 centry_put_uint32( centry, gid );
4766 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4768 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4770 centry_free(centry);
4773 #ifdef HAVE_ADS
4775 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4776 const struct dom_sid *user_sid,
4777 TALLOC_CTX *ctx,
4778 const char **homedir, const char **shell,
4779 const char **gecos, gid_t *p_gid)
4781 struct winbind_cache *cache = get_cache(domain);
4782 struct cache_entry *centry = NULL;
4783 NTSTATUS nt_status;
4784 fstring tmp;
4786 if (!cache->tdb)
4787 goto do_query;
4789 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4790 sid_to_fstring(tmp, user_sid));
4792 if (!centry)
4793 goto do_query;
4795 *homedir = centry_string( centry, ctx );
4796 *shell = centry_string( centry, ctx );
4797 *gecos = centry_string( centry, ctx );
4798 *p_gid = centry_uint32( centry );
4800 centry_free(centry);
4802 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4803 sid_string_dbg(user_sid)));
4805 return NT_STATUS_OK;
4807 do_query:
4809 nt_status = nss_get_info( domain->name, user_sid, ctx,
4810 homedir, shell, gecos, p_gid );
4812 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4814 if ( NT_STATUS_IS_OK(nt_status) ) {
4815 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4816 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4817 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4818 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4820 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4821 *homedir, *shell, *gecos, *p_gid );
4824 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4825 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4826 domain->name ));
4827 set_domain_offline( domain );
4830 return nt_status;
4833 #endif
4835 /* the cache backend methods are exposed via this structure */
4836 struct winbindd_methods cache_methods = {
4837 true,
4838 query_user_list,
4839 enum_dom_groups,
4840 enum_local_groups,
4841 name_to_sid,
4842 sid_to_name,
4843 rids_to_names,
4844 query_user,
4845 lookup_usergroups,
4846 lookup_useraliases,
4847 lookup_groupmem,
4848 sequence_number,
4849 lockout_policy,
4850 password_policy,
4851 trusted_domains
4854 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
4855 uint32_t opnum, const DATA_BLOB *req,
4856 TDB_DATA *pkey)
4858 char *key;
4859 size_t keylen;
4861 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4862 if (key == NULL) {
4863 return false;
4865 keylen = talloc_get_size(key) - 1;
4867 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4868 if (key == NULL) {
4869 return false;
4871 memcpy(key + keylen, req->data, req->length);
4873 pkey->dptr = (uint8_t *)key;
4874 pkey->dsize = talloc_get_size(key);
4875 return true;
4878 static bool wcache_opnum_cacheable(uint32_t opnum)
4880 switch (opnum) {
4881 case NDR_WBINT_PING:
4882 case NDR_WBINT_QUERYSEQUENCENUMBER:
4883 case NDR_WBINT_ALLOCATEUID:
4884 case NDR_WBINT_ALLOCATEGID:
4885 case NDR_WBINT_CHECKMACHINEACCOUNT:
4886 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4887 case NDR_WBINT_PINGDC:
4888 return false;
4890 return true;
4893 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4894 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4896 TDB_DATA key, data;
4897 bool ret = false;
4899 if (!wcache_opnum_cacheable(opnum) ||
4900 is_my_own_sam_domain(domain) ||
4901 is_builtin_domain(domain)) {
4902 return false;
4905 if (wcache->tdb == NULL) {
4906 return false;
4909 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4910 return false;
4912 data = tdb_fetch_compat(wcache->tdb, key);
4913 TALLOC_FREE(key.dptr);
4915 if (data.dptr == NULL) {
4916 return false;
4918 if (data.dsize < 12) {
4919 goto fail;
4922 if (!is_domain_offline(domain)) {
4923 uint32_t entry_seqnum, dom_seqnum, last_check;
4924 uint64_t entry_timeout;
4926 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4927 &last_check)) {
4928 goto fail;
4930 entry_seqnum = IVAL(data.dptr, 0);
4931 if (entry_seqnum != dom_seqnum) {
4932 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4933 (int)entry_seqnum));
4934 goto fail;
4936 entry_timeout = BVAL(data.dptr, 4);
4937 if (time(NULL) > entry_timeout) {
4938 DEBUG(10, ("Entry has timed out\n"));
4939 goto fail;
4943 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4944 data.dsize - 12);
4945 if (resp->data == NULL) {
4946 DEBUG(10, ("talloc failed\n"));
4947 goto fail;
4949 resp->length = data.dsize - 12;
4951 ret = true;
4952 fail:
4953 SAFE_FREE(data.dptr);
4954 return ret;
4957 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4958 const DATA_BLOB *req, const DATA_BLOB *resp)
4960 TDB_DATA key, data;
4961 uint32_t dom_seqnum, last_check;
4962 uint64_t timeout;
4964 if (!wcache_opnum_cacheable(opnum) ||
4965 is_my_own_sam_domain(domain) ||
4966 is_builtin_domain(domain)) {
4967 return;
4970 if (wcache->tdb == NULL) {
4971 return;
4974 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4975 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4976 domain->name));
4977 return;
4980 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4981 return;
4984 timeout = time(NULL) + lp_winbind_cache_time();
4986 data.dsize = resp->length + 12;
4987 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4988 if (data.dptr == NULL) {
4989 goto done;
4992 SIVAL(data.dptr, 0, dom_seqnum);
4993 SBVAL(data.dptr, 4, timeout);
4994 memcpy(data.dptr + 12, resp->data, resp->length);
4996 tdb_store(wcache->tdb, key, data, 0);
4998 done:
4999 TALLOC_FREE(key.dptr);
5000 return;