selftest/known: samba3.smb2.session.*reconnect passes now
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blob315202d618163b99499b41782c3a60bd2f9719a5
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_domain(&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_domain(&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 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 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 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 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1115 return NT_STATUS_NO_MEMORY;
1116 strupper_m(upper_name);
1118 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1120 SAFE_FREE( upper_name );
1122 if (!centry)
1123 goto do_query;
1125 status = centry->status;
1127 if (!NT_STATUS_IS_OK(status)) {
1128 centry_free(centry);
1129 return status;
1132 *alias = centry_string( centry, mem_ctx );
1134 centry_free(centry);
1136 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1137 name, *alias ? *alias : "(none)"));
1139 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1141 do_query:
1143 /* If its not in cache and we are offline, then fail */
1145 if ( get_global_winbindd_state_offline() || !domain->online ) {
1146 DEBUG(8,("resolve_username_to_alias: rejecting query "
1147 "in offline mode\n"));
1148 return NT_STATUS_NOT_FOUND;
1151 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1153 if ( NT_STATUS_IS_OK( status ) ) {
1154 wcache_save_username_alias(domain, status, name, *alias);
1157 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1158 wcache_save_username_alias(domain, status, name, "(NULL)");
1161 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1162 nt_errstr(status)));
1164 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1165 set_domain_offline( domain );
1168 return status;
1171 /***************************************************************************
1172 ***************************************************************************/
1174 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1175 struct winbindd_domain *domain,
1176 const char *alias, char **name )
1178 struct winbind_cache *cache = get_cache(domain);
1179 struct cache_entry *centry = NULL;
1180 NTSTATUS status;
1181 char *upper_name;
1183 if ( domain->internal )
1184 return NT_STATUS_NOT_SUPPORTED;
1186 if (!cache->tdb)
1187 goto do_query;
1189 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1190 return NT_STATUS_NO_MEMORY;
1191 strupper_m(upper_name);
1193 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1195 SAFE_FREE( upper_name );
1197 if (!centry)
1198 goto do_query;
1200 status = centry->status;
1202 if (!NT_STATUS_IS_OK(status)) {
1203 centry_free(centry);
1204 return status;
1207 *name = centry_string( centry, mem_ctx );
1209 centry_free(centry);
1211 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1212 alias, *name ? *name : "(none)"));
1214 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1216 do_query:
1218 /* If its not in cache and we are offline, then fail */
1220 if ( get_global_winbindd_state_offline() || !domain->online ) {
1221 DEBUG(8,("resolve_alias_to_username: rejecting query "
1222 "in offline mode\n"));
1223 return NT_STATUS_NOT_FOUND;
1226 /* an alias cannot contain a domain prefix or '@' */
1228 if (strchr(alias, '\\') || strchr(alias, '@')) {
1229 DEBUG(10,("resolve_alias_to_username: skipping fully "
1230 "qualified name %s\n", alias));
1231 return NT_STATUS_OBJECT_NAME_INVALID;
1234 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1236 if ( NT_STATUS_IS_OK( status ) ) {
1237 wcache_save_alias_username( domain, status, alias, *name );
1240 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1241 wcache_save_alias_username(domain, status, alias, "(NULL)");
1244 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1245 nt_errstr(status)));
1247 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1248 set_domain_offline( domain );
1251 return status;
1254 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1256 struct winbind_cache *cache = get_cache(domain);
1257 TDB_DATA data;
1258 fstring key_str, tmp;
1259 uint32 rid;
1261 if (!cache->tdb) {
1262 return NT_STATUS_INTERNAL_DB_ERROR;
1265 if (is_null_sid(sid)) {
1266 return NT_STATUS_INVALID_SID;
1269 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1270 return NT_STATUS_INVALID_SID;
1273 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1275 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1276 if (!data.dptr) {
1277 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1280 SAFE_FREE(data.dptr);
1281 return NT_STATUS_OK;
1284 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1285 as new salted ones. */
1287 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1288 TALLOC_CTX *mem_ctx,
1289 const struct dom_sid *sid,
1290 const uint8 **cached_nt_pass,
1291 const uint8 **cached_salt)
1293 struct winbind_cache *cache = get_cache(domain);
1294 struct cache_entry *centry = NULL;
1295 NTSTATUS status;
1296 uint32 rid;
1297 fstring tmp;
1299 if (!winbindd_use_cache()) {
1300 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1303 if (!cache->tdb) {
1304 return NT_STATUS_INTERNAL_DB_ERROR;
1307 if (is_null_sid(sid)) {
1308 return NT_STATUS_INVALID_SID;
1311 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1312 return NT_STATUS_INVALID_SID;
1315 /* Try and get a salted cred first. If we can't
1316 fall back to an unsalted cred. */
1318 centry = wcache_fetch(cache, domain, "CRED/%s",
1319 sid_to_fstring(tmp, sid));
1320 if (!centry) {
1321 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1322 sid_string_dbg(sid)));
1323 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1326 /* In the salted case this isn't actually the nt_hash itself,
1327 but the MD5 of the salt + nt_hash. Let the caller
1328 sort this out. It can tell as we only return the cached_salt
1329 if we are returning a salted cred. */
1331 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1332 if (*cached_nt_pass == NULL) {
1333 fstring sidstr;
1335 sid_to_fstring(sidstr, sid);
1337 /* Bad (old) cred cache. Delete and pretend we
1338 don't have it. */
1339 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1340 sidstr));
1341 wcache_delete("CRED/%s", sidstr);
1342 centry_free(centry);
1343 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1346 /* We only have 17 bytes more data in the salted cred case. */
1347 if (centry->len - centry->ofs == 17) {
1348 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1349 } else {
1350 *cached_salt = NULL;
1353 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1354 if (*cached_salt) {
1355 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1358 status = centry->status;
1360 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1361 sid_string_dbg(sid), nt_errstr(status) ));
1363 centry_free(centry);
1364 return status;
1367 /* Store creds for a SID - only writes out new salted ones. */
1369 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1370 const struct dom_sid *sid,
1371 const uint8 nt_pass[NT_HASH_LEN])
1373 struct cache_entry *centry;
1374 fstring sid_string;
1375 uint32 rid;
1376 uint8 cred_salt[NT_HASH_LEN];
1377 uint8 salted_hash[NT_HASH_LEN];
1379 if (is_null_sid(sid)) {
1380 return NT_STATUS_INVALID_SID;
1383 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1384 return NT_STATUS_INVALID_SID;
1387 centry = centry_start(domain, NT_STATUS_OK);
1388 if (!centry) {
1389 return NT_STATUS_INTERNAL_DB_ERROR;
1392 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1394 centry_put_time(centry, time(NULL));
1396 /* Create a salt and then salt the hash. */
1397 generate_random_buffer(cred_salt, NT_HASH_LEN);
1398 E_md5hash(cred_salt, nt_pass, salted_hash);
1400 centry_put_hash16(centry, salted_hash);
1401 centry_put_hash16(centry, cred_salt);
1402 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1404 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1406 centry_free(centry);
1408 return NT_STATUS_OK;
1412 /* Query display info. This is the basic user list fn */
1413 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1414 TALLOC_CTX *mem_ctx,
1415 uint32 *num_entries,
1416 struct wbint_userinfo **info)
1418 struct winbind_cache *cache = get_cache(domain);
1419 struct cache_entry *centry = NULL;
1420 NTSTATUS status;
1421 unsigned int i, retry;
1422 bool old_status = domain->online;
1424 if (!cache->tdb)
1425 goto do_query;
1427 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1428 if (!centry)
1429 goto do_query;
1431 do_fetch_cache:
1432 *num_entries = centry_uint32(centry);
1434 if (*num_entries == 0)
1435 goto do_cached;
1437 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1438 if (! (*info)) {
1439 smb_panic_fn("query_user_list out of memory");
1441 for (i=0; i<(*num_entries); i++) {
1442 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1443 (*info)[i].full_name = centry_string(centry, mem_ctx);
1444 (*info)[i].homedir = centry_string(centry, mem_ctx);
1445 (*info)[i].shell = centry_string(centry, mem_ctx);
1446 centry_sid(centry, &(*info)[i].user_sid);
1447 centry_sid(centry, &(*info)[i].group_sid);
1450 do_cached:
1451 status = centry->status;
1453 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1454 domain->name, nt_errstr(status) ));
1456 centry_free(centry);
1457 return status;
1459 do_query:
1460 *num_entries = 0;
1461 *info = NULL;
1463 /* Return status value returned by seq number check */
1465 if (!NT_STATUS_IS_OK(domain->last_status))
1466 return domain->last_status;
1468 /* Put the query_user_list() in a retry loop. There appears to be
1469 * some bug either with Windows 2000 or Samba's handling of large
1470 * rpc replies. This manifests itself as sudden disconnection
1471 * at a random point in the enumeration of a large (60k) user list.
1472 * The retry loop simply tries the operation again. )-: It's not
1473 * pretty but an acceptable workaround until we work out what the
1474 * real problem is. */
1476 retry = 0;
1477 do {
1479 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1480 domain->name ));
1482 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1483 if (!NT_STATUS_IS_OK(status)) {
1484 DEBUG(3, ("query_user_list: returned 0x%08x, "
1485 "retrying\n", NT_STATUS_V(status)));
1487 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1488 DEBUG(3, ("query_user_list: flushing "
1489 "connection cache\n"));
1490 invalidate_cm_connection(&domain->conn);
1492 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1493 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1494 if (!domain->internal && old_status) {
1495 set_domain_offline(domain);
1497 /* store partial response. */
1498 if (*num_entries > 0) {
1500 * humm, what about the status used for cache?
1501 * Should it be NT_STATUS_OK?
1503 break;
1506 * domain is offline now, and there is no user entries,
1507 * try to fetch from cache again.
1509 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1510 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1511 /* partial response... */
1512 if (!centry) {
1513 goto skip_save;
1514 } else {
1515 goto do_fetch_cache;
1517 } else {
1518 goto skip_save;
1522 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1523 (retry++ < 5));
1525 /* and save it */
1526 refresh_sequence_number(domain, false);
1527 if (!NT_STATUS_IS_OK(status)) {
1528 return status;
1530 centry = centry_start(domain, status);
1531 if (!centry)
1532 goto skip_save;
1533 centry_put_uint32(centry, *num_entries);
1534 for (i=0; i<(*num_entries); i++) {
1535 centry_put_string(centry, (*info)[i].acct_name);
1536 centry_put_string(centry, (*info)[i].full_name);
1537 centry_put_string(centry, (*info)[i].homedir);
1538 centry_put_string(centry, (*info)[i].shell);
1539 centry_put_sid(centry, &(*info)[i].user_sid);
1540 centry_put_sid(centry, &(*info)[i].group_sid);
1541 if (domain->backend && domain->backend->consistent) {
1542 /* when the backend is consistent we can pre-prime some mappings */
1543 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1544 domain->name,
1545 (*info)[i].acct_name,
1546 &(*info)[i].user_sid,
1547 SID_NAME_USER);
1548 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1549 &(*info)[i].user_sid,
1550 domain->name,
1551 (*info)[i].acct_name,
1552 SID_NAME_USER);
1553 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1556 centry_end(centry, "UL/%s", domain->name);
1557 centry_free(centry);
1559 skip_save:
1560 return status;
1563 /* list all domain groups */
1564 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1565 TALLOC_CTX *mem_ctx,
1566 uint32 *num_entries,
1567 struct wb_acct_info **info)
1569 struct winbind_cache *cache = get_cache(domain);
1570 struct cache_entry *centry = NULL;
1571 NTSTATUS status;
1572 unsigned int i;
1573 bool old_status;
1575 old_status = domain->online;
1576 if (!cache->tdb)
1577 goto do_query;
1579 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1580 if (!centry)
1581 goto do_query;
1583 do_fetch_cache:
1584 *num_entries = centry_uint32(centry);
1586 if (*num_entries == 0)
1587 goto do_cached;
1589 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1590 if (! (*info)) {
1591 smb_panic_fn("enum_dom_groups out of memory");
1593 for (i=0; i<(*num_entries); i++) {
1594 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1595 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1596 (*info)[i].rid = centry_uint32(centry);
1599 do_cached:
1600 status = centry->status;
1602 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1603 domain->name, nt_errstr(status) ));
1605 centry_free(centry);
1606 return status;
1608 do_query:
1609 *num_entries = 0;
1610 *info = NULL;
1612 /* Return status value returned by seq number check */
1614 if (!NT_STATUS_IS_OK(domain->last_status))
1615 return domain->last_status;
1617 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1618 domain->name ));
1620 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1622 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1623 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1624 if (!domain->internal && old_status) {
1625 set_domain_offline(domain);
1627 if (cache->tdb &&
1628 !domain->online &&
1629 !domain->internal &&
1630 old_status) {
1631 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1632 if (centry) {
1633 goto do_fetch_cache;
1637 /* and save it */
1638 refresh_sequence_number(domain, false);
1639 if (!NT_STATUS_IS_OK(status)) {
1640 return status;
1642 centry = centry_start(domain, status);
1643 if (!centry)
1644 goto skip_save;
1645 centry_put_uint32(centry, *num_entries);
1646 for (i=0; i<(*num_entries); i++) {
1647 centry_put_string(centry, (*info)[i].acct_name);
1648 centry_put_string(centry, (*info)[i].acct_desc);
1649 centry_put_uint32(centry, (*info)[i].rid);
1651 centry_end(centry, "GL/%s/domain", domain->name);
1652 centry_free(centry);
1654 skip_save:
1655 return status;
1658 /* list all domain groups */
1659 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1660 TALLOC_CTX *mem_ctx,
1661 uint32 *num_entries,
1662 struct wb_acct_info **info)
1664 struct winbind_cache *cache = get_cache(domain);
1665 struct cache_entry *centry = NULL;
1666 NTSTATUS status;
1667 unsigned int i;
1668 bool old_status;
1670 old_status = domain->online;
1671 if (!cache->tdb)
1672 goto do_query;
1674 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1675 if (!centry)
1676 goto do_query;
1678 do_fetch_cache:
1679 *num_entries = centry_uint32(centry);
1681 if (*num_entries == 0)
1682 goto do_cached;
1684 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1685 if (! (*info)) {
1686 smb_panic_fn("enum_dom_groups out of memory");
1688 for (i=0; i<(*num_entries); i++) {
1689 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1690 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1691 (*info)[i].rid = centry_uint32(centry);
1694 do_cached:
1696 /* If we are returning cached data and the domain controller
1697 is down then we don't know whether the data is up to date
1698 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1699 indicate this. */
1701 if (wcache_server_down(domain)) {
1702 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1703 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1704 } else
1705 status = centry->status;
1707 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1708 domain->name, nt_errstr(status) ));
1710 centry_free(centry);
1711 return status;
1713 do_query:
1714 *num_entries = 0;
1715 *info = NULL;
1717 /* Return status value returned by seq number check */
1719 if (!NT_STATUS_IS_OK(domain->last_status))
1720 return domain->last_status;
1722 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1723 domain->name ));
1725 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1727 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1728 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1729 if (!domain->internal && old_status) {
1730 set_domain_offline(domain);
1732 if (cache->tdb &&
1733 !domain->internal &&
1734 !domain->online &&
1735 old_status) {
1736 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1737 if (centry) {
1738 goto do_fetch_cache;
1742 /* and save it */
1743 refresh_sequence_number(domain, false);
1744 if (!NT_STATUS_IS_OK(status)) {
1745 return status;
1747 centry = centry_start(domain, status);
1748 if (!centry)
1749 goto skip_save;
1750 centry_put_uint32(centry, *num_entries);
1751 for (i=0; i<(*num_entries); i++) {
1752 centry_put_string(centry, (*info)[i].acct_name);
1753 centry_put_string(centry, (*info)[i].acct_desc);
1754 centry_put_uint32(centry, (*info)[i].rid);
1756 centry_end(centry, "GL/%s/local", domain->name);
1757 centry_free(centry);
1759 skip_save:
1760 return status;
1763 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1764 const char *domain_name,
1765 const char *name,
1766 struct dom_sid *sid,
1767 enum lsa_SidType *type)
1769 struct winbind_cache *cache = get_cache(domain);
1770 struct cache_entry *centry;
1771 NTSTATUS status;
1772 char *uname;
1774 if (cache->tdb == NULL) {
1775 return NT_STATUS_NOT_FOUND;
1778 uname = talloc_strdup_upper(talloc_tos(), name);
1779 if (uname == NULL) {
1780 return NT_STATUS_NO_MEMORY;
1783 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1784 TALLOC_FREE(uname);
1785 if (centry == NULL) {
1786 return NT_STATUS_NOT_FOUND;
1789 status = centry->status;
1790 if (NT_STATUS_IS_OK(status)) {
1791 *type = (enum lsa_SidType)centry_uint32(centry);
1792 centry_sid(centry, sid);
1795 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1796 "%s\n", domain->name, nt_errstr(status) ));
1798 centry_free(centry);
1799 return status;
1802 /* convert a single name to a sid in a domain */
1803 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1804 TALLOC_CTX *mem_ctx,
1805 const char *domain_name,
1806 const char *name,
1807 uint32_t flags,
1808 struct dom_sid *sid,
1809 enum lsa_SidType *type)
1811 NTSTATUS status;
1812 bool old_status;
1814 old_status = domain->online;
1816 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1817 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1818 return status;
1821 ZERO_STRUCTP(sid);
1823 /* If the seq number check indicated that there is a problem
1824 * with this DC, then return that status... except for
1825 * access_denied. This is special because the dc may be in
1826 * "restrict anonymous = 1" mode, in which case it will deny
1827 * most unauthenticated operations, but *will* allow the LSA
1828 * name-to-sid that we try as a fallback. */
1830 if (!(NT_STATUS_IS_OK(domain->last_status)
1831 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1832 return domain->last_status;
1834 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1835 domain->name ));
1837 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1838 name, flags, sid, type);
1840 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1841 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1842 if (!domain->internal && old_status) {
1843 set_domain_offline(domain);
1845 if (!domain->internal &&
1846 !domain->online &&
1847 old_status) {
1848 NTSTATUS cache_status;
1849 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1850 return cache_status;
1853 /* and save it */
1854 refresh_sequence_number(domain, false);
1856 if (domain->online &&
1857 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1858 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1860 /* Only save the reverse mapping if this was not a UPN */
1861 if (!strchr(name, '@')) {
1862 strupper_m(discard_const_p(char, domain_name));
1863 strlower_m(discard_const_p(char, name));
1864 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1868 return status;
1871 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1872 const struct dom_sid *sid,
1873 TALLOC_CTX *mem_ctx,
1874 char **domain_name,
1875 char **name,
1876 enum lsa_SidType *type)
1878 struct winbind_cache *cache = get_cache(domain);
1879 struct cache_entry *centry;
1880 char *sid_string;
1881 NTSTATUS status;
1883 if (cache->tdb == NULL) {
1884 return NT_STATUS_NOT_FOUND;
1887 sid_string = sid_string_tos(sid);
1888 if (sid_string == NULL) {
1889 return NT_STATUS_NO_MEMORY;
1892 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1893 TALLOC_FREE(sid_string);
1894 if (centry == NULL) {
1895 return NT_STATUS_NOT_FOUND;
1898 if (NT_STATUS_IS_OK(centry->status)) {
1899 *type = (enum lsa_SidType)centry_uint32(centry);
1900 *domain_name = centry_string(centry, mem_ctx);
1901 *name = centry_string(centry, mem_ctx);
1904 status = centry->status;
1905 centry_free(centry);
1907 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1908 "%s\n", domain->name, nt_errstr(status) ));
1910 return status;
1913 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1914 given */
1915 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1916 TALLOC_CTX *mem_ctx,
1917 const struct dom_sid *sid,
1918 char **domain_name,
1919 char **name,
1920 enum lsa_SidType *type)
1922 NTSTATUS status;
1923 bool old_status;
1925 old_status = domain->online;
1926 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1927 type);
1928 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1929 return status;
1932 *name = NULL;
1933 *domain_name = NULL;
1935 /* If the seq number check indicated that there is a problem
1936 * with this DC, then return that status... except for
1937 * access_denied. This is special because the dc may be in
1938 * "restrict anonymous = 1" mode, in which case it will deny
1939 * most unauthenticated operations, but *will* allow the LSA
1940 * sid-to-name that we try as a fallback. */
1942 if (!(NT_STATUS_IS_OK(domain->last_status)
1943 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1944 return domain->last_status;
1946 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1947 domain->name ));
1949 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1951 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1952 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1953 if (!domain->internal && old_status) {
1954 set_domain_offline(domain);
1956 if (!domain->internal &&
1957 !domain->online &&
1958 old_status) {
1959 NTSTATUS cache_status;
1960 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1961 domain_name, name, type);
1962 return cache_status;
1965 /* and save it */
1966 refresh_sequence_number(domain, false);
1967 if (!NT_STATUS_IS_OK(status)) {
1968 return status;
1970 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1972 /* We can't save the name to sid mapping here, as with sid history a
1973 * later name2sid would give the wrong sid. */
1975 return status;
1978 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1979 TALLOC_CTX *mem_ctx,
1980 const struct dom_sid *domain_sid,
1981 uint32 *rids,
1982 size_t num_rids,
1983 char **domain_name,
1984 char ***names,
1985 enum lsa_SidType **types)
1987 struct winbind_cache *cache = get_cache(domain);
1988 size_t i;
1989 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1990 bool have_mapped;
1991 bool have_unmapped;
1992 bool old_status;
1994 old_status = domain->online;
1995 *domain_name = NULL;
1996 *names = NULL;
1997 *types = NULL;
1999 if (!cache->tdb) {
2000 goto do_query;
2003 if (num_rids == 0) {
2004 return NT_STATUS_OK;
2007 *names = talloc_array(mem_ctx, char *, num_rids);
2008 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2010 if ((*names == NULL) || (*types == NULL)) {
2011 result = NT_STATUS_NO_MEMORY;
2012 goto error;
2015 have_mapped = have_unmapped = false;
2017 for (i=0; i<num_rids; i++) {
2018 struct dom_sid sid;
2019 struct cache_entry *centry;
2020 fstring tmp;
2022 if (!sid_compose(&sid, domain_sid, rids[i])) {
2023 result = NT_STATUS_INTERNAL_ERROR;
2024 goto error;
2027 centry = wcache_fetch(cache, domain, "SN/%s",
2028 sid_to_fstring(tmp, &sid));
2029 if (!centry) {
2030 goto do_query;
2033 (*types)[i] = SID_NAME_UNKNOWN;
2034 (*names)[i] = talloc_strdup(*names, "");
2036 if (NT_STATUS_IS_OK(centry->status)) {
2037 char *dom;
2038 have_mapped = true;
2039 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2041 dom = centry_string(centry, mem_ctx);
2042 if (*domain_name == NULL) {
2043 *domain_name = dom;
2044 } else {
2045 talloc_free(dom);
2048 (*names)[i] = centry_string(centry, *names);
2050 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2051 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2052 have_unmapped = true;
2054 } else {
2055 /* something's definitely wrong */
2056 result = centry->status;
2057 goto error;
2060 centry_free(centry);
2063 if (!have_mapped) {
2064 return NT_STATUS_NONE_MAPPED;
2066 if (!have_unmapped) {
2067 return NT_STATUS_OK;
2069 return STATUS_SOME_UNMAPPED;
2071 do_query:
2073 TALLOC_FREE(*names);
2074 TALLOC_FREE(*types);
2076 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2077 rids, num_rids, domain_name,
2078 names, types);
2080 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2081 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2082 if (!domain->internal && old_status) {
2083 set_domain_offline(domain);
2085 if (cache->tdb &&
2086 !domain->internal &&
2087 !domain->online &&
2088 old_status) {
2089 have_mapped = have_unmapped = false;
2091 for (i=0; i<num_rids; i++) {
2092 struct dom_sid sid;
2093 struct cache_entry *centry;
2094 fstring tmp;
2096 if (!sid_compose(&sid, domain_sid, rids[i])) {
2097 result = NT_STATUS_INTERNAL_ERROR;
2098 goto error;
2101 centry = wcache_fetch(cache, domain, "SN/%s",
2102 sid_to_fstring(tmp, &sid));
2103 if (!centry) {
2104 (*types)[i] = SID_NAME_UNKNOWN;
2105 (*names)[i] = talloc_strdup(*names, "");
2106 continue;
2109 (*types)[i] = SID_NAME_UNKNOWN;
2110 (*names)[i] = talloc_strdup(*names, "");
2112 if (NT_STATUS_IS_OK(centry->status)) {
2113 char *dom;
2114 have_mapped = true;
2115 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2117 dom = centry_string(centry, mem_ctx);
2118 if (*domain_name == NULL) {
2119 *domain_name = dom;
2120 } else {
2121 talloc_free(dom);
2124 (*names)[i] = centry_string(centry, *names);
2126 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2127 have_unmapped = true;
2129 } else {
2130 /* something's definitely wrong */
2131 result = centry->status;
2132 goto error;
2135 centry_free(centry);
2138 if (!have_mapped) {
2139 return NT_STATUS_NONE_MAPPED;
2141 if (!have_unmapped) {
2142 return NT_STATUS_OK;
2144 return STATUS_SOME_UNMAPPED;
2148 None of the queried rids has been found so save all negative entries
2150 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2151 for (i = 0; i < num_rids; i++) {
2152 struct dom_sid sid;
2153 const char *name = "";
2154 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2155 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2157 if (!sid_compose(&sid, domain_sid, rids[i])) {
2158 return NT_STATUS_INTERNAL_ERROR;
2161 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2162 name, type);
2165 return result;
2169 Some or all of the queried rids have been found.
2171 if (!NT_STATUS_IS_OK(result) &&
2172 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2173 return result;
2176 refresh_sequence_number(domain, false);
2178 for (i=0; i<num_rids; i++) {
2179 struct dom_sid sid;
2180 NTSTATUS status;
2182 if (!sid_compose(&sid, domain_sid, rids[i])) {
2183 result = NT_STATUS_INTERNAL_ERROR;
2184 goto error;
2187 status = (*types)[i] == SID_NAME_UNKNOWN ?
2188 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2190 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2191 (*names)[i], (*types)[i]);
2194 return result;
2196 error:
2197 TALLOC_FREE(*names);
2198 TALLOC_FREE(*types);
2199 return result;
2202 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2203 TALLOC_CTX *mem_ctx,
2204 const struct dom_sid *user_sid,
2205 struct wbint_userinfo *info)
2207 struct winbind_cache *cache = get_cache(domain);
2208 struct cache_entry *centry = NULL;
2209 NTSTATUS status;
2210 char *sid_string;
2212 if (cache->tdb == NULL) {
2213 return NT_STATUS_NOT_FOUND;
2216 sid_string = sid_string_tos(user_sid);
2217 if (sid_string == NULL) {
2218 return NT_STATUS_NO_MEMORY;
2221 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2222 TALLOC_FREE(sid_string);
2223 if (centry == NULL) {
2224 return NT_STATUS_NOT_FOUND;
2228 * If we have an access denied cache entry and a cached info3
2229 * in the samlogon cache then do a query. This will force the
2230 * rpc back end to return the info3 data.
2233 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2234 netsamlogon_cache_have(user_sid)) {
2235 DEBUG(10, ("query_user: cached access denied and have cached "
2236 "info3\n"));
2237 domain->last_status = NT_STATUS_OK;
2238 centry_free(centry);
2239 return NT_STATUS_NOT_FOUND;
2242 /* if status is not ok then this is a negative hit
2243 and the rest of the data doesn't matter */
2244 status = centry->status;
2245 if (NT_STATUS_IS_OK(status)) {
2246 info->acct_name = centry_string(centry, mem_ctx);
2247 info->full_name = centry_string(centry, mem_ctx);
2248 info->homedir = centry_string(centry, mem_ctx);
2249 info->shell = centry_string(centry, mem_ctx);
2250 info->primary_gid = centry_uint32(centry);
2251 centry_sid(centry, &info->user_sid);
2252 centry_sid(centry, &info->group_sid);
2255 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2256 "%s\n", domain->name, nt_errstr(status) ));
2258 centry_free(centry);
2259 return status;
2262 /* Lookup user information from a rid */
2263 static NTSTATUS query_user(struct winbindd_domain *domain,
2264 TALLOC_CTX *mem_ctx,
2265 const struct dom_sid *user_sid,
2266 struct wbint_userinfo *info)
2268 NTSTATUS status;
2269 bool old_status;
2271 old_status = domain->online;
2272 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2273 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2274 return status;
2277 ZERO_STRUCTP(info);
2279 /* Return status value returned by seq number check */
2281 if (!NT_STATUS_IS_OK(domain->last_status))
2282 return domain->last_status;
2284 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2285 domain->name ));
2287 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2289 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2290 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2291 if (!domain->internal && old_status) {
2292 set_domain_offline(domain);
2294 if (!domain->internal &&
2295 !domain->online &&
2296 old_status) {
2297 NTSTATUS cache_status;
2298 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2299 return cache_status;
2302 /* and save it */
2303 refresh_sequence_number(domain, false);
2304 if (!NT_STATUS_IS_OK(status)) {
2305 return status;
2307 wcache_save_user(domain, status, info);
2309 return status;
2312 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2313 TALLOC_CTX *mem_ctx,
2314 const struct dom_sid *user_sid,
2315 uint32_t *pnum_sids,
2316 struct dom_sid **psids)
2318 struct winbind_cache *cache = get_cache(domain);
2319 struct cache_entry *centry = NULL;
2320 NTSTATUS status;
2321 uint32_t i, num_sids;
2322 struct dom_sid *sids;
2323 fstring sid_string;
2325 if (cache->tdb == NULL) {
2326 return NT_STATUS_NOT_FOUND;
2329 centry = wcache_fetch(cache, domain, "UG/%s",
2330 sid_to_fstring(sid_string, user_sid));
2331 if (centry == NULL) {
2332 return NT_STATUS_NOT_FOUND;
2335 /* If we have an access denied cache entry and a cached info3 in the
2336 samlogon cache then do a query. This will force the rpc back end
2337 to return the info3 data. */
2339 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2340 && netsamlogon_cache_have(user_sid)) {
2341 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2342 "cached info3\n"));
2343 domain->last_status = NT_STATUS_OK;
2344 centry_free(centry);
2345 return NT_STATUS_NOT_FOUND;
2348 num_sids = centry_uint32(centry);
2349 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2350 if (sids == NULL) {
2351 centry_free(centry);
2352 return NT_STATUS_NO_MEMORY;
2355 for (i=0; i<num_sids; i++) {
2356 centry_sid(centry, &sids[i]);
2359 status = centry->status;
2361 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2362 "status: %s\n", domain->name, nt_errstr(status)));
2364 centry_free(centry);
2366 *pnum_sids = num_sids;
2367 *psids = sids;
2368 return status;
2371 /* Lookup groups a user is a member of. */
2372 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2373 TALLOC_CTX *mem_ctx,
2374 const struct dom_sid *user_sid,
2375 uint32 *num_groups, struct dom_sid **user_gids)
2377 struct cache_entry *centry = NULL;
2378 NTSTATUS status;
2379 unsigned int i;
2380 fstring sid_string;
2381 bool old_status;
2383 old_status = domain->online;
2384 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2385 num_groups, user_gids);
2386 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2387 return status;
2390 (*num_groups) = 0;
2391 (*user_gids) = NULL;
2393 /* Return status value returned by seq number check */
2395 if (!NT_STATUS_IS_OK(domain->last_status))
2396 return domain->last_status;
2398 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2399 domain->name ));
2401 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2403 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2404 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2405 if (!domain->internal && old_status) {
2406 set_domain_offline(domain);
2408 if (!domain->internal &&
2409 !domain->online &&
2410 old_status) {
2411 NTSTATUS cache_status;
2412 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2413 num_groups, user_gids);
2414 return cache_status;
2417 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2418 goto skip_save;
2420 /* and save it */
2421 refresh_sequence_number(domain, false);
2422 if (!NT_STATUS_IS_OK(status)) {
2423 return status;
2425 centry = centry_start(domain, status);
2426 if (!centry)
2427 goto skip_save;
2429 centry_put_uint32(centry, *num_groups);
2430 for (i=0; i<(*num_groups); i++) {
2431 centry_put_sid(centry, &(*user_gids)[i]);
2434 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2435 centry_free(centry);
2437 skip_save:
2438 return status;
2441 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2442 const struct dom_sid *sids)
2444 uint32_t i;
2445 char *sidlist;
2447 sidlist = talloc_strdup(mem_ctx, "");
2448 if (sidlist == NULL) {
2449 return NULL;
2451 for (i=0; i<num_sids; i++) {
2452 fstring tmp;
2453 sidlist = talloc_asprintf_append_buffer(
2454 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2455 if (sidlist == NULL) {
2456 return NULL;
2459 return sidlist;
2462 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2463 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2464 const struct dom_sid *sids,
2465 uint32_t *pnum_aliases, uint32_t **paliases)
2467 struct winbind_cache *cache = get_cache(domain);
2468 struct cache_entry *centry = NULL;
2469 uint32_t num_aliases;
2470 uint32_t *aliases;
2471 NTSTATUS status;
2472 char *sidlist;
2473 int i;
2475 if (cache->tdb == NULL) {
2476 return NT_STATUS_NOT_FOUND;
2479 if (num_sids == 0) {
2480 *pnum_aliases = 0;
2481 *paliases = NULL;
2482 return NT_STATUS_OK;
2485 /* We need to cache indexed by the whole list of SIDs, the aliases
2486 * resulting might come from any of the SIDs. */
2488 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2489 if (sidlist == NULL) {
2490 return NT_STATUS_NO_MEMORY;
2493 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2494 TALLOC_FREE(sidlist);
2495 if (centry == NULL) {
2496 return NT_STATUS_NOT_FOUND;
2499 num_aliases = centry_uint32(centry);
2500 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2501 if (aliases == NULL) {
2502 centry_free(centry);
2503 return NT_STATUS_NO_MEMORY;
2506 for (i=0; i<num_aliases; i++) {
2507 aliases[i] = centry_uint32(centry);
2510 status = centry->status;
2512 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2513 "status %s\n", domain->name, nt_errstr(status)));
2515 centry_free(centry);
2517 *pnum_aliases = num_aliases;
2518 *paliases = aliases;
2520 return status;
2523 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2524 TALLOC_CTX *mem_ctx,
2525 uint32 num_sids, const struct dom_sid *sids,
2526 uint32 *num_aliases, uint32 **alias_rids)
2528 struct cache_entry *centry = NULL;
2529 NTSTATUS status;
2530 char *sidlist;
2531 int i;
2532 bool old_status;
2534 old_status = domain->online;
2535 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2536 num_aliases, alias_rids);
2537 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2538 return status;
2541 (*num_aliases) = 0;
2542 (*alias_rids) = NULL;
2544 if (!NT_STATUS_IS_OK(domain->last_status))
2545 return domain->last_status;
2547 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2548 "for domain %s\n", domain->name ));
2550 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2551 if (sidlist == NULL) {
2552 return NT_STATUS_NO_MEMORY;
2555 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2556 num_sids, sids,
2557 num_aliases, alias_rids);
2559 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2560 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2561 if (!domain->internal && old_status) {
2562 set_domain_offline(domain);
2564 if (!domain->internal &&
2565 !domain->online &&
2566 old_status) {
2567 NTSTATUS cache_status;
2568 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2569 sids, num_aliases, alias_rids);
2570 return cache_status;
2573 /* and save it */
2574 refresh_sequence_number(domain, false);
2575 if (!NT_STATUS_IS_OK(status)) {
2576 return status;
2578 centry = centry_start(domain, status);
2579 if (!centry)
2580 goto skip_save;
2581 centry_put_uint32(centry, *num_aliases);
2582 for (i=0; i<(*num_aliases); i++)
2583 centry_put_uint32(centry, (*alias_rids)[i]);
2584 centry_end(centry, "UA%s", sidlist);
2585 centry_free(centry);
2587 skip_save:
2588 return status;
2591 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2592 TALLOC_CTX *mem_ctx,
2593 const struct dom_sid *group_sid,
2594 uint32_t *num_names,
2595 struct dom_sid **sid_mem, char ***names,
2596 uint32_t **name_types)
2598 struct winbind_cache *cache = get_cache(domain);
2599 struct cache_entry *centry = NULL;
2600 NTSTATUS status;
2601 unsigned int i;
2602 char *sid_string;
2604 if (cache->tdb == NULL) {
2605 return NT_STATUS_NOT_FOUND;
2608 sid_string = sid_string_tos(group_sid);
2609 if (sid_string == NULL) {
2610 return NT_STATUS_NO_MEMORY;
2613 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2614 TALLOC_FREE(sid_string);
2615 if (centry == NULL) {
2616 return NT_STATUS_NOT_FOUND;
2619 *sid_mem = NULL;
2620 *names = NULL;
2621 *name_types = NULL;
2623 *num_names = centry_uint32(centry);
2624 if (*num_names == 0) {
2625 centry_free(centry);
2626 return NT_STATUS_OK;
2629 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2630 *names = talloc_array(mem_ctx, char *, *num_names);
2631 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2633 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2634 TALLOC_FREE(*sid_mem);
2635 TALLOC_FREE(*names);
2636 TALLOC_FREE(*name_types);
2637 centry_free(centry);
2638 return NT_STATUS_NO_MEMORY;
2641 for (i=0; i<(*num_names); i++) {
2642 centry_sid(centry, &(*sid_mem)[i]);
2643 (*names)[i] = centry_string(centry, mem_ctx);
2644 (*name_types)[i] = centry_uint32(centry);
2647 status = centry->status;
2649 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2650 "status: %s\n", domain->name, nt_errstr(status)));
2652 centry_free(centry);
2653 return status;
2656 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2657 TALLOC_CTX *mem_ctx,
2658 const struct dom_sid *group_sid,
2659 enum lsa_SidType type,
2660 uint32 *num_names,
2661 struct dom_sid **sid_mem, char ***names,
2662 uint32 **name_types)
2664 struct cache_entry *centry = NULL;
2665 NTSTATUS status;
2666 unsigned int i;
2667 fstring sid_string;
2668 bool old_status;
2670 old_status = domain->online;
2671 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2672 sid_mem, names, name_types);
2673 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2674 return status;
2677 (*num_names) = 0;
2678 (*sid_mem) = NULL;
2679 (*names) = NULL;
2680 (*name_types) = NULL;
2682 /* Return status value returned by seq number check */
2684 if (!NT_STATUS_IS_OK(domain->last_status))
2685 return domain->last_status;
2687 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2688 domain->name ));
2690 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2691 type, num_names,
2692 sid_mem, names, name_types);
2694 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2695 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2696 if (!domain->internal && old_status) {
2697 set_domain_offline(domain);
2699 if (!domain->internal &&
2700 !domain->online &&
2701 old_status) {
2702 NTSTATUS cache_status;
2703 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2704 num_names, sid_mem, names,
2705 name_types);
2706 return cache_status;
2709 /* and save it */
2710 refresh_sequence_number(domain, false);
2711 if (!NT_STATUS_IS_OK(status)) {
2712 return status;
2714 centry = centry_start(domain, status);
2715 if (!centry)
2716 goto skip_save;
2717 centry_put_uint32(centry, *num_names);
2718 for (i=0; i<(*num_names); i++) {
2719 centry_put_sid(centry, &(*sid_mem)[i]);
2720 centry_put_string(centry, (*names)[i]);
2721 centry_put_uint32(centry, (*name_types)[i]);
2723 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2724 centry_free(centry);
2726 skip_save:
2727 return status;
2730 /* find the sequence number for a domain */
2731 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2733 refresh_sequence_number(domain, false);
2735 *seq = domain->sequence_number;
2737 return NT_STATUS_OK;
2740 /* enumerate trusted domains
2741 * (we need to have the list of trustdoms in the cache when we go offline) -
2742 * Guenther */
2743 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2744 TALLOC_CTX *mem_ctx,
2745 struct netr_DomainTrustList *trusts)
2747 NTSTATUS status;
2748 struct winbind_cache *cache;
2749 struct winbindd_tdc_domain *dom_list = NULL;
2750 size_t num_domains = 0;
2751 bool retval = false;
2752 int i;
2753 bool old_status;
2755 old_status = domain->online;
2756 trusts->count = 0;
2757 trusts->array = NULL;
2759 cache = get_cache(domain);
2760 if (!cache || !cache->tdb) {
2761 goto do_query;
2764 if (domain->online) {
2765 goto do_query;
2768 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2769 if (!retval || !num_domains || !dom_list) {
2770 TALLOC_FREE(dom_list);
2771 goto do_query;
2774 do_fetch_cache:
2775 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2776 if (!trusts->array) {
2777 TALLOC_FREE(dom_list);
2778 return NT_STATUS_NO_MEMORY;
2781 for (i = 0; i < num_domains; i++) {
2782 struct netr_DomainTrust *trust;
2783 struct dom_sid *sid;
2784 struct winbindd_domain *dom;
2786 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2787 if (dom && dom->internal) {
2788 continue;
2791 trust = &trusts->array[trusts->count];
2792 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2793 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2794 sid = talloc(trusts->array, struct dom_sid);
2795 if (!trust->netbios_name || !trust->dns_name ||
2796 !sid) {
2797 TALLOC_FREE(dom_list);
2798 TALLOC_FREE(trusts->array);
2799 return NT_STATUS_NO_MEMORY;
2802 trust->trust_flags = dom_list[i].trust_flags;
2803 trust->trust_attributes = dom_list[i].trust_attribs;
2804 trust->trust_type = dom_list[i].trust_type;
2805 sid_copy(sid, &dom_list[i].sid);
2806 trust->sid = sid;
2807 trusts->count++;
2810 TALLOC_FREE(dom_list);
2811 return NT_STATUS_OK;
2813 do_query:
2814 /* Return status value returned by seq number check */
2816 if (!NT_STATUS_IS_OK(domain->last_status))
2817 return domain->last_status;
2819 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2820 domain->name ));
2822 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2824 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2825 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2826 if (!domain->internal && old_status) {
2827 set_domain_offline(domain);
2829 if (!domain->internal &&
2830 !domain->online &&
2831 old_status) {
2832 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2833 if (retval && num_domains && dom_list) {
2834 TALLOC_FREE(trusts->array);
2835 trusts->count = 0;
2836 goto do_fetch_cache;
2840 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2841 * so that the generic centry handling still applies correctly -
2842 * Guenther*/
2844 if (!NT_STATUS_IS_ERR(status)) {
2845 status = NT_STATUS_OK;
2847 return status;
2850 /* get lockout policy */
2851 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2852 TALLOC_CTX *mem_ctx,
2853 struct samr_DomInfo12 *policy)
2855 struct winbind_cache *cache = get_cache(domain);
2856 struct cache_entry *centry = NULL;
2857 NTSTATUS status;
2858 bool old_status;
2860 old_status = domain->online;
2861 if (!cache->tdb)
2862 goto do_query;
2864 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2866 if (!centry)
2867 goto do_query;
2869 do_fetch_cache:
2870 policy->lockout_duration = centry_nttime(centry);
2871 policy->lockout_window = centry_nttime(centry);
2872 policy->lockout_threshold = centry_uint16(centry);
2874 status = centry->status;
2876 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2877 domain->name, nt_errstr(status) ));
2879 centry_free(centry);
2880 return status;
2882 do_query:
2883 ZERO_STRUCTP(policy);
2885 /* Return status value returned by seq number check */
2887 if (!NT_STATUS_IS_OK(domain->last_status))
2888 return domain->last_status;
2890 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2891 domain->name ));
2893 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2895 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2896 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2897 if (!domain->internal && old_status) {
2898 set_domain_offline(domain);
2900 if (cache->tdb &&
2901 !domain->internal &&
2902 !domain->online &&
2903 old_status) {
2904 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2905 if (centry) {
2906 goto do_fetch_cache;
2910 /* and save it */
2911 refresh_sequence_number(domain, false);
2912 if (!NT_STATUS_IS_OK(status)) {
2913 return status;
2915 wcache_save_lockout_policy(domain, status, policy);
2917 return status;
2920 /* get password policy */
2921 static NTSTATUS password_policy(struct winbindd_domain *domain,
2922 TALLOC_CTX *mem_ctx,
2923 struct samr_DomInfo1 *policy)
2925 struct winbind_cache *cache = get_cache(domain);
2926 struct cache_entry *centry = NULL;
2927 NTSTATUS status;
2928 bool old_status;
2930 old_status = domain->online;
2931 if (!cache->tdb)
2932 goto do_query;
2934 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2936 if (!centry)
2937 goto do_query;
2939 do_fetch_cache:
2940 policy->min_password_length = centry_uint16(centry);
2941 policy->password_history_length = centry_uint16(centry);
2942 policy->password_properties = centry_uint32(centry);
2943 policy->max_password_age = centry_nttime(centry);
2944 policy->min_password_age = centry_nttime(centry);
2946 status = centry->status;
2948 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2949 domain->name, nt_errstr(status) ));
2951 centry_free(centry);
2952 return status;
2954 do_query:
2955 ZERO_STRUCTP(policy);
2957 /* Return status value returned by seq number check */
2959 if (!NT_STATUS_IS_OK(domain->last_status))
2960 return domain->last_status;
2962 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2963 domain->name ));
2965 status = domain->backend->password_policy(domain, mem_ctx, policy);
2967 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2968 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2969 if (!domain->internal && old_status) {
2970 set_domain_offline(domain);
2972 if (cache->tdb &&
2973 !domain->internal &&
2974 !domain->online &&
2975 old_status) {
2976 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2977 if (centry) {
2978 goto do_fetch_cache;
2982 /* and save it */
2983 refresh_sequence_number(domain, false);
2984 if (!NT_STATUS_IS_OK(status)) {
2985 return status;
2987 wcache_save_password_policy(domain, status, policy);
2989 return status;
2993 /* Invalidate cached user and group lists coherently */
2995 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2996 void *state)
2998 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2999 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3000 tdb_delete(the_tdb, kbuf);
3002 return 0;
3005 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3007 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3008 const struct dom_sid *sid)
3010 fstring key_str, sid_string;
3011 struct winbind_cache *cache;
3013 /* dont clear cached U/SID and UG/SID entries when we want to logon
3014 * offline - gd */
3016 if (lp_winbind_offline_logon()) {
3017 return;
3020 if (!domain)
3021 return;
3023 cache = get_cache(domain);
3025 if (!cache->tdb) {
3026 return;
3029 /* Clear U/SID cache entry */
3030 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3031 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3032 tdb_delete(cache->tdb, string_tdb_data(key_str));
3034 /* Clear UG/SID cache entry */
3035 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3036 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3037 tdb_delete(cache->tdb, string_tdb_data(key_str));
3039 /* Samba/winbindd never needs this. */
3040 netsamlogon_clear_cached_user(sid);
3043 bool wcache_invalidate_cache(void)
3045 struct winbindd_domain *domain;
3047 for (domain = domain_list(); domain; domain = domain->next) {
3048 struct winbind_cache *cache = get_cache(domain);
3050 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3051 "entries for %s\n", domain->name));
3052 if (cache) {
3053 if (cache->tdb) {
3054 tdb_traverse(cache->tdb, traverse_fn, NULL);
3055 } else {
3056 return false;
3060 return true;
3063 bool wcache_invalidate_cache_noinit(void)
3065 struct winbindd_domain *domain;
3067 for (domain = domain_list(); domain; domain = domain->next) {
3068 struct winbind_cache *cache;
3070 /* Skip uninitialized domains. */
3071 if (!domain->initialized && !domain->internal) {
3072 continue;
3075 cache = get_cache(domain);
3077 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3078 "entries for %s\n", domain->name));
3079 if (cache) {
3080 if (cache->tdb) {
3081 tdb_traverse(cache->tdb, traverse_fn, NULL);
3083 * Flushing cache has nothing to with domains.
3084 * return here if we successfully flushed once.
3085 * To avoid unnecessary traversing the cache.
3087 return true;
3088 } else {
3089 return false;
3093 return true;
3096 bool init_wcache(void)
3098 if (wcache == NULL) {
3099 wcache = SMB_XMALLOC_P(struct winbind_cache);
3100 ZERO_STRUCTP(wcache);
3103 if (wcache->tdb != NULL)
3104 return true;
3106 /* when working offline we must not clear the cache on restart */
3107 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3108 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3109 TDB_INCOMPATIBLE_HASH |
3110 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3111 O_RDWR|O_CREAT, 0600);
3113 if (wcache->tdb == NULL) {
3114 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3115 return false;
3118 return true;
3121 /************************************************************************
3122 This is called by the parent to initialize the cache file.
3123 We don't need sophisticated locking here as we know we're the
3124 only opener.
3125 ************************************************************************/
3127 bool initialize_winbindd_cache(void)
3129 bool cache_bad = true;
3130 uint32 vers;
3132 if (!init_wcache()) {
3133 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3134 return false;
3137 /* Check version number. */
3138 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3139 vers == WINBINDD_CACHE_VERSION) {
3140 cache_bad = false;
3143 if (cache_bad) {
3144 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3145 "and re-creating with version number %d\n",
3146 WINBINDD_CACHE_VERSION ));
3148 tdb_close(wcache->tdb);
3149 wcache->tdb = NULL;
3151 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3152 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3153 state_path("winbindd_cache.tdb"),
3154 strerror(errno) ));
3155 return false;
3157 if (!init_wcache()) {
3158 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3159 "init_wcache failed.\n"));
3160 return false;
3163 /* Write the version. */
3164 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3165 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3166 tdb_errorstr_compat(wcache->tdb) ));
3167 return false;
3171 tdb_close(wcache->tdb);
3172 wcache->tdb = NULL;
3173 return true;
3176 void close_winbindd_cache(void)
3178 if (!wcache) {
3179 return;
3181 if (wcache->tdb) {
3182 tdb_close(wcache->tdb);
3183 wcache->tdb = NULL;
3187 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3188 char **domain_name, char **name,
3189 enum lsa_SidType *type)
3191 struct winbindd_domain *domain;
3192 NTSTATUS status;
3194 domain = find_lookup_domain_from_sid(sid);
3195 if (domain == NULL) {
3196 return false;
3198 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3199 type);
3200 return NT_STATUS_IS_OK(status);
3203 bool lookup_cached_name(const char *domain_name,
3204 const char *name,
3205 struct dom_sid *sid,
3206 enum lsa_SidType *type)
3208 struct winbindd_domain *domain;
3209 NTSTATUS status;
3210 bool original_online_state;
3212 domain = find_lookup_domain_from_name(domain_name);
3213 if (domain == NULL) {
3214 return false;
3217 /* If we are doing a cached logon, temporarily set the domain
3218 offline so the cache won't expire the entry */
3220 original_online_state = domain->online;
3221 domain->online = false;
3222 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3223 domain->online = original_online_state;
3225 return NT_STATUS_IS_OK(status);
3228 void cache_name2sid(struct winbindd_domain *domain,
3229 const char *domain_name, const char *name,
3230 enum lsa_SidType type, const struct dom_sid *sid)
3232 refresh_sequence_number(domain, false);
3233 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3234 sid, type);
3238 * The original idea that this cache only contains centries has
3239 * been blurred - now other stuff gets put in here. Ensure we
3240 * ignore these things on cleanup.
3243 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3244 TDB_DATA dbuf, void *state)
3246 struct cache_entry *centry;
3248 if (is_non_centry_key(kbuf)) {
3249 return 0;
3252 centry = wcache_fetch_raw((char *)kbuf.dptr);
3253 if (!centry) {
3254 return 0;
3257 if (!NT_STATUS_IS_OK(centry->status)) {
3258 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3259 tdb_delete(the_tdb, kbuf);
3262 centry_free(centry);
3263 return 0;
3266 /* flush the cache */
3267 void wcache_flush_cache(void)
3269 if (!wcache)
3270 return;
3271 if (wcache->tdb) {
3272 tdb_close(wcache->tdb);
3273 wcache->tdb = NULL;
3275 if (!winbindd_use_cache()) {
3276 return;
3279 /* when working offline we must not clear the cache on restart */
3280 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3281 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3282 TDB_INCOMPATIBLE_HASH |
3283 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3284 O_RDWR|O_CREAT, 0600);
3286 if (!wcache->tdb) {
3287 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3288 return;
3291 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3293 DEBUG(10,("wcache_flush_cache success\n"));
3296 /* Count cached creds */
3298 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3299 void *state)
3301 int *cred_count = (int*)state;
3303 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3304 (*cred_count)++;
3306 return 0;
3309 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3311 struct winbind_cache *cache = get_cache(domain);
3313 *count = 0;
3315 if (!cache->tdb) {
3316 return NT_STATUS_INTERNAL_DB_ERROR;
3319 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3321 return NT_STATUS_OK;
3324 struct cred_list {
3325 struct cred_list *prev, *next;
3326 TDB_DATA key;
3327 fstring name;
3328 time_t created;
3330 static struct cred_list *wcache_cred_list;
3332 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3333 void *state)
3335 struct cred_list *cred;
3337 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3339 cred = SMB_MALLOC_P(struct cred_list);
3340 if (cred == NULL) {
3341 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3342 return -1;
3345 ZERO_STRUCTP(cred);
3347 /* save a copy of the key */
3349 fstrcpy(cred->name, (const char *)kbuf.dptr);
3350 DLIST_ADD(wcache_cred_list, cred);
3353 return 0;
3356 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3358 struct winbind_cache *cache = get_cache(domain);
3359 NTSTATUS status;
3360 int ret;
3361 struct cred_list *cred, *oldest = NULL;
3363 if (!cache->tdb) {
3364 return NT_STATUS_INTERNAL_DB_ERROR;
3367 /* we possibly already have an entry */
3368 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3370 fstring key_str, tmp;
3372 DEBUG(11,("we already have an entry, deleting that\n"));
3374 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3376 tdb_delete(cache->tdb, string_tdb_data(key_str));
3378 return NT_STATUS_OK;
3381 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3382 if (ret == 0) {
3383 return NT_STATUS_OK;
3384 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3385 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3388 ZERO_STRUCTP(oldest);
3390 for (cred = wcache_cred_list; cred; cred = cred->next) {
3392 TDB_DATA data;
3393 time_t t;
3395 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3396 if (!data.dptr) {
3397 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3398 cred->name));
3399 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3400 goto done;
3403 t = IVAL(data.dptr, 0);
3404 SAFE_FREE(data.dptr);
3406 if (!oldest) {
3407 oldest = SMB_MALLOC_P(struct cred_list);
3408 if (oldest == NULL) {
3409 status = NT_STATUS_NO_MEMORY;
3410 goto done;
3413 fstrcpy(oldest->name, cred->name);
3414 oldest->created = t;
3415 continue;
3418 if (t < oldest->created) {
3419 fstrcpy(oldest->name, cred->name);
3420 oldest->created = t;
3424 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3425 status = NT_STATUS_OK;
3426 } else {
3427 status = NT_STATUS_UNSUCCESSFUL;
3429 done:
3430 SAFE_FREE(wcache_cred_list);
3431 SAFE_FREE(oldest);
3433 return status;
3436 /* Change the global online/offline state. */
3437 bool set_global_winbindd_state_offline(void)
3439 TDB_DATA data;
3441 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3443 /* Only go offline if someone has created
3444 the key "WINBINDD_OFFLINE" in the cache tdb. */
3446 if (wcache == NULL || wcache->tdb == NULL) {
3447 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3448 return false;
3451 if (!lp_winbind_offline_logon()) {
3452 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3453 return false;
3456 if (global_winbindd_offline_state) {
3457 /* Already offline. */
3458 return true;
3461 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3463 if (!data.dptr || data.dsize != 4) {
3464 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3465 SAFE_FREE(data.dptr);
3466 return false;
3467 } else {
3468 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3469 global_winbindd_offline_state = true;
3470 SAFE_FREE(data.dptr);
3471 return true;
3475 void set_global_winbindd_state_online(void)
3477 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3479 if (!lp_winbind_offline_logon()) {
3480 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3481 return;
3484 if (!global_winbindd_offline_state) {
3485 /* Already online. */
3486 return;
3488 global_winbindd_offline_state = false;
3490 if (!wcache->tdb) {
3491 return;
3494 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3495 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3498 bool get_global_winbindd_state_offline(void)
3500 return global_winbindd_offline_state;
3503 /***********************************************************************
3504 Validate functions for all possible cache tdb keys.
3505 ***********************************************************************/
3507 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3508 struct tdb_validation_status *state)
3510 struct cache_entry *centry;
3512 centry = SMB_XMALLOC_P(struct cache_entry);
3513 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3514 if (!centry->data) {
3515 SAFE_FREE(centry);
3516 return NULL;
3518 centry->len = data.dsize;
3519 centry->ofs = 0;
3521 if (centry->len < 16) {
3522 /* huh? corrupt cache? */
3523 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3524 "(len < 16) ?\n", kstr));
3525 centry_free(centry);
3526 state->bad_entry = true;
3527 state->success = false;
3528 return NULL;
3531 centry->status = NT_STATUS(centry_uint32(centry));
3532 centry->sequence_number = centry_uint32(centry);
3533 centry->timeout = centry_uint64_t(centry);
3534 return centry;
3537 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3538 struct tdb_validation_status *state)
3540 if (dbuf.dsize != 8) {
3541 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3542 keystr, (unsigned int)dbuf.dsize ));
3543 state->bad_entry = true;
3544 return 1;
3546 return 0;
3549 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3550 struct tdb_validation_status *state)
3552 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3553 if (!centry) {
3554 return 1;
3557 (void)centry_uint32(centry);
3558 if (NT_STATUS_IS_OK(centry->status)) {
3559 struct dom_sid sid;
3560 (void)centry_sid(centry, &sid);
3563 centry_free(centry);
3565 if (!(state->success)) {
3566 return 1;
3568 DEBUG(10,("validate_ns: %s ok\n", keystr));
3569 return 0;
3572 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573 struct tdb_validation_status *state)
3575 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576 if (!centry) {
3577 return 1;
3580 if (NT_STATUS_IS_OK(centry->status)) {
3581 (void)centry_uint32(centry);
3582 (void)centry_string(centry, mem_ctx);
3583 (void)centry_string(centry, mem_ctx);
3586 centry_free(centry);
3588 if (!(state->success)) {
3589 return 1;
3591 DEBUG(10,("validate_sn: %s ok\n", keystr));
3592 return 0;
3595 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3596 struct tdb_validation_status *state)
3598 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3599 struct dom_sid sid;
3601 if (!centry) {
3602 return 1;
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_string(centry, mem_ctx);
3607 (void)centry_string(centry, mem_ctx);
3608 (void)centry_string(centry, mem_ctx);
3609 (void)centry_uint32(centry);
3610 (void)centry_sid(centry, &sid);
3611 (void)centry_sid(centry, &sid);
3613 centry_free(centry);
3615 if (!(state->success)) {
3616 return 1;
3618 DEBUG(10,("validate_u: %s ok\n", keystr));
3619 return 0;
3622 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3623 struct tdb_validation_status *state)
3625 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3627 if (!centry) {
3628 return 1;
3631 (void)centry_nttime(centry);
3632 (void)centry_nttime(centry);
3633 (void)centry_uint16(centry);
3635 centry_free(centry);
3637 if (!(state->success)) {
3638 return 1;
3640 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3641 return 0;
3644 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3645 struct tdb_validation_status *state)
3647 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649 if (!centry) {
3650 return 1;
3653 (void)centry_uint16(centry);
3654 (void)centry_uint16(centry);
3655 (void)centry_uint32(centry);
3656 (void)centry_nttime(centry);
3657 (void)centry_nttime(centry);
3659 centry_free(centry);
3661 if (!(state->success)) {
3662 return 1;
3664 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3665 return 0;
3668 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3669 struct tdb_validation_status *state)
3671 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3673 if (!centry) {
3674 return 1;
3677 (void)centry_time(centry);
3678 (void)centry_hash16(centry, mem_ctx);
3680 /* We only have 17 bytes more data in the salted cred case. */
3681 if (centry->len - centry->ofs == 17) {
3682 (void)centry_hash16(centry, mem_ctx);
3685 centry_free(centry);
3687 if (!(state->success)) {
3688 return 1;
3690 DEBUG(10,("validate_cred: %s ok\n", keystr));
3691 return 0;
3694 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3695 struct tdb_validation_status *state)
3697 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3698 int32 num_entries, i;
3700 if (!centry) {
3701 return 1;
3704 num_entries = (int32)centry_uint32(centry);
3706 for (i=0; i< num_entries; i++) {
3707 struct dom_sid sid;
3708 (void)centry_string(centry, mem_ctx);
3709 (void)centry_string(centry, mem_ctx);
3710 (void)centry_string(centry, mem_ctx);
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_sid(centry, &sid);
3713 (void)centry_sid(centry, &sid);
3716 centry_free(centry);
3718 if (!(state->success)) {
3719 return 1;
3721 DEBUG(10,("validate_ul: %s ok\n", keystr));
3722 return 0;
3725 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3726 struct tdb_validation_status *state)
3728 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3729 int32 num_entries, i;
3731 if (!centry) {
3732 return 1;
3735 num_entries = centry_uint32(centry);
3737 for (i=0; i< num_entries; i++) {
3738 (void)centry_string(centry, mem_ctx);
3739 (void)centry_string(centry, mem_ctx);
3740 (void)centry_uint32(centry);
3743 centry_free(centry);
3745 if (!(state->success)) {
3746 return 1;
3748 DEBUG(10,("validate_gl: %s ok\n", keystr));
3749 return 0;
3752 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3753 struct tdb_validation_status *state)
3755 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3756 int32 num_groups, i;
3758 if (!centry) {
3759 return 1;
3762 num_groups = centry_uint32(centry);
3764 for (i=0; i< num_groups; i++) {
3765 struct dom_sid sid;
3766 centry_sid(centry, &sid);
3769 centry_free(centry);
3771 if (!(state->success)) {
3772 return 1;
3774 DEBUG(10,("validate_ug: %s ok\n", keystr));
3775 return 0;
3778 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3779 struct tdb_validation_status *state)
3781 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3782 int32 num_aliases, i;
3784 if (!centry) {
3785 return 1;
3788 num_aliases = centry_uint32(centry);
3790 for (i=0; i < num_aliases; i++) {
3791 (void)centry_uint32(centry);
3794 centry_free(centry);
3796 if (!(state->success)) {
3797 return 1;
3799 DEBUG(10,("validate_ua: %s ok\n", keystr));
3800 return 0;
3803 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3804 struct tdb_validation_status *state)
3806 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3807 int32 num_names, i;
3809 if (!centry) {
3810 return 1;
3813 num_names = centry_uint32(centry);
3815 for (i=0; i< num_names; i++) {
3816 struct dom_sid sid;
3817 centry_sid(centry, &sid);
3818 (void)centry_string(centry, mem_ctx);
3819 (void)centry_uint32(centry);
3822 centry_free(centry);
3824 if (!(state->success)) {
3825 return 1;
3827 DEBUG(10,("validate_gm: %s ok\n", keystr));
3828 return 0;
3831 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3832 struct tdb_validation_status *state)
3834 /* Can't say anything about this other than must be nonzero. */
3835 if (dbuf.dsize == 0) {
3836 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3837 keystr));
3838 state->bad_entry = true;
3839 state->success = false;
3840 return 1;
3843 DEBUG(10,("validate_dr: %s ok\n", keystr));
3844 return 0;
3847 static int validate_de(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_de: 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_de: %s ok\n", keystr));
3860 return 0;
3863 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3864 TDB_DATA dbuf, struct tdb_validation_status *state)
3866 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3868 if (!centry) {
3869 return 1;
3872 (void)centry_string(centry, mem_ctx);
3873 (void)centry_string(centry, mem_ctx);
3874 (void)centry_string(centry, mem_ctx);
3875 (void)centry_uint32(centry);
3877 centry_free(centry);
3879 if (!(state->success)) {
3880 return 1;
3882 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3883 return 0;
3886 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3887 TDB_DATA dbuf,
3888 struct tdb_validation_status *state)
3890 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3892 if (!centry) {
3893 return 1;
3896 (void)centry_string( centry, mem_ctx );
3898 centry_free(centry);
3900 if (!(state->success)) {
3901 return 1;
3903 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3904 return 0;
3907 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3908 TDB_DATA dbuf,
3909 struct tdb_validation_status *state)
3911 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3913 if (!centry) {
3914 return 1;
3917 (void)centry_string( centry, mem_ctx );
3919 centry_free(centry);
3921 if (!(state->success)) {
3922 return 1;
3924 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3925 return 0;
3928 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3929 TDB_DATA dbuf,
3930 struct tdb_validation_status *state)
3932 if (dbuf.dsize == 0) {
3933 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3934 "key %s (len ==0) ?\n", keystr));
3935 state->bad_entry = true;
3936 state->success = false;
3937 return 1;
3940 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3941 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3942 return 0;
3945 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3946 struct tdb_validation_status *state)
3948 if (dbuf.dsize != 4) {
3949 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3950 keystr, (unsigned int)dbuf.dsize ));
3951 state->bad_entry = true;
3952 state->success = false;
3953 return 1;
3955 DEBUG(10,("validate_offline: %s ok\n", keystr));
3956 return 0;
3959 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3960 struct tdb_validation_status *state)
3963 * Ignore validation for now. The proper way to do this is with a
3964 * checksum. Just pure parsing does not really catch much.
3966 return 0;
3969 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3970 struct tdb_validation_status *state)
3972 if (dbuf.dsize != 4) {
3973 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3974 "key %s (len %u != 4) ?\n",
3975 keystr, (unsigned int)dbuf.dsize));
3976 state->bad_entry = true;
3977 state->success = false;
3978 return 1;
3981 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3982 return 0;
3985 /***********************************************************************
3986 A list of all possible cache tdb keys with associated validation
3987 functions.
3988 ***********************************************************************/
3990 struct key_val_struct {
3991 const char *keyname;
3992 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3993 } key_val[] = {
3994 {"SEQNUM/", validate_seqnum},
3995 {"NS/", validate_ns},
3996 {"SN/", validate_sn},
3997 {"U/", validate_u},
3998 {"LOC_POL/", validate_loc_pol},
3999 {"PWD_POL/", validate_pwd_pol},
4000 {"CRED/", validate_cred},
4001 {"UL/", validate_ul},
4002 {"GL/", validate_gl},
4003 {"UG/", validate_ug},
4004 {"UA", validate_ua},
4005 {"GM/", validate_gm},
4006 {"DR/", validate_dr},
4007 {"DE/", validate_de},
4008 {"NSS/PWINFO/", validate_pwinfo},
4009 {"TRUSTDOMCACHE/", validate_trustdomcache},
4010 {"NSS/NA/", validate_nss_na},
4011 {"NSS/AN/", validate_nss_an},
4012 {"WINBINDD_OFFLINE", validate_offline},
4013 {"NDR/", validate_ndr},
4014 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4015 {NULL, NULL}
4018 /***********************************************************************
4019 Function to look at every entry in the tdb and validate it as far as
4020 possible.
4021 ***********************************************************************/
4023 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4025 int i;
4026 unsigned int max_key_len = 1024;
4027 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4029 /* Paranoia check. */
4030 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4031 max_key_len = 1024 * 1024;
4033 if (kbuf.dsize > max_key_len) {
4034 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4035 "(%u) > (%u)\n\n",
4036 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4037 return 1;
4040 for (i = 0; key_val[i].keyname; i++) {
4041 size_t namelen = strlen(key_val[i].keyname);
4042 if (kbuf.dsize >= namelen && (
4043 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4044 TALLOC_CTX *mem_ctx;
4045 char *keystr;
4046 int ret;
4048 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4049 if (!keystr) {
4050 return 1;
4052 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4053 keystr[kbuf.dsize] = '\0';
4055 mem_ctx = talloc_init("validate_ctx");
4056 if (!mem_ctx) {
4057 SAFE_FREE(keystr);
4058 return 1;
4061 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4062 v_state);
4064 SAFE_FREE(keystr);
4065 talloc_destroy(mem_ctx);
4066 return ret;
4070 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4071 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4072 DEBUG(0,("data :\n"));
4073 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4074 v_state->unknown_key = true;
4075 v_state->success = false;
4076 return 1; /* terminate. */
4079 static void validate_panic(const char *const why)
4081 DEBUG(0,("validating cache: would panic %s\n", why ));
4082 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4083 exit(47);
4086 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4087 TDB_DATA key,
4088 TDB_DATA data,
4089 void *state)
4091 uint64_t ctimeout;
4092 TDB_DATA blob;
4094 if (is_non_centry_key(key)) {
4095 return 0;
4098 if (data.dptr == NULL || data.dsize == 0) {
4099 if (tdb_delete(tdb, key) < 0) {
4100 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4101 key.dptr));
4102 return 1;
4106 /* add timeout to blob (uint64_t) */
4107 blob.dsize = data.dsize + 8;
4109 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4110 if (blob.dptr == NULL) {
4111 return 1;
4113 memset(blob.dptr, 0, blob.dsize);
4115 /* copy status and seqnum */
4116 memcpy(blob.dptr, data.dptr, 8);
4118 /* add timeout */
4119 ctimeout = lp_winbind_cache_time() + time(NULL);
4120 SBVAL(blob.dptr, 8, ctimeout);
4122 /* copy the rest */
4123 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4125 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4126 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4127 key.dptr));
4128 SAFE_FREE(blob.dptr);
4129 return 1;
4132 SAFE_FREE(blob.dptr);
4133 return 0;
4136 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4138 int rc;
4140 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4142 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4143 if (rc < 0) {
4144 return false;
4147 return true;
4150 /***********************************************************************
4151 Try and validate every entry in the winbindd cache. If we fail here,
4152 delete the cache tdb and return non-zero.
4153 ***********************************************************************/
4155 int winbindd_validate_cache(void)
4157 int ret = -1;
4158 const char *tdb_path = state_path("winbindd_cache.tdb");
4159 TDB_CONTEXT *tdb = NULL;
4160 uint32_t vers_id;
4161 bool ok;
4163 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4164 smb_panic_fn = validate_panic;
4166 tdb = tdb_open_log(tdb_path,
4167 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4168 TDB_INCOMPATIBLE_HASH |
4169 ( lp_winbind_offline_logon()
4170 ? TDB_DEFAULT
4171 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4172 O_RDWR|O_CREAT,
4173 0600);
4174 if (!tdb) {
4175 DEBUG(0, ("winbindd_validate_cache: "
4176 "error opening/initializing tdb\n"));
4177 goto done;
4180 /* Version check and upgrade code. */
4181 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4182 DEBUG(10, ("Fresh database\n"));
4183 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4184 vers_id = WINBINDD_CACHE_VERSION;
4187 if (vers_id != WINBINDD_CACHE_VERSION) {
4188 if (vers_id == WINBINDD_CACHE_VER1) {
4189 ok = wbcache_upgrade_v1_to_v2(tdb);
4190 if (!ok) {
4191 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4192 unlink(tdb_path);
4193 goto done;
4196 tdb_store_uint32(tdb,
4197 WINBINDD_CACHE_VERSION_KEYSTR,
4198 WINBINDD_CACHE_VERSION);
4199 vers_id = WINBINDD_CACHE_VER2;
4203 tdb_close(tdb);
4205 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4207 if (ret != 0) {
4208 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4209 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4210 unlink(tdb_path);
4213 done:
4214 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4215 smb_panic_fn = smb_panic;
4216 return ret;
4219 /***********************************************************************
4220 Try and validate every entry in the winbindd cache.
4221 ***********************************************************************/
4223 int winbindd_validate_cache_nobackup(void)
4225 int ret = -1;
4226 const char *tdb_path = state_path("winbindd_cache.tdb");
4228 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4229 smb_panic_fn = validate_panic;
4232 if (wcache == NULL || wcache->tdb == NULL) {
4233 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4234 } else {
4235 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4238 if (ret != 0) {
4239 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4240 "successful.\n"));
4243 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4244 "function\n"));
4245 smb_panic_fn = smb_panic;
4246 return ret;
4249 bool winbindd_cache_validate_and_initialize(void)
4251 close_winbindd_cache();
4253 if (lp_winbind_offline_logon()) {
4254 if (winbindd_validate_cache() < 0) {
4255 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4256 "could be restored.\n"));
4260 return initialize_winbindd_cache();
4263 /*********************************************************************
4264 ********************************************************************/
4266 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4267 struct winbindd_tdc_domain **domains,
4268 size_t *num_domains )
4270 struct winbindd_tdc_domain *list = NULL;
4271 size_t idx;
4272 int i;
4273 bool set_only = false;
4275 /* don't allow duplicates */
4277 idx = *num_domains;
4278 list = *domains;
4280 for ( i=0; i< (*num_domains); i++ ) {
4281 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4282 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4283 new_dom->name));
4284 idx = i;
4285 set_only = true;
4287 break;
4291 if ( !set_only ) {
4292 if ( !*domains ) {
4293 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4294 idx = 0;
4295 } else {
4296 list = talloc_realloc( *domains, *domains,
4297 struct winbindd_tdc_domain,
4298 (*num_domains)+1);
4299 idx = *num_domains;
4302 ZERO_STRUCT( list[idx] );
4305 if ( !list )
4306 return false;
4308 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4309 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4311 if ( !is_null_sid( &new_dom->sid ) ) {
4312 sid_copy( &list[idx].sid, &new_dom->sid );
4313 } else {
4314 sid_copy(&list[idx].sid, &global_sid_NULL);
4317 if ( new_dom->domain_flags != 0x0 )
4318 list[idx].trust_flags = new_dom->domain_flags;
4320 if ( new_dom->domain_type != 0x0 )
4321 list[idx].trust_type = new_dom->domain_type;
4323 if ( new_dom->domain_trust_attribs != 0x0 )
4324 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4326 if ( !set_only ) {
4327 *domains = list;
4328 *num_domains = idx + 1;
4331 return true;
4334 /*********************************************************************
4335 ********************************************************************/
4337 static TDB_DATA make_tdc_key( const char *domain_name )
4339 char *keystr = NULL;
4340 TDB_DATA key = { NULL, 0 };
4342 if ( !domain_name ) {
4343 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4344 return key;
4347 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4348 return key;
4350 key = string_term_tdb_data(keystr);
4352 return key;
4355 /*********************************************************************
4356 ********************************************************************/
4358 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4359 size_t num_domains,
4360 unsigned char **buf )
4362 unsigned char *buffer = NULL;
4363 int len = 0;
4364 int buflen = 0;
4365 int i = 0;
4367 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4368 (int)num_domains));
4370 buflen = 0;
4372 again:
4373 len = 0;
4375 /* Store the number of array items first */
4376 len += tdb_pack( buffer+len, buflen-len, "d",
4377 num_domains );
4379 /* now pack each domain trust record */
4380 for ( i=0; i<num_domains; i++ ) {
4382 fstring tmp;
4384 if ( buflen > 0 ) {
4385 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4386 domains[i].domain_name,
4387 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4390 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4391 domains[i].domain_name,
4392 domains[i].dns_name,
4393 sid_to_fstring(tmp, &domains[i].sid),
4394 domains[i].trust_flags,
4395 domains[i].trust_attribs,
4396 domains[i].trust_type );
4399 if ( buflen < len ) {
4400 SAFE_FREE(buffer);
4401 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4402 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4403 buflen = -1;
4404 goto done;
4406 buflen = len;
4407 goto again;
4410 *buf = buffer;
4412 done:
4413 return buflen;
4416 /*********************************************************************
4417 ********************************************************************/
4419 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4420 struct winbindd_tdc_domain **domains )
4422 fstring domain_name, dns_name, sid_string;
4423 uint32 type, attribs, flags;
4424 int num_domains;
4425 int len = 0;
4426 int i;
4427 struct winbindd_tdc_domain *list = NULL;
4429 /* get the number of domains */
4430 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4431 if ( len == -1 ) {
4432 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4433 return 0;
4436 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4437 if ( !list ) {
4438 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4439 return 0;
4442 for ( i=0; i<num_domains; i++ ) {
4443 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4444 domain_name,
4445 dns_name,
4446 sid_string,
4447 &flags,
4448 &attribs,
4449 &type );
4451 if ( len == -1 ) {
4452 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4453 TALLOC_FREE( list );
4454 return 0;
4457 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4458 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4459 domain_name, dns_name, sid_string,
4460 flags, attribs, type));
4462 list[i].domain_name = talloc_strdup( list, domain_name );
4463 list[i].dns_name = talloc_strdup( list, dns_name );
4464 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4465 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4466 domain_name));
4468 list[i].trust_flags = flags;
4469 list[i].trust_attribs = attribs;
4470 list[i].trust_type = type;
4473 *domains = list;
4475 return num_domains;
4478 /*********************************************************************
4479 ********************************************************************/
4481 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4483 TDB_DATA key = make_tdc_key( lp_workgroup() );
4484 TDB_DATA data = { NULL, 0 };
4485 int ret;
4487 if ( !key.dptr )
4488 return false;
4490 /* See if we were asked to delete the cache entry */
4492 if ( !domains ) {
4493 ret = tdb_delete( wcache->tdb, key );
4494 goto done;
4497 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4499 if ( !data.dptr ) {
4500 ret = -1;
4501 goto done;
4504 ret = tdb_store( wcache->tdb, key, data, 0 );
4506 done:
4507 SAFE_FREE( data.dptr );
4508 SAFE_FREE( key.dptr );
4510 return ( ret == 0 );
4513 /*********************************************************************
4514 ********************************************************************/
4516 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4518 TDB_DATA key = make_tdc_key( lp_workgroup() );
4519 TDB_DATA data = { NULL, 0 };
4521 *domains = NULL;
4522 *num_domains = 0;
4524 if ( !key.dptr )
4525 return false;
4527 data = tdb_fetch_compat( wcache->tdb, key );
4529 SAFE_FREE( key.dptr );
4531 if ( !data.dptr )
4532 return false;
4534 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4536 SAFE_FREE( data.dptr );
4538 if ( !*domains )
4539 return false;
4541 return true;
4544 /*********************************************************************
4545 ********************************************************************/
4547 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4549 struct winbindd_tdc_domain *dom_list = NULL;
4550 size_t num_domains = 0;
4551 bool ret = false;
4553 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4554 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4555 domain->name, domain->alt_name,
4556 sid_string_dbg(&domain->sid),
4557 domain->domain_flags,
4558 domain->domain_trust_attribs,
4559 domain->domain_type));
4561 if ( !init_wcache() ) {
4562 return false;
4565 /* fetch the list */
4567 wcache_tdc_fetch_list( &dom_list, &num_domains );
4569 /* add the new domain */
4571 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4572 goto done;
4575 /* pack the domain */
4577 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4578 goto done;
4581 /* Success */
4583 ret = true;
4584 done:
4585 TALLOC_FREE( dom_list );
4587 return ret;
4590 /*********************************************************************
4591 ********************************************************************/
4593 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4595 struct winbindd_tdc_domain *dom_list = NULL;
4596 size_t num_domains = 0;
4597 int i;
4598 struct winbindd_tdc_domain *d = NULL;
4600 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4602 if ( !init_wcache() ) {
4603 return NULL;
4606 /* fetch the list */
4608 wcache_tdc_fetch_list( &dom_list, &num_domains );
4610 for ( i=0; i<num_domains; i++ ) {
4611 if ( strequal(name, dom_list[i].domain_name) ||
4612 strequal(name, dom_list[i].dns_name) )
4614 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4615 name));
4617 d = talloc( ctx, struct winbindd_tdc_domain );
4618 if ( !d )
4619 break;
4621 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4622 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4623 sid_copy( &d->sid, &dom_list[i].sid );
4624 d->trust_flags = dom_list[i].trust_flags;
4625 d->trust_type = dom_list[i].trust_type;
4626 d->trust_attribs = dom_list[i].trust_attribs;
4628 break;
4632 TALLOC_FREE( dom_list );
4634 return d;
4637 /*********************************************************************
4638 ********************************************************************/
4640 struct winbindd_tdc_domain*
4641 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4642 const struct dom_sid *sid)
4644 struct winbindd_tdc_domain *dom_list = NULL;
4645 size_t num_domains = 0;
4646 int i;
4647 struct winbindd_tdc_domain *d = NULL;
4649 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4650 sid_string_dbg(sid)));
4652 if (!init_wcache()) {
4653 return NULL;
4656 /* fetch the list */
4658 wcache_tdc_fetch_list(&dom_list, &num_domains);
4660 for (i = 0; i<num_domains; i++) {
4661 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4662 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4663 "Found domain %s for SID %s\n",
4664 dom_list[i].domain_name,
4665 sid_string_dbg(sid)));
4667 d = talloc(ctx, struct winbindd_tdc_domain);
4668 if (!d)
4669 break;
4671 d->domain_name = talloc_strdup(d,
4672 dom_list[i].domain_name);
4674 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4675 sid_copy(&d->sid, &dom_list[i].sid);
4676 d->trust_flags = dom_list[i].trust_flags;
4677 d->trust_type = dom_list[i].trust_type;
4678 d->trust_attribs = dom_list[i].trust_attribs;
4680 break;
4684 TALLOC_FREE(dom_list);
4686 return d;
4690 /*********************************************************************
4691 ********************************************************************/
4693 void wcache_tdc_clear( void )
4695 if ( !init_wcache() )
4696 return;
4698 wcache_tdc_store_list( NULL, 0 );
4700 return;
4704 /*********************************************************************
4705 ********************************************************************/
4707 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4708 NTSTATUS status,
4709 const struct dom_sid *user_sid,
4710 const char *homedir,
4711 const char *shell,
4712 const char *gecos,
4713 uint32 gid)
4715 struct cache_entry *centry;
4716 fstring tmp;
4718 if ( (centry = centry_start(domain, status)) == NULL )
4719 return;
4721 centry_put_string( centry, homedir );
4722 centry_put_string( centry, shell );
4723 centry_put_string( centry, gecos );
4724 centry_put_uint32( centry, gid );
4726 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4728 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4730 centry_free(centry);
4733 #ifdef HAVE_ADS
4735 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4736 const struct dom_sid *user_sid,
4737 TALLOC_CTX *ctx,
4738 const char **homedir, const char **shell,
4739 const char **gecos, gid_t *p_gid)
4741 struct winbind_cache *cache = get_cache(domain);
4742 struct cache_entry *centry = NULL;
4743 NTSTATUS nt_status;
4744 fstring tmp;
4746 if (!cache->tdb)
4747 goto do_query;
4749 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4750 sid_to_fstring(tmp, user_sid));
4752 if (!centry)
4753 goto do_query;
4755 *homedir = centry_string( centry, ctx );
4756 *shell = centry_string( centry, ctx );
4757 *gecos = centry_string( centry, ctx );
4758 *p_gid = centry_uint32( centry );
4760 centry_free(centry);
4762 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4763 sid_string_dbg(user_sid)));
4765 return NT_STATUS_OK;
4767 do_query:
4769 nt_status = nss_get_info( domain->name, user_sid, ctx,
4770 homedir, shell, gecos, p_gid );
4772 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4774 if ( NT_STATUS_IS_OK(nt_status) ) {
4775 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4776 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4777 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4778 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4780 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4781 *homedir, *shell, *gecos, *p_gid );
4784 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4785 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4786 domain->name ));
4787 set_domain_offline( domain );
4790 return nt_status;
4793 #endif
4795 /* the cache backend methods are exposed via this structure */
4796 struct winbindd_methods cache_methods = {
4797 true,
4798 query_user_list,
4799 enum_dom_groups,
4800 enum_local_groups,
4801 name_to_sid,
4802 sid_to_name,
4803 rids_to_names,
4804 query_user,
4805 lookup_usergroups,
4806 lookup_useraliases,
4807 lookup_groupmem,
4808 sequence_number,
4809 lockout_policy,
4810 password_policy,
4811 trusted_domains
4814 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4815 uint32_t opnum, const DATA_BLOB *req,
4816 TDB_DATA *pkey)
4818 char *key;
4819 size_t keylen;
4821 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4822 if (key == NULL) {
4823 return false;
4825 keylen = talloc_get_size(key) - 1;
4827 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4828 if (key == NULL) {
4829 return false;
4831 memcpy(key + keylen, req->data, req->length);
4833 pkey->dptr = (uint8_t *)key;
4834 pkey->dsize = talloc_get_size(key);
4835 return true;
4838 static bool wcache_opnum_cacheable(uint32_t opnum)
4840 switch (opnum) {
4841 case NDR_WBINT_PING:
4842 case NDR_WBINT_QUERYSEQUENCENUMBER:
4843 case NDR_WBINT_ALLOCATEUID:
4844 case NDR_WBINT_ALLOCATEGID:
4845 case NDR_WBINT_CHECKMACHINEACCOUNT:
4846 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4847 case NDR_WBINT_PINGDC:
4848 return false;
4850 return true;
4853 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4854 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4856 TDB_DATA key, data;
4857 bool ret = false;
4859 if (!wcache_opnum_cacheable(opnum) ||
4860 is_my_own_sam_domain(domain) ||
4861 is_builtin_domain(domain)) {
4862 return false;
4865 if (wcache->tdb == NULL) {
4866 return false;
4869 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4870 return false;
4872 data = tdb_fetch_compat(wcache->tdb, key);
4873 TALLOC_FREE(key.dptr);
4875 if (data.dptr == NULL) {
4876 return false;
4878 if (data.dsize < 12) {
4879 goto fail;
4882 if (!is_domain_offline(domain)) {
4883 uint32_t entry_seqnum, dom_seqnum, last_check;
4884 uint64_t entry_timeout;
4886 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4887 &last_check)) {
4888 goto fail;
4890 entry_seqnum = IVAL(data.dptr, 0);
4891 if (entry_seqnum != dom_seqnum) {
4892 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4893 (int)entry_seqnum));
4894 goto fail;
4896 entry_timeout = BVAL(data.dptr, 4);
4897 if (time(NULL) > entry_timeout) {
4898 DEBUG(10, ("Entry has timed out\n"));
4899 goto fail;
4903 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4904 data.dsize - 12);
4905 if (resp->data == NULL) {
4906 DEBUG(10, ("talloc failed\n"));
4907 goto fail;
4909 resp->length = data.dsize - 12;
4911 ret = true;
4912 fail:
4913 SAFE_FREE(data.dptr);
4914 return ret;
4917 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4918 const DATA_BLOB *req, const DATA_BLOB *resp)
4920 TDB_DATA key, data;
4921 uint32_t dom_seqnum, last_check;
4922 uint64_t timeout;
4924 if (!wcache_opnum_cacheable(opnum) ||
4925 is_my_own_sam_domain(domain) ||
4926 is_builtin_domain(domain)) {
4927 return;
4930 if (wcache->tdb == NULL) {
4931 return;
4934 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4935 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4936 domain->name));
4937 return;
4940 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4941 return;
4944 timeout = time(NULL) + lp_winbind_cache_time();
4946 data.dsize = resp->length + 12;
4947 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4948 if (data.dptr == NULL) {
4949 goto done;
4952 SIVAL(data.dptr, 0, dom_seqnum);
4953 SBVAL(data.dptr, 4, timeout);
4954 memcpy(data.dptr + 12, resp->data, resp->length);
4956 tdb_store(wcache->tdb, key, data, 0);
4958 done:
4959 TALLOC_FREE(key.dptr);
4960 return;