s4:dsdb/acl_util: add dsdb_module_schedule_sd_propagation()
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blobe3406a5858f2a1f74ed775df8b173a5d4b150651
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_our_sam(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_our_sam(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
946 centry_put_uint32(centry, type);
947 centry_put_sid(centry, sid);
948 fstrcpy(uname, name);
949 (void)strupper_m(uname);
950 centry_end(centry, "NS/%s/%s", domain_name, uname);
951 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
952 uname, sid_string_dbg(sid), nt_errstr(status)));
953 centry_free(centry);
956 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
957 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring sid_string;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if (NT_STATUS_IS_OK(status)) {
967 centry_put_uint32(centry, type);
968 centry_put_string(centry, domain_name);
969 centry_put_string(centry, name);
972 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
973 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
974 domain_name, name, nt_errstr(status)));
975 centry_free(centry);
979 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
980 struct wbint_userinfo *info)
982 struct cache_entry *centry;
983 fstring sid_string;
985 if (is_null_sid(&info->user_sid)) {
986 return;
989 centry = centry_start(domain, status);
990 if (!centry)
991 return;
992 centry_put_string(centry, info->acct_name);
993 centry_put_string(centry, info->full_name);
994 centry_put_string(centry, info->homedir);
995 centry_put_string(centry, info->shell);
996 centry_put_uint32(centry, info->primary_gid);
997 centry_put_sid(centry, &info->user_sid);
998 centry_put_sid(centry, &info->group_sid);
999 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1000 &info->user_sid));
1001 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 centry_free(centry);
1005 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1006 NTSTATUS status,
1007 struct samr_DomInfo12 *lockout_policy)
1009 struct cache_entry *centry;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1015 centry_put_nttime(centry, lockout_policy->lockout_duration);
1016 centry_put_nttime(centry, lockout_policy->lockout_window);
1017 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1019 centry_end(centry, "LOC_POL/%s", domain->name);
1021 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1023 centry_free(centry);
1028 static void wcache_save_password_policy(struct winbindd_domain *domain,
1029 NTSTATUS status,
1030 struct samr_DomInfo1 *policy)
1032 struct cache_entry *centry;
1034 centry = centry_start(domain, status);
1035 if (!centry)
1036 return;
1038 centry_put_uint16(centry, policy->min_password_length);
1039 centry_put_uint16(centry, policy->password_history_length);
1040 centry_put_uint32(centry, policy->password_properties);
1041 centry_put_nttime(centry, policy->max_password_age);
1042 centry_put_nttime(centry, policy->min_password_age);
1044 centry_end(centry, "PWD_POL/%s", domain->name);
1046 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1048 centry_free(centry);
1051 /***************************************************************************
1052 ***************************************************************************/
1054 static void wcache_save_username_alias(struct winbindd_domain *domain,
1055 NTSTATUS status,
1056 const char *name, const char *alias)
1058 struct cache_entry *centry;
1059 fstring uname;
1061 if ( (centry = centry_start(domain, status)) == NULL )
1062 return;
1064 centry_put_string( centry, alias );
1066 fstrcpy(uname, name);
1067 (void)strupper_m(uname);
1068 centry_end(centry, "NSS/NA/%s", uname);
1070 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1072 centry_free(centry);
1075 static void wcache_save_alias_username(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *alias, const char *name)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, name );
1087 fstrcpy(uname, alias);
1088 (void)strupper_m(uname);
1089 centry_end(centry, "NSS/AN/%s", uname);
1091 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1093 centry_free(centry);
1096 /***************************************************************************
1097 ***************************************************************************/
1099 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1100 struct winbindd_domain *domain,
1101 const char *name, char **alias )
1103 struct winbind_cache *cache = get_cache(domain);
1104 struct cache_entry *centry = NULL;
1105 NTSTATUS status;
1106 char *upper_name;
1108 if ( domain->internal )
1109 return NT_STATUS_NOT_SUPPORTED;
1111 if (!cache->tdb)
1112 goto do_query;
1114 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1115 return NT_STATUS_NO_MEMORY;
1116 if (!strupper_m(upper_name)) {
1117 SAFE_FREE(upper_name);
1118 return NT_STATUS_INVALID_PARAMETER;
1121 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1123 SAFE_FREE( upper_name );
1125 if (!centry)
1126 goto do_query;
1128 status = centry->status;
1130 if (!NT_STATUS_IS_OK(status)) {
1131 centry_free(centry);
1132 return status;
1135 *alias = centry_string( centry, mem_ctx );
1137 centry_free(centry);
1139 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1140 name, *alias ? *alias : "(none)"));
1142 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1144 do_query:
1146 /* If its not in cache and we are offline, then fail */
1148 if ( get_global_winbindd_state_offline() || !domain->online ) {
1149 DEBUG(8,("resolve_username_to_alias: rejecting query "
1150 "in offline mode\n"));
1151 return NT_STATUS_NOT_FOUND;
1154 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1156 if ( NT_STATUS_IS_OK( status ) ) {
1157 wcache_save_username_alias(domain, status, name, *alias);
1160 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1161 wcache_save_username_alias(domain, status, name, "(NULL)");
1164 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1165 nt_errstr(status)));
1167 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1168 set_domain_offline( domain );
1171 return status;
1174 /***************************************************************************
1175 ***************************************************************************/
1177 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1178 struct winbindd_domain *domain,
1179 const char *alias, char **name )
1181 struct winbind_cache *cache = get_cache(domain);
1182 struct cache_entry *centry = NULL;
1183 NTSTATUS status;
1184 char *upper_name;
1186 if ( domain->internal )
1187 return NT_STATUS_NOT_SUPPORTED;
1189 if (!cache->tdb)
1190 goto do_query;
1192 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1193 return NT_STATUS_NO_MEMORY;
1194 if (!strupper_m(upper_name)) {
1195 SAFE_FREE(upper_name);
1196 return NT_STATUS_INVALID_PARAMETER;
1199 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1201 SAFE_FREE( upper_name );
1203 if (!centry)
1204 goto do_query;
1206 status = centry->status;
1208 if (!NT_STATUS_IS_OK(status)) {
1209 centry_free(centry);
1210 return status;
1213 *name = centry_string( centry, mem_ctx );
1215 centry_free(centry);
1217 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1218 alias, *name ? *name : "(none)"));
1220 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1222 do_query:
1224 /* If its not in cache and we are offline, then fail */
1226 if ( get_global_winbindd_state_offline() || !domain->online ) {
1227 DEBUG(8,("resolve_alias_to_username: rejecting query "
1228 "in offline mode\n"));
1229 return NT_STATUS_NOT_FOUND;
1232 /* an alias cannot contain a domain prefix or '@' */
1234 if (strchr(alias, '\\') || strchr(alias, '@')) {
1235 DEBUG(10,("resolve_alias_to_username: skipping fully "
1236 "qualified name %s\n", alias));
1237 return NT_STATUS_OBJECT_NAME_INVALID;
1240 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1242 if ( NT_STATUS_IS_OK( status ) ) {
1243 wcache_save_alias_username( domain, status, alias, *name );
1246 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1247 wcache_save_alias_username(domain, status, alias, "(NULL)");
1250 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1251 nt_errstr(status)));
1253 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1254 set_domain_offline( domain );
1257 return status;
1260 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1262 struct winbind_cache *cache = get_cache(domain);
1263 TDB_DATA data;
1264 fstring key_str, tmp;
1265 uint32 rid;
1267 if (!cache->tdb) {
1268 return NT_STATUS_INTERNAL_DB_ERROR;
1271 if (is_null_sid(sid)) {
1272 return NT_STATUS_INVALID_SID;
1275 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1276 return NT_STATUS_INVALID_SID;
1279 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1281 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1282 if (!data.dptr) {
1283 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1286 SAFE_FREE(data.dptr);
1287 return NT_STATUS_OK;
1290 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1291 as new salted ones. */
1293 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1294 TALLOC_CTX *mem_ctx,
1295 const struct dom_sid *sid,
1296 const uint8 **cached_nt_pass,
1297 const uint8 **cached_salt)
1299 struct winbind_cache *cache = get_cache(domain);
1300 struct cache_entry *centry = NULL;
1301 NTSTATUS status;
1302 uint32 rid;
1303 fstring tmp;
1305 if (!cache->tdb) {
1306 return NT_STATUS_INTERNAL_DB_ERROR;
1309 if (is_null_sid(sid)) {
1310 return NT_STATUS_INVALID_SID;
1313 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1314 return NT_STATUS_INVALID_SID;
1317 /* Try and get a salted cred first. If we can't
1318 fall back to an unsalted cred. */
1320 centry = wcache_fetch(cache, domain, "CRED/%s",
1321 sid_to_fstring(tmp, sid));
1322 if (!centry) {
1323 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1324 sid_string_dbg(sid)));
1325 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1329 * We don't use the time element at this moment,
1330 * but we have to consume it, so that we don't
1331 * neet to change the disk format of the cache.
1333 (void)centry_time(centry);
1335 /* In the salted case this isn't actually the nt_hash itself,
1336 but the MD5 of the salt + nt_hash. Let the caller
1337 sort this out. It can tell as we only return the cached_salt
1338 if we are returning a salted cred. */
1340 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1341 if (*cached_nt_pass == NULL) {
1342 fstring sidstr;
1344 sid_to_fstring(sidstr, sid);
1346 /* Bad (old) cred cache. Delete and pretend we
1347 don't have it. */
1348 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1349 sidstr));
1350 wcache_delete("CRED/%s", sidstr);
1351 centry_free(centry);
1352 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1355 /* We only have 17 bytes more data in the salted cred case. */
1356 if (centry->len - centry->ofs == 17) {
1357 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1358 } else {
1359 *cached_salt = NULL;
1362 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1363 if (*cached_salt) {
1364 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1367 status = centry->status;
1369 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1370 sid_string_dbg(sid), nt_errstr(status) ));
1372 centry_free(centry);
1373 return status;
1376 /* Store creds for a SID - only writes out new salted ones. */
1378 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1379 const struct dom_sid *sid,
1380 const uint8 nt_pass[NT_HASH_LEN])
1382 struct cache_entry *centry;
1383 fstring sid_string;
1384 uint32 rid;
1385 uint8 cred_salt[NT_HASH_LEN];
1386 uint8 salted_hash[NT_HASH_LEN];
1388 if (is_null_sid(sid)) {
1389 return NT_STATUS_INVALID_SID;
1392 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1393 return NT_STATUS_INVALID_SID;
1396 centry = centry_start(domain, NT_STATUS_OK);
1397 if (!centry) {
1398 return NT_STATUS_INTERNAL_DB_ERROR;
1401 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1403 centry_put_time(centry, time(NULL));
1405 /* Create a salt and then salt the hash. */
1406 generate_random_buffer(cred_salt, NT_HASH_LEN);
1407 E_md5hash(cred_salt, nt_pass, salted_hash);
1409 centry_put_hash16(centry, salted_hash);
1410 centry_put_hash16(centry, cred_salt);
1411 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1413 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1415 centry_free(centry);
1417 return NT_STATUS_OK;
1421 /* Query display info. This is the basic user list fn */
1422 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1423 TALLOC_CTX *mem_ctx,
1424 uint32 *num_entries,
1425 struct wbint_userinfo **info)
1427 struct winbind_cache *cache = get_cache(domain);
1428 struct cache_entry *centry = NULL;
1429 NTSTATUS status;
1430 unsigned int i, retry;
1431 bool old_status = domain->online;
1433 if (!cache->tdb)
1434 goto do_query;
1436 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1437 if (!centry)
1438 goto do_query;
1440 do_fetch_cache:
1441 *num_entries = centry_uint32(centry);
1443 if (*num_entries == 0)
1444 goto do_cached;
1446 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1447 if (! (*info)) {
1448 smb_panic_fn("query_user_list out of memory");
1450 for (i=0; i<(*num_entries); i++) {
1451 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1452 (*info)[i].full_name = centry_string(centry, mem_ctx);
1453 (*info)[i].homedir = centry_string(centry, mem_ctx);
1454 (*info)[i].shell = centry_string(centry, mem_ctx);
1455 centry_sid(centry, &(*info)[i].user_sid);
1456 centry_sid(centry, &(*info)[i].group_sid);
1459 do_cached:
1460 status = centry->status;
1462 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1463 domain->name, nt_errstr(status) ));
1465 centry_free(centry);
1466 return status;
1468 do_query:
1469 *num_entries = 0;
1470 *info = NULL;
1472 /* Return status value returned by seq number check */
1474 if (!NT_STATUS_IS_OK(domain->last_status))
1475 return domain->last_status;
1477 /* Put the query_user_list() in a retry loop. There appears to be
1478 * some bug either with Windows 2000 or Samba's handling of large
1479 * rpc replies. This manifests itself as sudden disconnection
1480 * at a random point in the enumeration of a large (60k) user list.
1481 * The retry loop simply tries the operation again. )-: It's not
1482 * pretty but an acceptable workaround until we work out what the
1483 * real problem is. */
1485 retry = 0;
1486 do {
1488 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1489 domain->name ));
1491 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1492 if (!NT_STATUS_IS_OK(status)) {
1493 DEBUG(3, ("query_user_list: returned 0x%08x, "
1494 "retrying\n", NT_STATUS_V(status)));
1496 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1497 DEBUG(3, ("query_user_list: flushing "
1498 "connection cache\n"));
1499 invalidate_cm_connection(&domain->conn);
1501 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1502 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1503 if (!domain->internal && old_status) {
1504 set_domain_offline(domain);
1506 /* store partial response. */
1507 if (*num_entries > 0) {
1509 * humm, what about the status used for cache?
1510 * Should it be NT_STATUS_OK?
1512 break;
1515 * domain is offline now, and there is no user entries,
1516 * try to fetch from cache again.
1518 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1519 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1520 /* partial response... */
1521 if (!centry) {
1522 goto skip_save;
1523 } else {
1524 goto do_fetch_cache;
1526 } else {
1527 goto skip_save;
1531 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1532 (retry++ < 5));
1534 /* and save it */
1535 refresh_sequence_number(domain, false);
1536 if (!NT_STATUS_IS_OK(status)) {
1537 return status;
1539 centry = centry_start(domain, status);
1540 if (!centry)
1541 goto skip_save;
1542 centry_put_uint32(centry, *num_entries);
1543 for (i=0; i<(*num_entries); i++) {
1544 centry_put_string(centry, (*info)[i].acct_name);
1545 centry_put_string(centry, (*info)[i].full_name);
1546 centry_put_string(centry, (*info)[i].homedir);
1547 centry_put_string(centry, (*info)[i].shell);
1548 centry_put_sid(centry, &(*info)[i].user_sid);
1549 centry_put_sid(centry, &(*info)[i].group_sid);
1550 if (domain->backend && domain->backend->consistent) {
1551 /* when the backend is consistent we can pre-prime some mappings */
1552 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1553 domain->name,
1554 (*info)[i].acct_name,
1555 &(*info)[i].user_sid,
1556 SID_NAME_USER);
1557 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1558 &(*info)[i].user_sid,
1559 domain->name,
1560 (*info)[i].acct_name,
1561 SID_NAME_USER);
1562 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1565 centry_end(centry, "UL/%s", domain->name);
1566 centry_free(centry);
1568 skip_save:
1569 return status;
1572 /* list all domain groups */
1573 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1574 TALLOC_CTX *mem_ctx,
1575 uint32 *num_entries,
1576 struct wb_acct_info **info)
1578 struct winbind_cache *cache = get_cache(domain);
1579 struct cache_entry *centry = NULL;
1580 NTSTATUS status;
1581 unsigned int i;
1582 bool old_status;
1584 old_status = domain->online;
1585 if (!cache->tdb)
1586 goto do_query;
1588 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1589 if (!centry)
1590 goto do_query;
1592 do_fetch_cache:
1593 *num_entries = centry_uint32(centry);
1595 if (*num_entries == 0)
1596 goto do_cached;
1598 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1599 if (! (*info)) {
1600 smb_panic_fn("enum_dom_groups out of memory");
1602 for (i=0; i<(*num_entries); i++) {
1603 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1604 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1605 (*info)[i].rid = centry_uint32(centry);
1608 do_cached:
1609 status = centry->status;
1611 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1612 domain->name, nt_errstr(status) ));
1614 centry_free(centry);
1615 return status;
1617 do_query:
1618 *num_entries = 0;
1619 *info = NULL;
1621 /* Return status value returned by seq number check */
1623 if (!NT_STATUS_IS_OK(domain->last_status))
1624 return domain->last_status;
1626 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1627 domain->name ));
1629 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1631 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1632 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1633 if (!domain->internal && old_status) {
1634 set_domain_offline(domain);
1636 if (cache->tdb &&
1637 !domain->online &&
1638 !domain->internal &&
1639 old_status) {
1640 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1641 if (centry) {
1642 goto do_fetch_cache;
1646 /* and save it */
1647 refresh_sequence_number(domain, false);
1648 if (!NT_STATUS_IS_OK(status)) {
1649 return status;
1651 centry = centry_start(domain, status);
1652 if (!centry)
1653 goto skip_save;
1654 centry_put_uint32(centry, *num_entries);
1655 for (i=0; i<(*num_entries); i++) {
1656 centry_put_string(centry, (*info)[i].acct_name);
1657 centry_put_string(centry, (*info)[i].acct_desc);
1658 centry_put_uint32(centry, (*info)[i].rid);
1660 centry_end(centry, "GL/%s/domain", domain->name);
1661 centry_free(centry);
1663 skip_save:
1664 return status;
1667 /* list all domain groups */
1668 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1669 TALLOC_CTX *mem_ctx,
1670 uint32 *num_entries,
1671 struct wb_acct_info **info)
1673 struct winbind_cache *cache = get_cache(domain);
1674 struct cache_entry *centry = NULL;
1675 NTSTATUS status;
1676 unsigned int i;
1677 bool old_status;
1679 old_status = domain->online;
1680 if (!cache->tdb)
1681 goto do_query;
1683 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1684 if (!centry)
1685 goto do_query;
1687 do_fetch_cache:
1688 *num_entries = centry_uint32(centry);
1690 if (*num_entries == 0)
1691 goto do_cached;
1693 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1694 if (! (*info)) {
1695 smb_panic_fn("enum_dom_groups out of memory");
1697 for (i=0; i<(*num_entries); i++) {
1698 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1699 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1700 (*info)[i].rid = centry_uint32(centry);
1703 do_cached:
1705 /* If we are returning cached data and the domain controller
1706 is down then we don't know whether the data is up to date
1707 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1708 indicate this. */
1710 if (wcache_server_down(domain)) {
1711 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1712 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1713 } else
1714 status = centry->status;
1716 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1717 domain->name, nt_errstr(status) ));
1719 centry_free(centry);
1720 return status;
1722 do_query:
1723 *num_entries = 0;
1724 *info = NULL;
1726 /* Return status value returned by seq number check */
1728 if (!NT_STATUS_IS_OK(domain->last_status))
1729 return domain->last_status;
1731 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1732 domain->name ));
1734 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1736 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1737 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1738 if (!domain->internal && old_status) {
1739 set_domain_offline(domain);
1741 if (cache->tdb &&
1742 !domain->internal &&
1743 !domain->online &&
1744 old_status) {
1745 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1746 if (centry) {
1747 goto do_fetch_cache;
1751 /* and save it */
1752 refresh_sequence_number(domain, false);
1753 if (!NT_STATUS_IS_OK(status)) {
1754 return status;
1756 centry = centry_start(domain, status);
1757 if (!centry)
1758 goto skip_save;
1759 centry_put_uint32(centry, *num_entries);
1760 for (i=0; i<(*num_entries); i++) {
1761 centry_put_string(centry, (*info)[i].acct_name);
1762 centry_put_string(centry, (*info)[i].acct_desc);
1763 centry_put_uint32(centry, (*info)[i].rid);
1765 centry_end(centry, "GL/%s/local", domain->name);
1766 centry_free(centry);
1768 skip_save:
1769 return status;
1772 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1773 const char *domain_name,
1774 const char *name,
1775 struct dom_sid *sid,
1776 enum lsa_SidType *type)
1778 struct winbind_cache *cache = get_cache(domain);
1779 struct cache_entry *centry;
1780 NTSTATUS status;
1781 char *uname;
1783 if (cache->tdb == NULL) {
1784 return NT_STATUS_NOT_FOUND;
1787 uname = talloc_strdup_upper(talloc_tos(), name);
1788 if (uname == NULL) {
1789 return NT_STATUS_NO_MEMORY;
1792 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1793 TALLOC_FREE(uname);
1794 if (centry == NULL) {
1795 return NT_STATUS_NOT_FOUND;
1798 status = centry->status;
1799 if (NT_STATUS_IS_OK(status)) {
1800 *type = (enum lsa_SidType)centry_uint32(centry);
1801 centry_sid(centry, sid);
1804 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1805 "%s\n", domain->name, nt_errstr(status) ));
1807 centry_free(centry);
1808 return status;
1811 /* convert a single name to a sid in a domain */
1812 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1813 TALLOC_CTX *mem_ctx,
1814 const char *domain_name,
1815 const char *name,
1816 uint32_t flags,
1817 struct dom_sid *sid,
1818 enum lsa_SidType *type)
1820 NTSTATUS status;
1821 bool old_status;
1823 old_status = domain->online;
1825 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1826 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1827 return status;
1830 ZERO_STRUCTP(sid);
1832 /* If the seq number check indicated that there is a problem
1833 * with this DC, then return that status... except for
1834 * access_denied. This is special because the dc may be in
1835 * "restrict anonymous = 1" mode, in which case it will deny
1836 * most unauthenticated operations, but *will* allow the LSA
1837 * name-to-sid that we try as a fallback. */
1839 if (!(NT_STATUS_IS_OK(domain->last_status)
1840 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1841 return domain->last_status;
1843 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1844 domain->name ));
1846 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1847 name, flags, sid, type);
1849 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1850 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1851 if (!domain->internal && old_status) {
1852 set_domain_offline(domain);
1854 if (!domain->internal &&
1855 !domain->online &&
1856 old_status) {
1857 NTSTATUS cache_status;
1858 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1859 return cache_status;
1862 /* and save it */
1863 refresh_sequence_number(domain, false);
1865 if (domain->online &&
1866 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1867 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1869 /* Only save the reverse mapping if this was not a UPN */
1870 if (!strchr(name, '@')) {
1871 if (!strupper_m(discard_const_p(char, domain_name))) {
1872 return NT_STATUS_INVALID_PARAMETER;
1874 (void)strlower_m(discard_const_p(char, name));
1875 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1879 return status;
1882 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1883 const struct dom_sid *sid,
1884 TALLOC_CTX *mem_ctx,
1885 char **domain_name,
1886 char **name,
1887 enum lsa_SidType *type)
1889 struct winbind_cache *cache = get_cache(domain);
1890 struct cache_entry *centry;
1891 char *sid_string;
1892 NTSTATUS status;
1894 if (cache->tdb == NULL) {
1895 return NT_STATUS_NOT_FOUND;
1898 sid_string = sid_string_tos(sid);
1899 if (sid_string == NULL) {
1900 return NT_STATUS_NO_MEMORY;
1903 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1904 TALLOC_FREE(sid_string);
1905 if (centry == NULL) {
1906 return NT_STATUS_NOT_FOUND;
1909 if (NT_STATUS_IS_OK(centry->status)) {
1910 *type = (enum lsa_SidType)centry_uint32(centry);
1911 *domain_name = centry_string(centry, mem_ctx);
1912 *name = centry_string(centry, mem_ctx);
1915 status = centry->status;
1916 centry_free(centry);
1918 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1919 "%s\n", domain->name, nt_errstr(status) ));
1921 return status;
1924 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1925 given */
1926 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1927 TALLOC_CTX *mem_ctx,
1928 const struct dom_sid *sid,
1929 char **domain_name,
1930 char **name,
1931 enum lsa_SidType *type)
1933 NTSTATUS status;
1934 bool old_status;
1936 old_status = domain->online;
1937 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1938 type);
1939 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1940 return status;
1943 *name = NULL;
1944 *domain_name = NULL;
1946 /* If the seq number check indicated that there is a problem
1947 * with this DC, then return that status... except for
1948 * access_denied. This is special because the dc may be in
1949 * "restrict anonymous = 1" mode, in which case it will deny
1950 * most unauthenticated operations, but *will* allow the LSA
1951 * sid-to-name that we try as a fallback. */
1953 if (!(NT_STATUS_IS_OK(domain->last_status)
1954 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1955 return domain->last_status;
1957 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1958 domain->name ));
1960 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1962 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1963 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1964 if (!domain->internal && old_status) {
1965 set_domain_offline(domain);
1967 if (!domain->internal &&
1968 !domain->online &&
1969 old_status) {
1970 NTSTATUS cache_status;
1971 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1972 domain_name, name, type);
1973 return cache_status;
1976 /* and save it */
1977 refresh_sequence_number(domain, false);
1978 if (!NT_STATUS_IS_OK(status)) {
1979 return status;
1981 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1983 /* We can't save the name to sid mapping here, as with sid history a
1984 * later name2sid would give the wrong sid. */
1986 return status;
1989 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1990 TALLOC_CTX *mem_ctx,
1991 const struct dom_sid *domain_sid,
1992 uint32 *rids,
1993 size_t num_rids,
1994 char **domain_name,
1995 char ***names,
1996 enum lsa_SidType **types)
1998 struct winbind_cache *cache = get_cache(domain);
1999 size_t i;
2000 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2001 bool have_mapped;
2002 bool have_unmapped;
2003 bool old_status;
2005 old_status = domain->online;
2006 *domain_name = NULL;
2007 *names = NULL;
2008 *types = NULL;
2010 if (!cache->tdb) {
2011 goto do_query;
2014 if (num_rids == 0) {
2015 return NT_STATUS_OK;
2018 *names = talloc_array(mem_ctx, char *, num_rids);
2019 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2021 if ((*names == NULL) || (*types == NULL)) {
2022 result = NT_STATUS_NO_MEMORY;
2023 goto error;
2026 have_mapped = have_unmapped = false;
2028 for (i=0; i<num_rids; i++) {
2029 struct dom_sid sid;
2030 struct cache_entry *centry;
2031 fstring tmp;
2033 if (!sid_compose(&sid, domain_sid, rids[i])) {
2034 result = NT_STATUS_INTERNAL_ERROR;
2035 goto error;
2038 centry = wcache_fetch(cache, domain, "SN/%s",
2039 sid_to_fstring(tmp, &sid));
2040 if (!centry) {
2041 goto do_query;
2044 (*types)[i] = SID_NAME_UNKNOWN;
2045 (*names)[i] = talloc_strdup(*names, "");
2047 if (NT_STATUS_IS_OK(centry->status)) {
2048 char *dom;
2049 have_mapped = true;
2050 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2052 dom = centry_string(centry, mem_ctx);
2053 if (*domain_name == NULL) {
2054 *domain_name = dom;
2055 } else {
2056 talloc_free(dom);
2059 (*names)[i] = centry_string(centry, *names);
2061 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2062 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2063 have_unmapped = true;
2065 } else {
2066 /* something's definitely wrong */
2067 result = centry->status;
2068 goto error;
2071 centry_free(centry);
2074 if (!have_mapped) {
2075 return NT_STATUS_NONE_MAPPED;
2077 if (!have_unmapped) {
2078 return NT_STATUS_OK;
2080 return STATUS_SOME_UNMAPPED;
2082 do_query:
2084 TALLOC_FREE(*names);
2085 TALLOC_FREE(*types);
2087 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2088 rids, num_rids, domain_name,
2089 names, types);
2091 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2092 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2093 if (!domain->internal && old_status) {
2094 set_domain_offline(domain);
2096 if (cache->tdb &&
2097 !domain->internal &&
2098 !domain->online &&
2099 old_status) {
2100 have_mapped = have_unmapped = false;
2102 for (i=0; i<num_rids; i++) {
2103 struct dom_sid sid;
2104 struct cache_entry *centry;
2105 fstring tmp;
2107 if (!sid_compose(&sid, domain_sid, rids[i])) {
2108 result = NT_STATUS_INTERNAL_ERROR;
2109 goto error;
2112 centry = wcache_fetch(cache, domain, "SN/%s",
2113 sid_to_fstring(tmp, &sid));
2114 if (!centry) {
2115 (*types)[i] = SID_NAME_UNKNOWN;
2116 (*names)[i] = talloc_strdup(*names, "");
2117 continue;
2120 (*types)[i] = SID_NAME_UNKNOWN;
2121 (*names)[i] = talloc_strdup(*names, "");
2123 if (NT_STATUS_IS_OK(centry->status)) {
2124 char *dom;
2125 have_mapped = true;
2126 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2128 dom = centry_string(centry, mem_ctx);
2129 if (*domain_name == NULL) {
2130 *domain_name = dom;
2131 } else {
2132 talloc_free(dom);
2135 (*names)[i] = centry_string(centry, *names);
2137 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2138 have_unmapped = true;
2140 } else {
2141 /* something's definitely wrong */
2142 result = centry->status;
2143 goto error;
2146 centry_free(centry);
2149 if (!have_mapped) {
2150 return NT_STATUS_NONE_MAPPED;
2152 if (!have_unmapped) {
2153 return NT_STATUS_OK;
2155 return STATUS_SOME_UNMAPPED;
2159 None of the queried rids has been found so save all negative entries
2161 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2162 for (i = 0; i < num_rids; i++) {
2163 struct dom_sid sid;
2164 const char *name = "";
2165 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2166 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2168 if (!sid_compose(&sid, domain_sid, rids[i])) {
2169 return NT_STATUS_INTERNAL_ERROR;
2172 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2173 name, type);
2176 return result;
2180 Some or all of the queried rids have been found.
2182 if (!NT_STATUS_IS_OK(result) &&
2183 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2184 return result;
2187 refresh_sequence_number(domain, false);
2189 for (i=0; i<num_rids; i++) {
2190 struct dom_sid sid;
2191 NTSTATUS status;
2193 if (!sid_compose(&sid, domain_sid, rids[i])) {
2194 result = NT_STATUS_INTERNAL_ERROR;
2195 goto error;
2198 status = (*types)[i] == SID_NAME_UNKNOWN ?
2199 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2201 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2202 (*names)[i], (*types)[i]);
2205 return result;
2207 error:
2208 TALLOC_FREE(*names);
2209 TALLOC_FREE(*types);
2210 return result;
2213 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2214 TALLOC_CTX *mem_ctx,
2215 const struct dom_sid *user_sid,
2216 struct wbint_userinfo *info)
2218 struct winbind_cache *cache = get_cache(domain);
2219 struct cache_entry *centry = NULL;
2220 NTSTATUS status;
2221 char *sid_string;
2223 if (cache->tdb == NULL) {
2224 return NT_STATUS_NOT_FOUND;
2227 sid_string = sid_string_tos(user_sid);
2228 if (sid_string == NULL) {
2229 return NT_STATUS_NO_MEMORY;
2232 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2233 TALLOC_FREE(sid_string);
2234 if (centry == NULL) {
2235 return NT_STATUS_NOT_FOUND;
2239 * If we have an access denied cache entry and a cached info3
2240 * in the samlogon cache then do a query. This will force the
2241 * rpc back end to return the info3 data.
2244 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2245 netsamlogon_cache_have(user_sid)) {
2246 DEBUG(10, ("query_user: cached access denied and have cached "
2247 "info3\n"));
2248 domain->last_status = NT_STATUS_OK;
2249 centry_free(centry);
2250 return NT_STATUS_NOT_FOUND;
2253 /* if status is not ok then this is a negative hit
2254 and the rest of the data doesn't matter */
2255 status = centry->status;
2256 if (NT_STATUS_IS_OK(status)) {
2257 info->acct_name = centry_string(centry, mem_ctx);
2258 info->full_name = centry_string(centry, mem_ctx);
2259 info->homedir = centry_string(centry, mem_ctx);
2260 info->shell = centry_string(centry, mem_ctx);
2261 info->primary_gid = centry_uint32(centry);
2262 centry_sid(centry, &info->user_sid);
2263 centry_sid(centry, &info->group_sid);
2266 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2267 "%s\n", domain->name, nt_errstr(status) ));
2269 centry_free(centry);
2270 return status;
2273 /* Lookup user information from a rid */
2274 static NTSTATUS query_user(struct winbindd_domain *domain,
2275 TALLOC_CTX *mem_ctx,
2276 const struct dom_sid *user_sid,
2277 struct wbint_userinfo *info)
2279 NTSTATUS status;
2280 bool old_status;
2282 old_status = domain->online;
2283 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2284 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2285 return status;
2288 ZERO_STRUCTP(info);
2290 /* Return status value returned by seq number check */
2292 if (!NT_STATUS_IS_OK(domain->last_status))
2293 return domain->last_status;
2295 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2296 domain->name ));
2298 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2300 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2301 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2302 if (!domain->internal && old_status) {
2303 set_domain_offline(domain);
2305 if (!domain->internal &&
2306 !domain->online &&
2307 old_status) {
2308 NTSTATUS cache_status;
2309 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2310 return cache_status;
2313 /* and save it */
2314 refresh_sequence_number(domain, false);
2315 if (!NT_STATUS_IS_OK(status)) {
2316 return status;
2318 wcache_save_user(domain, status, info);
2320 return status;
2323 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2324 TALLOC_CTX *mem_ctx,
2325 const struct dom_sid *user_sid,
2326 uint32_t *pnum_sids,
2327 struct dom_sid **psids)
2329 struct winbind_cache *cache = get_cache(domain);
2330 struct cache_entry *centry = NULL;
2331 NTSTATUS status;
2332 uint32_t i, num_sids;
2333 struct dom_sid *sids;
2334 fstring sid_string;
2336 if (cache->tdb == NULL) {
2337 return NT_STATUS_NOT_FOUND;
2340 centry = wcache_fetch(cache, domain, "UG/%s",
2341 sid_to_fstring(sid_string, user_sid));
2342 if (centry == NULL) {
2343 return NT_STATUS_NOT_FOUND;
2346 /* If we have an access denied cache entry and a cached info3 in the
2347 samlogon cache then do a query. This will force the rpc back end
2348 to return the info3 data. */
2350 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2351 && netsamlogon_cache_have(user_sid)) {
2352 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2353 "cached info3\n"));
2354 domain->last_status = NT_STATUS_OK;
2355 centry_free(centry);
2356 return NT_STATUS_NOT_FOUND;
2359 num_sids = centry_uint32(centry);
2360 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2361 if (sids == NULL) {
2362 centry_free(centry);
2363 return NT_STATUS_NO_MEMORY;
2366 for (i=0; i<num_sids; i++) {
2367 centry_sid(centry, &sids[i]);
2370 status = centry->status;
2372 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2373 "status: %s\n", domain->name, nt_errstr(status)));
2375 centry_free(centry);
2377 *pnum_sids = num_sids;
2378 *psids = sids;
2379 return status;
2382 /* Lookup groups a user is a member of. */
2383 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2384 TALLOC_CTX *mem_ctx,
2385 const struct dom_sid *user_sid,
2386 uint32 *num_groups, struct dom_sid **user_gids)
2388 struct cache_entry *centry = NULL;
2389 NTSTATUS status;
2390 unsigned int i;
2391 fstring sid_string;
2392 bool old_status;
2394 old_status = domain->online;
2395 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2396 num_groups, user_gids);
2397 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2398 return status;
2401 (*num_groups) = 0;
2402 (*user_gids) = NULL;
2404 /* Return status value returned by seq number check */
2406 if (!NT_STATUS_IS_OK(domain->last_status))
2407 return domain->last_status;
2409 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2410 domain->name ));
2412 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2414 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2415 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2416 if (!domain->internal && old_status) {
2417 set_domain_offline(domain);
2419 if (!domain->internal &&
2420 !domain->online &&
2421 old_status) {
2422 NTSTATUS cache_status;
2423 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2424 num_groups, user_gids);
2425 return cache_status;
2428 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2429 goto skip_save;
2431 /* and save it */
2432 refresh_sequence_number(domain, false);
2433 if (!NT_STATUS_IS_OK(status)) {
2434 return status;
2436 centry = centry_start(domain, status);
2437 if (!centry)
2438 goto skip_save;
2440 centry_put_uint32(centry, *num_groups);
2441 for (i=0; i<(*num_groups); i++) {
2442 centry_put_sid(centry, &(*user_gids)[i]);
2445 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2446 centry_free(centry);
2448 skip_save:
2449 return status;
2452 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2453 const struct dom_sid *sids)
2455 uint32_t i;
2456 char *sidlist;
2458 sidlist = talloc_strdup(mem_ctx, "");
2459 if (sidlist == NULL) {
2460 return NULL;
2462 for (i=0; i<num_sids; i++) {
2463 fstring tmp;
2464 sidlist = talloc_asprintf_append_buffer(
2465 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2466 if (sidlist == NULL) {
2467 return NULL;
2470 return sidlist;
2473 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2474 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2475 const struct dom_sid *sids,
2476 uint32_t *pnum_aliases, uint32_t **paliases)
2478 struct winbind_cache *cache = get_cache(domain);
2479 struct cache_entry *centry = NULL;
2480 uint32_t num_aliases;
2481 uint32_t *aliases;
2482 NTSTATUS status;
2483 char *sidlist;
2484 int i;
2486 if (cache->tdb == NULL) {
2487 return NT_STATUS_NOT_FOUND;
2490 if (num_sids == 0) {
2491 *pnum_aliases = 0;
2492 *paliases = NULL;
2493 return NT_STATUS_OK;
2496 /* We need to cache indexed by the whole list of SIDs, the aliases
2497 * resulting might come from any of the SIDs. */
2499 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2500 if (sidlist == NULL) {
2501 return NT_STATUS_NO_MEMORY;
2504 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2505 TALLOC_FREE(sidlist);
2506 if (centry == NULL) {
2507 return NT_STATUS_NOT_FOUND;
2510 num_aliases = centry_uint32(centry);
2511 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2512 if (aliases == NULL) {
2513 centry_free(centry);
2514 return NT_STATUS_NO_MEMORY;
2517 for (i=0; i<num_aliases; i++) {
2518 aliases[i] = centry_uint32(centry);
2521 status = centry->status;
2523 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2524 "status %s\n", domain->name, nt_errstr(status)));
2526 centry_free(centry);
2528 *pnum_aliases = num_aliases;
2529 *paliases = aliases;
2531 return status;
2534 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2535 TALLOC_CTX *mem_ctx,
2536 uint32 num_sids, const struct dom_sid *sids,
2537 uint32 *num_aliases, uint32 **alias_rids)
2539 struct cache_entry *centry = NULL;
2540 NTSTATUS status;
2541 char *sidlist;
2542 int i;
2543 bool old_status;
2545 old_status = domain->online;
2546 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2547 num_aliases, alias_rids);
2548 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2549 return status;
2552 (*num_aliases) = 0;
2553 (*alias_rids) = NULL;
2555 if (!NT_STATUS_IS_OK(domain->last_status))
2556 return domain->last_status;
2558 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2559 "for domain %s\n", domain->name ));
2561 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2562 if (sidlist == NULL) {
2563 return NT_STATUS_NO_MEMORY;
2566 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2567 num_sids, sids,
2568 num_aliases, alias_rids);
2570 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2571 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2572 if (!domain->internal && old_status) {
2573 set_domain_offline(domain);
2575 if (!domain->internal &&
2576 !domain->online &&
2577 old_status) {
2578 NTSTATUS cache_status;
2579 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2580 sids, num_aliases, alias_rids);
2581 return cache_status;
2584 /* and save it */
2585 refresh_sequence_number(domain, false);
2586 if (!NT_STATUS_IS_OK(status)) {
2587 return status;
2589 centry = centry_start(domain, status);
2590 if (!centry)
2591 goto skip_save;
2592 centry_put_uint32(centry, *num_aliases);
2593 for (i=0; i<(*num_aliases); i++)
2594 centry_put_uint32(centry, (*alias_rids)[i]);
2595 centry_end(centry, "UA%s", sidlist);
2596 centry_free(centry);
2598 skip_save:
2599 return status;
2602 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2603 TALLOC_CTX *mem_ctx,
2604 const struct dom_sid *group_sid,
2605 uint32_t *num_names,
2606 struct dom_sid **sid_mem, char ***names,
2607 uint32_t **name_types)
2609 struct winbind_cache *cache = get_cache(domain);
2610 struct cache_entry *centry = NULL;
2611 NTSTATUS status;
2612 unsigned int i;
2613 char *sid_string;
2615 if (cache->tdb == NULL) {
2616 return NT_STATUS_NOT_FOUND;
2619 sid_string = sid_string_tos(group_sid);
2620 if (sid_string == NULL) {
2621 return NT_STATUS_NO_MEMORY;
2624 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2625 TALLOC_FREE(sid_string);
2626 if (centry == NULL) {
2627 return NT_STATUS_NOT_FOUND;
2630 *sid_mem = NULL;
2631 *names = NULL;
2632 *name_types = NULL;
2634 *num_names = centry_uint32(centry);
2635 if (*num_names == 0) {
2636 centry_free(centry);
2637 return NT_STATUS_OK;
2640 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2641 *names = talloc_array(mem_ctx, char *, *num_names);
2642 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2644 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2645 TALLOC_FREE(*sid_mem);
2646 TALLOC_FREE(*names);
2647 TALLOC_FREE(*name_types);
2648 centry_free(centry);
2649 return NT_STATUS_NO_MEMORY;
2652 for (i=0; i<(*num_names); i++) {
2653 centry_sid(centry, &(*sid_mem)[i]);
2654 (*names)[i] = centry_string(centry, mem_ctx);
2655 (*name_types)[i] = centry_uint32(centry);
2658 status = centry->status;
2660 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2661 "status: %s\n", domain->name, nt_errstr(status)));
2663 centry_free(centry);
2664 return status;
2667 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2668 TALLOC_CTX *mem_ctx,
2669 const struct dom_sid *group_sid,
2670 enum lsa_SidType type,
2671 uint32 *num_names,
2672 struct dom_sid **sid_mem, char ***names,
2673 uint32 **name_types)
2675 struct cache_entry *centry = NULL;
2676 NTSTATUS status;
2677 unsigned int i;
2678 fstring sid_string;
2679 bool old_status;
2681 old_status = domain->online;
2682 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2683 sid_mem, names, name_types);
2684 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2685 return status;
2688 (*num_names) = 0;
2689 (*sid_mem) = NULL;
2690 (*names) = NULL;
2691 (*name_types) = NULL;
2693 /* Return status value returned by seq number check */
2695 if (!NT_STATUS_IS_OK(domain->last_status))
2696 return domain->last_status;
2698 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2699 domain->name ));
2701 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2702 type, num_names,
2703 sid_mem, names, name_types);
2705 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2706 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2707 if (!domain->internal && old_status) {
2708 set_domain_offline(domain);
2710 if (!domain->internal &&
2711 !domain->online &&
2712 old_status) {
2713 NTSTATUS cache_status;
2714 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2715 num_names, sid_mem, names,
2716 name_types);
2717 return cache_status;
2720 /* and save it */
2721 refresh_sequence_number(domain, false);
2722 if (!NT_STATUS_IS_OK(status)) {
2723 return status;
2725 centry = centry_start(domain, status);
2726 if (!centry)
2727 goto skip_save;
2728 centry_put_uint32(centry, *num_names);
2729 for (i=0; i<(*num_names); i++) {
2730 centry_put_sid(centry, &(*sid_mem)[i]);
2731 centry_put_string(centry, (*names)[i]);
2732 centry_put_uint32(centry, (*name_types)[i]);
2734 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2735 centry_free(centry);
2737 skip_save:
2738 return status;
2741 /* find the sequence number for a domain */
2742 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2744 refresh_sequence_number(domain, false);
2746 *seq = domain->sequence_number;
2748 return NT_STATUS_OK;
2751 /* enumerate trusted domains
2752 * (we need to have the list of trustdoms in the cache when we go offline) -
2753 * Guenther */
2754 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2755 TALLOC_CTX *mem_ctx,
2756 struct netr_DomainTrustList *trusts)
2758 NTSTATUS status;
2759 struct winbind_cache *cache;
2760 struct winbindd_tdc_domain *dom_list = NULL;
2761 size_t num_domains = 0;
2762 bool retval = false;
2763 int i;
2764 bool old_status;
2766 old_status = domain->online;
2767 trusts->count = 0;
2768 trusts->array = NULL;
2770 cache = get_cache(domain);
2771 if (!cache || !cache->tdb) {
2772 goto do_query;
2775 if (domain->online) {
2776 goto do_query;
2779 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2780 if (!retval || !num_domains || !dom_list) {
2781 TALLOC_FREE(dom_list);
2782 goto do_query;
2785 do_fetch_cache:
2786 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2787 if (!trusts->array) {
2788 TALLOC_FREE(dom_list);
2789 return NT_STATUS_NO_MEMORY;
2792 for (i = 0; i < num_domains; i++) {
2793 struct netr_DomainTrust *trust;
2794 struct dom_sid *sid;
2795 struct winbindd_domain *dom;
2797 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2798 if (dom && dom->internal) {
2799 continue;
2802 trust = &trusts->array[trusts->count];
2803 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2804 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2805 sid = talloc(trusts->array, struct dom_sid);
2806 if (!trust->netbios_name || !trust->dns_name ||
2807 !sid) {
2808 TALLOC_FREE(dom_list);
2809 TALLOC_FREE(trusts->array);
2810 return NT_STATUS_NO_MEMORY;
2813 trust->trust_flags = dom_list[i].trust_flags;
2814 trust->trust_attributes = dom_list[i].trust_attribs;
2815 trust->trust_type = dom_list[i].trust_type;
2816 sid_copy(sid, &dom_list[i].sid);
2817 trust->sid = sid;
2818 trusts->count++;
2821 TALLOC_FREE(dom_list);
2822 return NT_STATUS_OK;
2824 do_query:
2825 /* Return status value returned by seq number check */
2827 if (!NT_STATUS_IS_OK(domain->last_status))
2828 return domain->last_status;
2830 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2831 domain->name ));
2833 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2835 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2836 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2837 if (!domain->internal && old_status) {
2838 set_domain_offline(domain);
2840 if (!domain->internal &&
2841 !domain->online &&
2842 old_status) {
2843 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2844 if (retval && num_domains && dom_list) {
2845 TALLOC_FREE(trusts->array);
2846 trusts->count = 0;
2847 goto do_fetch_cache;
2851 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2852 * so that the generic centry handling still applies correctly -
2853 * Guenther*/
2855 if (!NT_STATUS_IS_ERR(status)) {
2856 status = NT_STATUS_OK;
2858 return status;
2861 /* get lockout policy */
2862 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2863 TALLOC_CTX *mem_ctx,
2864 struct samr_DomInfo12 *policy)
2866 struct winbind_cache *cache = get_cache(domain);
2867 struct cache_entry *centry = NULL;
2868 NTSTATUS status;
2869 bool old_status;
2871 old_status = domain->online;
2872 if (!cache->tdb)
2873 goto do_query;
2875 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2877 if (!centry)
2878 goto do_query;
2880 do_fetch_cache:
2881 policy->lockout_duration = centry_nttime(centry);
2882 policy->lockout_window = centry_nttime(centry);
2883 policy->lockout_threshold = centry_uint16(centry);
2885 status = centry->status;
2887 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2888 domain->name, nt_errstr(status) ));
2890 centry_free(centry);
2891 return status;
2893 do_query:
2894 ZERO_STRUCTP(policy);
2896 /* Return status value returned by seq number check */
2898 if (!NT_STATUS_IS_OK(domain->last_status))
2899 return domain->last_status;
2901 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2902 domain->name ));
2904 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2906 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2907 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2908 if (!domain->internal && old_status) {
2909 set_domain_offline(domain);
2911 if (cache->tdb &&
2912 !domain->internal &&
2913 !domain->online &&
2914 old_status) {
2915 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2916 if (centry) {
2917 goto do_fetch_cache;
2921 /* and save it */
2922 refresh_sequence_number(domain, false);
2923 if (!NT_STATUS_IS_OK(status)) {
2924 return status;
2926 wcache_save_lockout_policy(domain, status, policy);
2928 return status;
2931 /* get password policy */
2932 static NTSTATUS password_policy(struct winbindd_domain *domain,
2933 TALLOC_CTX *mem_ctx,
2934 struct samr_DomInfo1 *policy)
2936 struct winbind_cache *cache = get_cache(domain);
2937 struct cache_entry *centry = NULL;
2938 NTSTATUS status;
2939 bool old_status;
2941 old_status = domain->online;
2942 if (!cache->tdb)
2943 goto do_query;
2945 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2947 if (!centry)
2948 goto do_query;
2950 do_fetch_cache:
2951 policy->min_password_length = centry_uint16(centry);
2952 policy->password_history_length = centry_uint16(centry);
2953 policy->password_properties = centry_uint32(centry);
2954 policy->max_password_age = centry_nttime(centry);
2955 policy->min_password_age = centry_nttime(centry);
2957 status = centry->status;
2959 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2960 domain->name, nt_errstr(status) ));
2962 centry_free(centry);
2963 return status;
2965 do_query:
2966 ZERO_STRUCTP(policy);
2968 /* Return status value returned by seq number check */
2970 if (!NT_STATUS_IS_OK(domain->last_status))
2971 return domain->last_status;
2973 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2974 domain->name ));
2976 status = domain->backend->password_policy(domain, mem_ctx, policy);
2978 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2979 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2980 if (!domain->internal && old_status) {
2981 set_domain_offline(domain);
2983 if (cache->tdb &&
2984 !domain->internal &&
2985 !domain->online &&
2986 old_status) {
2987 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2988 if (centry) {
2989 goto do_fetch_cache;
2993 /* and save it */
2994 refresh_sequence_number(domain, false);
2995 if (!NT_STATUS_IS_OK(status)) {
2996 return status;
2998 wcache_save_password_policy(domain, status, policy);
3000 return status;
3004 /* Invalidate cached user and group lists coherently */
3006 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3007 void *state)
3009 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3010 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3011 tdb_delete(the_tdb, kbuf);
3013 return 0;
3016 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3018 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3019 const struct dom_sid *sid)
3021 fstring key_str, sid_string;
3022 struct winbind_cache *cache;
3024 /* dont clear cached U/SID and UG/SID entries when we want to logon
3025 * offline - gd */
3027 if (lp_winbind_offline_logon()) {
3028 return;
3031 if (!domain)
3032 return;
3034 cache = get_cache(domain);
3036 if (!cache->tdb) {
3037 return;
3040 /* Clear U/SID cache entry */
3041 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3042 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3043 tdb_delete(cache->tdb, string_tdb_data(key_str));
3045 /* Clear UG/SID cache entry */
3046 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3047 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3048 tdb_delete(cache->tdb, string_tdb_data(key_str));
3050 /* Samba/winbindd never needs this. */
3051 netsamlogon_clear_cached_user(sid);
3054 bool wcache_invalidate_cache(void)
3056 struct winbindd_domain *domain;
3058 for (domain = domain_list(); domain; domain = domain->next) {
3059 struct winbind_cache *cache = get_cache(domain);
3061 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3062 "entries for %s\n", domain->name));
3063 if (cache) {
3064 if (cache->tdb) {
3065 tdb_traverse(cache->tdb, traverse_fn, NULL);
3066 } else {
3067 return false;
3071 return true;
3074 bool wcache_invalidate_cache_noinit(void)
3076 struct winbindd_domain *domain;
3078 for (domain = domain_list(); domain; domain = domain->next) {
3079 struct winbind_cache *cache;
3081 /* Skip uninitialized domains. */
3082 if (!domain->initialized && !domain->internal) {
3083 continue;
3086 cache = get_cache(domain);
3088 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3089 "entries for %s\n", domain->name));
3090 if (cache) {
3091 if (cache->tdb) {
3092 tdb_traverse(cache->tdb, traverse_fn, NULL);
3094 * Flushing cache has nothing to with domains.
3095 * return here if we successfully flushed once.
3096 * To avoid unnecessary traversing the cache.
3098 return true;
3099 } else {
3100 return false;
3104 return true;
3107 bool init_wcache(void)
3109 if (wcache == NULL) {
3110 wcache = SMB_XMALLOC_P(struct winbind_cache);
3111 ZERO_STRUCTP(wcache);
3114 if (wcache->tdb != NULL)
3115 return true;
3117 /* when working offline we must not clear the cache on restart */
3118 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3119 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3120 TDB_INCOMPATIBLE_HASH |
3121 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3122 O_RDWR|O_CREAT, 0600);
3124 if (wcache->tdb == NULL) {
3125 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3126 return false;
3129 return true;
3132 /************************************************************************
3133 This is called by the parent to initialize the cache file.
3134 We don't need sophisticated locking here as we know we're the
3135 only opener.
3136 ************************************************************************/
3138 bool initialize_winbindd_cache(void)
3140 bool cache_bad = true;
3141 uint32 vers;
3143 if (!init_wcache()) {
3144 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3145 return false;
3148 /* Check version number. */
3149 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3150 vers == WINBINDD_CACHE_VERSION) {
3151 cache_bad = false;
3154 if (cache_bad) {
3155 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3156 "and re-creating with version number %d\n",
3157 WINBINDD_CACHE_VERSION ));
3159 tdb_close(wcache->tdb);
3160 wcache->tdb = NULL;
3162 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3163 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3164 state_path("winbindd_cache.tdb"),
3165 strerror(errno) ));
3166 return false;
3168 if (!init_wcache()) {
3169 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3170 "init_wcache failed.\n"));
3171 return false;
3174 /* Write the version. */
3175 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3176 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3177 tdb_errorstr_compat(wcache->tdb) ));
3178 return false;
3182 tdb_close(wcache->tdb);
3183 wcache->tdb = NULL;
3184 return true;
3187 void close_winbindd_cache(void)
3189 if (!wcache) {
3190 return;
3192 if (wcache->tdb) {
3193 tdb_close(wcache->tdb);
3194 wcache->tdb = NULL;
3198 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3199 char **domain_name, char **name,
3200 enum lsa_SidType *type)
3202 struct winbindd_domain *domain;
3203 NTSTATUS status;
3205 domain = find_lookup_domain_from_sid(sid);
3206 if (domain == NULL) {
3207 return false;
3209 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3210 type);
3211 return NT_STATUS_IS_OK(status);
3214 bool lookup_cached_name(const char *domain_name,
3215 const char *name,
3216 struct dom_sid *sid,
3217 enum lsa_SidType *type)
3219 struct winbindd_domain *domain;
3220 NTSTATUS status;
3221 bool original_online_state;
3223 domain = find_lookup_domain_from_name(domain_name);
3224 if (domain == NULL) {
3225 return false;
3228 /* If we are doing a cached logon, temporarily set the domain
3229 offline so the cache won't expire the entry */
3231 original_online_state = domain->online;
3232 domain->online = false;
3233 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3234 domain->online = original_online_state;
3236 return NT_STATUS_IS_OK(status);
3239 void cache_name2sid(struct winbindd_domain *domain,
3240 const char *domain_name, const char *name,
3241 enum lsa_SidType type, const struct dom_sid *sid)
3243 refresh_sequence_number(domain, false);
3244 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3245 sid, type);
3249 * The original idea that this cache only contains centries has
3250 * been blurred - now other stuff gets put in here. Ensure we
3251 * ignore these things on cleanup.
3254 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3255 TDB_DATA dbuf, void *state)
3257 struct cache_entry *centry;
3259 if (is_non_centry_key(kbuf)) {
3260 return 0;
3263 centry = wcache_fetch_raw((char *)kbuf.dptr);
3264 if (!centry) {
3265 return 0;
3268 if (!NT_STATUS_IS_OK(centry->status)) {
3269 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3270 tdb_delete(the_tdb, kbuf);
3273 centry_free(centry);
3274 return 0;
3277 /* flush the cache */
3278 void wcache_flush_cache(void)
3280 if (!wcache)
3281 return;
3282 if (wcache->tdb) {
3283 tdb_close(wcache->tdb);
3284 wcache->tdb = NULL;
3286 if (!winbindd_use_cache()) {
3287 return;
3290 /* when working offline we must not clear the cache on restart */
3291 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3292 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3293 TDB_INCOMPATIBLE_HASH |
3294 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3295 O_RDWR|O_CREAT, 0600);
3297 if (!wcache->tdb) {
3298 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3299 return;
3302 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3304 DEBUG(10,("wcache_flush_cache success\n"));
3307 /* Count cached creds */
3309 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3310 void *state)
3312 int *cred_count = (int*)state;
3314 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3315 (*cred_count)++;
3317 return 0;
3320 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3322 struct winbind_cache *cache = get_cache(domain);
3324 *count = 0;
3326 if (!cache->tdb) {
3327 return NT_STATUS_INTERNAL_DB_ERROR;
3330 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3332 return NT_STATUS_OK;
3335 struct cred_list {
3336 struct cred_list *prev, *next;
3337 TDB_DATA key;
3338 fstring name;
3339 time_t created;
3341 static struct cred_list *wcache_cred_list;
3343 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3344 void *state)
3346 struct cred_list *cred;
3348 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3350 cred = SMB_MALLOC_P(struct cred_list);
3351 if (cred == NULL) {
3352 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3353 return -1;
3356 ZERO_STRUCTP(cred);
3358 /* save a copy of the key */
3360 fstrcpy(cred->name, (const char *)kbuf.dptr);
3361 DLIST_ADD(wcache_cred_list, cred);
3364 return 0;
3367 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3369 struct winbind_cache *cache = get_cache(domain);
3370 NTSTATUS status;
3371 int ret;
3372 struct cred_list *cred, *oldest = NULL;
3374 if (!cache->tdb) {
3375 return NT_STATUS_INTERNAL_DB_ERROR;
3378 /* we possibly already have an entry */
3379 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3381 fstring key_str, tmp;
3383 DEBUG(11,("we already have an entry, deleting that\n"));
3385 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3387 tdb_delete(cache->tdb, string_tdb_data(key_str));
3389 return NT_STATUS_OK;
3392 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3393 if (ret == 0) {
3394 return NT_STATUS_OK;
3395 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3396 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3399 ZERO_STRUCTP(oldest);
3401 for (cred = wcache_cred_list; cred; cred = cred->next) {
3403 TDB_DATA data;
3404 time_t t;
3406 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3407 if (!data.dptr) {
3408 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3409 cred->name));
3410 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3411 goto done;
3414 t = IVAL(data.dptr, 0);
3415 SAFE_FREE(data.dptr);
3417 if (!oldest) {
3418 oldest = SMB_MALLOC_P(struct cred_list);
3419 if (oldest == NULL) {
3420 status = NT_STATUS_NO_MEMORY;
3421 goto done;
3424 fstrcpy(oldest->name, cred->name);
3425 oldest->created = t;
3426 continue;
3429 if (t < oldest->created) {
3430 fstrcpy(oldest->name, cred->name);
3431 oldest->created = t;
3435 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3436 status = NT_STATUS_OK;
3437 } else {
3438 status = NT_STATUS_UNSUCCESSFUL;
3440 done:
3441 SAFE_FREE(wcache_cred_list);
3442 SAFE_FREE(oldest);
3444 return status;
3447 /* Change the global online/offline state. */
3448 bool set_global_winbindd_state_offline(void)
3450 TDB_DATA data;
3452 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3454 /* Only go offline if someone has created
3455 the key "WINBINDD_OFFLINE" in the cache tdb. */
3457 if (wcache == NULL || wcache->tdb == NULL) {
3458 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3459 return false;
3462 if (!lp_winbind_offline_logon()) {
3463 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3464 return false;
3467 if (global_winbindd_offline_state) {
3468 /* Already offline. */
3469 return true;
3472 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3474 if (!data.dptr || data.dsize != 4) {
3475 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3476 SAFE_FREE(data.dptr);
3477 return false;
3478 } else {
3479 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3480 global_winbindd_offline_state = true;
3481 SAFE_FREE(data.dptr);
3482 return true;
3486 void set_global_winbindd_state_online(void)
3488 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3490 if (!lp_winbind_offline_logon()) {
3491 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3492 return;
3495 if (!global_winbindd_offline_state) {
3496 /* Already online. */
3497 return;
3499 global_winbindd_offline_state = false;
3501 if (!wcache->tdb) {
3502 return;
3505 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3506 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3509 bool get_global_winbindd_state_offline(void)
3511 return global_winbindd_offline_state;
3514 /***********************************************************************
3515 Validate functions for all possible cache tdb keys.
3516 ***********************************************************************/
3518 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3519 struct tdb_validation_status *state)
3521 struct cache_entry *centry;
3523 centry = SMB_XMALLOC_P(struct cache_entry);
3524 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3525 if (!centry->data) {
3526 SAFE_FREE(centry);
3527 return NULL;
3529 centry->len = data.dsize;
3530 centry->ofs = 0;
3532 if (centry->len < 16) {
3533 /* huh? corrupt cache? */
3534 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3535 "(len < 16) ?\n", kstr));
3536 centry_free(centry);
3537 state->bad_entry = true;
3538 state->success = false;
3539 return NULL;
3542 centry->status = NT_STATUS(centry_uint32(centry));
3543 centry->sequence_number = centry_uint32(centry);
3544 centry->timeout = centry_uint64_t(centry);
3545 return centry;
3548 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3549 struct tdb_validation_status *state)
3551 if (dbuf.dsize != 8) {
3552 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3553 keystr, (unsigned int)dbuf.dsize ));
3554 state->bad_entry = true;
3555 return 1;
3557 return 0;
3560 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3561 struct tdb_validation_status *state)
3563 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3564 if (!centry) {
3565 return 1;
3568 (void)centry_uint32(centry);
3569 if (NT_STATUS_IS_OK(centry->status)) {
3570 struct dom_sid sid;
3571 (void)centry_sid(centry, &sid);
3574 centry_free(centry);
3576 if (!(state->success)) {
3577 return 1;
3579 DEBUG(10,("validate_ns: %s ok\n", keystr));
3580 return 0;
3583 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3584 struct tdb_validation_status *state)
3586 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3587 if (!centry) {
3588 return 1;
3591 if (NT_STATUS_IS_OK(centry->status)) {
3592 (void)centry_uint32(centry);
3593 (void)centry_string(centry, mem_ctx);
3594 (void)centry_string(centry, mem_ctx);
3597 centry_free(centry);
3599 if (!(state->success)) {
3600 return 1;
3602 DEBUG(10,("validate_sn: %s ok\n", keystr));
3603 return 0;
3606 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3607 struct tdb_validation_status *state)
3609 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3610 struct dom_sid sid;
3612 if (!centry) {
3613 return 1;
3616 (void)centry_string(centry, mem_ctx);
3617 (void)centry_string(centry, mem_ctx);
3618 (void)centry_string(centry, mem_ctx);
3619 (void)centry_string(centry, mem_ctx);
3620 (void)centry_uint32(centry);
3621 (void)centry_sid(centry, &sid);
3622 (void)centry_sid(centry, &sid);
3624 centry_free(centry);
3626 if (!(state->success)) {
3627 return 1;
3629 DEBUG(10,("validate_u: %s ok\n", keystr));
3630 return 0;
3633 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3634 struct tdb_validation_status *state)
3636 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3638 if (!centry) {
3639 return 1;
3642 (void)centry_nttime(centry);
3643 (void)centry_nttime(centry);
3644 (void)centry_uint16(centry);
3646 centry_free(centry);
3648 if (!(state->success)) {
3649 return 1;
3651 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3652 return 0;
3655 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3656 struct tdb_validation_status *state)
3658 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3660 if (!centry) {
3661 return 1;
3664 (void)centry_uint16(centry);
3665 (void)centry_uint16(centry);
3666 (void)centry_uint32(centry);
3667 (void)centry_nttime(centry);
3668 (void)centry_nttime(centry);
3670 centry_free(centry);
3672 if (!(state->success)) {
3673 return 1;
3675 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3676 return 0;
3679 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3680 struct tdb_validation_status *state)
3682 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3684 if (!centry) {
3685 return 1;
3688 (void)centry_time(centry);
3689 (void)centry_hash16(centry, mem_ctx);
3691 /* We only have 17 bytes more data in the salted cred case. */
3692 if (centry->len - centry->ofs == 17) {
3693 (void)centry_hash16(centry, mem_ctx);
3696 centry_free(centry);
3698 if (!(state->success)) {
3699 return 1;
3701 DEBUG(10,("validate_cred: %s ok\n", keystr));
3702 return 0;
3705 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3706 struct tdb_validation_status *state)
3708 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3709 int32 num_entries, i;
3711 if (!centry) {
3712 return 1;
3715 num_entries = (int32)centry_uint32(centry);
3717 for (i=0; i< num_entries; i++) {
3718 struct dom_sid sid;
3719 (void)centry_string(centry, mem_ctx);
3720 (void)centry_string(centry, mem_ctx);
3721 (void)centry_string(centry, mem_ctx);
3722 (void)centry_string(centry, mem_ctx);
3723 (void)centry_sid(centry, &sid);
3724 (void)centry_sid(centry, &sid);
3727 centry_free(centry);
3729 if (!(state->success)) {
3730 return 1;
3732 DEBUG(10,("validate_ul: %s ok\n", keystr));
3733 return 0;
3736 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3737 struct tdb_validation_status *state)
3739 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3740 int32 num_entries, i;
3742 if (!centry) {
3743 return 1;
3746 num_entries = centry_uint32(centry);
3748 for (i=0; i< num_entries; i++) {
3749 (void)centry_string(centry, mem_ctx);
3750 (void)centry_string(centry, mem_ctx);
3751 (void)centry_uint32(centry);
3754 centry_free(centry);
3756 if (!(state->success)) {
3757 return 1;
3759 DEBUG(10,("validate_gl: %s ok\n", keystr));
3760 return 0;
3763 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3764 struct tdb_validation_status *state)
3766 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3767 int32 num_groups, i;
3769 if (!centry) {
3770 return 1;
3773 num_groups = centry_uint32(centry);
3775 for (i=0; i< num_groups; i++) {
3776 struct dom_sid sid;
3777 centry_sid(centry, &sid);
3780 centry_free(centry);
3782 if (!(state->success)) {
3783 return 1;
3785 DEBUG(10,("validate_ug: %s ok\n", keystr));
3786 return 0;
3789 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3790 struct tdb_validation_status *state)
3792 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3793 int32 num_aliases, i;
3795 if (!centry) {
3796 return 1;
3799 num_aliases = centry_uint32(centry);
3801 for (i=0; i < num_aliases; i++) {
3802 (void)centry_uint32(centry);
3805 centry_free(centry);
3807 if (!(state->success)) {
3808 return 1;
3810 DEBUG(10,("validate_ua: %s ok\n", keystr));
3811 return 0;
3814 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3815 struct tdb_validation_status *state)
3817 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3818 int32 num_names, i;
3820 if (!centry) {
3821 return 1;
3824 num_names = centry_uint32(centry);
3826 for (i=0; i< num_names; i++) {
3827 struct dom_sid sid;
3828 centry_sid(centry, &sid);
3829 (void)centry_string(centry, mem_ctx);
3830 (void)centry_uint32(centry);
3833 centry_free(centry);
3835 if (!(state->success)) {
3836 return 1;
3838 DEBUG(10,("validate_gm: %s ok\n", keystr));
3839 return 0;
3842 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3843 struct tdb_validation_status *state)
3845 /* Can't say anything about this other than must be nonzero. */
3846 if (dbuf.dsize == 0) {
3847 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3848 keystr));
3849 state->bad_entry = true;
3850 state->success = false;
3851 return 1;
3854 DEBUG(10,("validate_dr: %s ok\n", keystr));
3855 return 0;
3858 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3859 struct tdb_validation_status *state)
3861 /* Can't say anything about this other than must be nonzero. */
3862 if (dbuf.dsize == 0) {
3863 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3864 keystr));
3865 state->bad_entry = true;
3866 state->success = false;
3867 return 1;
3870 DEBUG(10,("validate_de: %s ok\n", keystr));
3871 return 0;
3874 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3875 TDB_DATA dbuf, struct tdb_validation_status *state)
3877 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3879 if (!centry) {
3880 return 1;
3883 (void)centry_string(centry, mem_ctx);
3884 (void)centry_string(centry, mem_ctx);
3885 (void)centry_string(centry, mem_ctx);
3886 (void)centry_uint32(centry);
3888 centry_free(centry);
3890 if (!(state->success)) {
3891 return 1;
3893 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3894 return 0;
3897 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3898 TDB_DATA dbuf,
3899 struct tdb_validation_status *state)
3901 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3903 if (!centry) {
3904 return 1;
3907 (void)centry_string( centry, mem_ctx );
3909 centry_free(centry);
3911 if (!(state->success)) {
3912 return 1;
3914 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3915 return 0;
3918 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3919 TDB_DATA dbuf,
3920 struct tdb_validation_status *state)
3922 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3924 if (!centry) {
3925 return 1;
3928 (void)centry_string( centry, mem_ctx );
3930 centry_free(centry);
3932 if (!(state->success)) {
3933 return 1;
3935 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3936 return 0;
3939 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3940 TDB_DATA dbuf,
3941 struct tdb_validation_status *state)
3943 if (dbuf.dsize == 0) {
3944 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3945 "key %s (len ==0) ?\n", keystr));
3946 state->bad_entry = true;
3947 state->success = false;
3948 return 1;
3951 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3952 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3953 return 0;
3956 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3957 struct tdb_validation_status *state)
3959 if (dbuf.dsize != 4) {
3960 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3961 keystr, (unsigned int)dbuf.dsize ));
3962 state->bad_entry = true;
3963 state->success = false;
3964 return 1;
3966 DEBUG(10,("validate_offline: %s ok\n", keystr));
3967 return 0;
3970 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3971 struct tdb_validation_status *state)
3974 * Ignore validation for now. The proper way to do this is with a
3975 * checksum. Just pure parsing does not really catch much.
3977 return 0;
3980 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3981 struct tdb_validation_status *state)
3983 if (dbuf.dsize != 4) {
3984 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3985 "key %s (len %u != 4) ?\n",
3986 keystr, (unsigned int)dbuf.dsize));
3987 state->bad_entry = true;
3988 state->success = false;
3989 return 1;
3992 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3993 return 0;
3996 /***********************************************************************
3997 A list of all possible cache tdb keys with associated validation
3998 functions.
3999 ***********************************************************************/
4001 struct key_val_struct {
4002 const char *keyname;
4003 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4004 } key_val[] = {
4005 {"SEQNUM/", validate_seqnum},
4006 {"NS/", validate_ns},
4007 {"SN/", validate_sn},
4008 {"U/", validate_u},
4009 {"LOC_POL/", validate_loc_pol},
4010 {"PWD_POL/", validate_pwd_pol},
4011 {"CRED/", validate_cred},
4012 {"UL/", validate_ul},
4013 {"GL/", validate_gl},
4014 {"UG/", validate_ug},
4015 {"UA", validate_ua},
4016 {"GM/", validate_gm},
4017 {"DR/", validate_dr},
4018 {"DE/", validate_de},
4019 {"NSS/PWINFO/", validate_pwinfo},
4020 {"TRUSTDOMCACHE/", validate_trustdomcache},
4021 {"NSS/NA/", validate_nss_na},
4022 {"NSS/AN/", validate_nss_an},
4023 {"WINBINDD_OFFLINE", validate_offline},
4024 {"NDR/", validate_ndr},
4025 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4026 {NULL, NULL}
4029 /***********************************************************************
4030 Function to look at every entry in the tdb and validate it as far as
4031 possible.
4032 ***********************************************************************/
4034 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4036 int i;
4037 unsigned int max_key_len = 1024;
4038 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4040 /* Paranoia check. */
4041 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4042 max_key_len = 1024 * 1024;
4044 if (kbuf.dsize > max_key_len) {
4045 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4046 "(%u) > (%u)\n\n",
4047 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4048 return 1;
4051 for (i = 0; key_val[i].keyname; i++) {
4052 size_t namelen = strlen(key_val[i].keyname);
4053 if (kbuf.dsize >= namelen && (
4054 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4055 TALLOC_CTX *mem_ctx;
4056 char *keystr;
4057 int ret;
4059 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4060 if (!keystr) {
4061 return 1;
4063 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4064 keystr[kbuf.dsize] = '\0';
4066 mem_ctx = talloc_init("validate_ctx");
4067 if (!mem_ctx) {
4068 SAFE_FREE(keystr);
4069 return 1;
4072 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4073 v_state);
4075 SAFE_FREE(keystr);
4076 talloc_destroy(mem_ctx);
4077 return ret;
4081 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4082 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4083 DEBUG(0,("data :\n"));
4084 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4085 v_state->unknown_key = true;
4086 v_state->success = false;
4087 return 1; /* terminate. */
4090 static void validate_panic(const char *const why)
4092 DEBUG(0,("validating cache: would panic %s\n", why ));
4093 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4094 exit(47);
4097 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4098 TDB_DATA key,
4099 TDB_DATA data,
4100 void *state)
4102 uint64_t ctimeout;
4103 TDB_DATA blob;
4105 if (is_non_centry_key(key)) {
4106 return 0;
4109 if (data.dptr == NULL || data.dsize == 0) {
4110 if (tdb_delete(tdb, key) < 0) {
4111 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4112 key.dptr));
4113 return 1;
4117 /* add timeout to blob (uint64_t) */
4118 blob.dsize = data.dsize + 8;
4120 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4121 if (blob.dptr == NULL) {
4122 return 1;
4124 memset(blob.dptr, 0, blob.dsize);
4126 /* copy status and seqnum */
4127 memcpy(blob.dptr, data.dptr, 8);
4129 /* add timeout */
4130 ctimeout = lp_winbind_cache_time() + time(NULL);
4131 SBVAL(blob.dptr, 8, ctimeout);
4133 /* copy the rest */
4134 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4136 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4137 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4138 key.dptr));
4139 SAFE_FREE(blob.dptr);
4140 return 1;
4143 SAFE_FREE(blob.dptr);
4144 return 0;
4147 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4149 int rc;
4151 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4153 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4154 if (rc < 0) {
4155 return false;
4158 return true;
4161 /***********************************************************************
4162 Try and validate every entry in the winbindd cache. If we fail here,
4163 delete the cache tdb and return non-zero.
4164 ***********************************************************************/
4166 int winbindd_validate_cache(void)
4168 int ret = -1;
4169 const char *tdb_path = state_path("winbindd_cache.tdb");
4170 TDB_CONTEXT *tdb = NULL;
4171 uint32_t vers_id;
4172 bool ok;
4174 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4175 smb_panic_fn = validate_panic;
4177 tdb = tdb_open_log(tdb_path,
4178 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4179 TDB_INCOMPATIBLE_HASH |
4180 ( lp_winbind_offline_logon()
4181 ? TDB_DEFAULT
4182 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4183 O_RDWR|O_CREAT,
4184 0600);
4185 if (!tdb) {
4186 DEBUG(0, ("winbindd_validate_cache: "
4187 "error opening/initializing tdb\n"));
4188 goto done;
4191 /* Version check and upgrade code. */
4192 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4193 DEBUG(10, ("Fresh database\n"));
4194 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4195 vers_id = WINBINDD_CACHE_VERSION;
4198 if (vers_id != WINBINDD_CACHE_VERSION) {
4199 if (vers_id == WINBINDD_CACHE_VER1) {
4200 ok = wbcache_upgrade_v1_to_v2(tdb);
4201 if (!ok) {
4202 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4203 unlink(tdb_path);
4204 goto done;
4207 tdb_store_uint32(tdb,
4208 WINBINDD_CACHE_VERSION_KEYSTR,
4209 WINBINDD_CACHE_VERSION);
4210 vers_id = WINBINDD_CACHE_VER2;
4214 tdb_close(tdb);
4216 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4218 if (ret != 0) {
4219 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4220 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4221 unlink(tdb_path);
4224 done:
4225 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4226 smb_panic_fn = smb_panic;
4227 return ret;
4230 /***********************************************************************
4231 Try and validate every entry in the winbindd cache.
4232 ***********************************************************************/
4234 int winbindd_validate_cache_nobackup(void)
4236 int ret = -1;
4237 const char *tdb_path = state_path("winbindd_cache.tdb");
4239 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4240 smb_panic_fn = validate_panic;
4243 if (wcache == NULL || wcache->tdb == NULL) {
4244 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4245 } else {
4246 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4249 if (ret != 0) {
4250 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4251 "successful.\n"));
4254 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4255 "function\n"));
4256 smb_panic_fn = smb_panic;
4257 return ret;
4260 bool winbindd_cache_validate_and_initialize(void)
4262 close_winbindd_cache();
4264 if (lp_winbind_offline_logon()) {
4265 if (winbindd_validate_cache() < 0) {
4266 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4267 "could be restored.\n"));
4271 return initialize_winbindd_cache();
4274 /*********************************************************************
4275 ********************************************************************/
4277 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4278 struct winbindd_tdc_domain **domains,
4279 size_t *num_domains )
4281 struct winbindd_tdc_domain *list = NULL;
4282 size_t idx;
4283 int i;
4284 bool set_only = false;
4286 /* don't allow duplicates */
4288 idx = *num_domains;
4289 list = *domains;
4291 for ( i=0; i< (*num_domains); i++ ) {
4292 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4293 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4294 new_dom->name));
4295 idx = i;
4296 set_only = true;
4298 break;
4302 if ( !set_only ) {
4303 if ( !*domains ) {
4304 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4305 idx = 0;
4306 } else {
4307 list = talloc_realloc( *domains, *domains,
4308 struct winbindd_tdc_domain,
4309 (*num_domains)+1);
4310 idx = *num_domains;
4313 ZERO_STRUCT( list[idx] );
4316 if ( !list )
4317 return false;
4319 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4320 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4322 if ( !is_null_sid( &new_dom->sid ) ) {
4323 sid_copy( &list[idx].sid, &new_dom->sid );
4324 } else {
4325 sid_copy(&list[idx].sid, &global_sid_NULL);
4328 if ( new_dom->domain_flags != 0x0 )
4329 list[idx].trust_flags = new_dom->domain_flags;
4331 if ( new_dom->domain_type != 0x0 )
4332 list[idx].trust_type = new_dom->domain_type;
4334 if ( new_dom->domain_trust_attribs != 0x0 )
4335 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4337 if ( !set_only ) {
4338 *domains = list;
4339 *num_domains = idx + 1;
4342 return true;
4345 /*********************************************************************
4346 ********************************************************************/
4348 static TDB_DATA make_tdc_key( const char *domain_name )
4350 char *keystr = NULL;
4351 TDB_DATA key = { NULL, 0 };
4353 if ( !domain_name ) {
4354 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4355 return key;
4358 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4359 return key;
4361 key = string_term_tdb_data(keystr);
4363 return key;
4366 /*********************************************************************
4367 ********************************************************************/
4369 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4370 size_t num_domains,
4371 unsigned char **buf )
4373 unsigned char *buffer = NULL;
4374 int len = 0;
4375 int buflen = 0;
4376 int i = 0;
4378 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4379 (int)num_domains));
4381 buflen = 0;
4383 again:
4384 len = 0;
4386 /* Store the number of array items first */
4387 len += tdb_pack( buffer+len, buflen-len, "d",
4388 num_domains );
4390 /* now pack each domain trust record */
4391 for ( i=0; i<num_domains; i++ ) {
4393 fstring tmp;
4395 if ( buflen > 0 ) {
4396 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4397 domains[i].domain_name,
4398 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4401 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4402 domains[i].domain_name,
4403 domains[i].dns_name,
4404 sid_to_fstring(tmp, &domains[i].sid),
4405 domains[i].trust_flags,
4406 domains[i].trust_attribs,
4407 domains[i].trust_type );
4410 if ( buflen < len ) {
4411 SAFE_FREE(buffer);
4412 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4413 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4414 buflen = -1;
4415 goto done;
4417 buflen = len;
4418 goto again;
4421 *buf = buffer;
4423 done:
4424 return buflen;
4427 /*********************************************************************
4428 ********************************************************************/
4430 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4431 struct winbindd_tdc_domain **domains )
4433 fstring domain_name, dns_name, sid_string;
4434 uint32 type, attribs, flags;
4435 int num_domains;
4436 int len = 0;
4437 int i;
4438 struct winbindd_tdc_domain *list = NULL;
4440 /* get the number of domains */
4441 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4442 if ( len == -1 ) {
4443 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4444 return 0;
4447 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4448 if ( !list ) {
4449 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4450 return 0;
4453 for ( i=0; i<num_domains; i++ ) {
4454 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4455 domain_name,
4456 dns_name,
4457 sid_string,
4458 &flags,
4459 &attribs,
4460 &type );
4462 if ( len == -1 ) {
4463 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4464 TALLOC_FREE( list );
4465 return 0;
4468 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4469 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4470 domain_name, dns_name, sid_string,
4471 flags, attribs, type));
4473 list[i].domain_name = talloc_strdup( list, domain_name );
4474 list[i].dns_name = talloc_strdup( list, dns_name );
4475 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4476 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4477 domain_name));
4479 list[i].trust_flags = flags;
4480 list[i].trust_attribs = attribs;
4481 list[i].trust_type = type;
4484 *domains = list;
4486 return num_domains;
4489 /*********************************************************************
4490 ********************************************************************/
4492 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4494 TDB_DATA key = make_tdc_key( lp_workgroup() );
4495 TDB_DATA data = { NULL, 0 };
4496 int ret;
4498 if ( !key.dptr )
4499 return false;
4501 /* See if we were asked to delete the cache entry */
4503 if ( !domains ) {
4504 ret = tdb_delete( wcache->tdb, key );
4505 goto done;
4508 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4510 if ( !data.dptr ) {
4511 ret = -1;
4512 goto done;
4515 ret = tdb_store( wcache->tdb, key, data, 0 );
4517 done:
4518 SAFE_FREE( data.dptr );
4519 SAFE_FREE( key.dptr );
4521 return ( ret == 0 );
4524 /*********************************************************************
4525 ********************************************************************/
4527 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4529 TDB_DATA key = make_tdc_key( lp_workgroup() );
4530 TDB_DATA data = { NULL, 0 };
4532 *domains = NULL;
4533 *num_domains = 0;
4535 if ( !key.dptr )
4536 return false;
4538 data = tdb_fetch_compat( wcache->tdb, key );
4540 SAFE_FREE( key.dptr );
4542 if ( !data.dptr )
4543 return false;
4545 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4547 SAFE_FREE( data.dptr );
4549 if ( !*domains )
4550 return false;
4552 return true;
4555 /*********************************************************************
4556 ********************************************************************/
4558 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4560 struct winbindd_tdc_domain *dom_list = NULL;
4561 size_t num_domains = 0;
4562 bool ret = false;
4564 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4565 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4566 domain->name, domain->alt_name,
4567 sid_string_dbg(&domain->sid),
4568 domain->domain_flags,
4569 domain->domain_trust_attribs,
4570 domain->domain_type));
4572 if ( !init_wcache() ) {
4573 return false;
4576 /* fetch the list */
4578 wcache_tdc_fetch_list( &dom_list, &num_domains );
4580 /* add the new domain */
4582 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4583 goto done;
4586 /* pack the domain */
4588 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4589 goto done;
4592 /* Success */
4594 ret = true;
4595 done:
4596 TALLOC_FREE( dom_list );
4598 return ret;
4601 /*********************************************************************
4602 ********************************************************************/
4604 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4606 struct winbindd_tdc_domain *dom_list = NULL;
4607 size_t num_domains = 0;
4608 int i;
4609 struct winbindd_tdc_domain *d = NULL;
4611 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4613 if ( !init_wcache() ) {
4614 return NULL;
4617 /* fetch the list */
4619 wcache_tdc_fetch_list( &dom_list, &num_domains );
4621 for ( i=0; i<num_domains; i++ ) {
4622 if ( strequal(name, dom_list[i].domain_name) ||
4623 strequal(name, dom_list[i].dns_name) )
4625 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4626 name));
4628 d = talloc( ctx, struct winbindd_tdc_domain );
4629 if ( !d )
4630 break;
4632 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4633 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4634 sid_copy( &d->sid, &dom_list[i].sid );
4635 d->trust_flags = dom_list[i].trust_flags;
4636 d->trust_type = dom_list[i].trust_type;
4637 d->trust_attribs = dom_list[i].trust_attribs;
4639 break;
4643 TALLOC_FREE( dom_list );
4645 return d;
4648 /*********************************************************************
4649 ********************************************************************/
4651 struct winbindd_tdc_domain*
4652 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4653 const struct dom_sid *sid)
4655 struct winbindd_tdc_domain *dom_list = NULL;
4656 size_t num_domains = 0;
4657 int i;
4658 struct winbindd_tdc_domain *d = NULL;
4660 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4661 sid_string_dbg(sid)));
4663 if (!init_wcache()) {
4664 return NULL;
4667 /* fetch the list */
4669 wcache_tdc_fetch_list(&dom_list, &num_domains);
4671 for (i = 0; i<num_domains; i++) {
4672 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4673 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4674 "Found domain %s for SID %s\n",
4675 dom_list[i].domain_name,
4676 sid_string_dbg(sid)));
4678 d = talloc(ctx, struct winbindd_tdc_domain);
4679 if (!d)
4680 break;
4682 d->domain_name = talloc_strdup(d,
4683 dom_list[i].domain_name);
4685 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4686 sid_copy(&d->sid, &dom_list[i].sid);
4687 d->trust_flags = dom_list[i].trust_flags;
4688 d->trust_type = dom_list[i].trust_type;
4689 d->trust_attribs = dom_list[i].trust_attribs;
4691 break;
4695 TALLOC_FREE(dom_list);
4697 return d;
4701 /*********************************************************************
4702 ********************************************************************/
4704 void wcache_tdc_clear( void )
4706 if ( !init_wcache() )
4707 return;
4709 wcache_tdc_store_list( NULL, 0 );
4711 return;
4715 /*********************************************************************
4716 ********************************************************************/
4718 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4719 NTSTATUS status,
4720 const struct dom_sid *user_sid,
4721 const char *homedir,
4722 const char *shell,
4723 const char *gecos,
4724 uint32 gid)
4726 struct cache_entry *centry;
4727 fstring tmp;
4729 if ( (centry = centry_start(domain, status)) == NULL )
4730 return;
4732 centry_put_string( centry, homedir );
4733 centry_put_string( centry, shell );
4734 centry_put_string( centry, gecos );
4735 centry_put_uint32( centry, gid );
4737 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4739 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4741 centry_free(centry);
4744 #ifdef HAVE_ADS
4746 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4747 const struct dom_sid *user_sid,
4748 TALLOC_CTX *ctx,
4749 const char **homedir, const char **shell,
4750 const char **gecos, gid_t *p_gid)
4752 struct winbind_cache *cache = get_cache(domain);
4753 struct cache_entry *centry = NULL;
4754 NTSTATUS nt_status;
4755 fstring tmp;
4757 if (!cache->tdb)
4758 goto do_query;
4760 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4761 sid_to_fstring(tmp, user_sid));
4763 if (!centry)
4764 goto do_query;
4766 *homedir = centry_string( centry, ctx );
4767 *shell = centry_string( centry, ctx );
4768 *gecos = centry_string( centry, ctx );
4769 *p_gid = centry_uint32( centry );
4771 centry_free(centry);
4773 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4774 sid_string_dbg(user_sid)));
4776 return NT_STATUS_OK;
4778 do_query:
4780 nt_status = nss_get_info( domain->name, user_sid, ctx,
4781 homedir, shell, gecos, p_gid );
4783 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4785 if ( NT_STATUS_IS_OK(nt_status) ) {
4786 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4787 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4788 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4789 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4791 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4792 *homedir, *shell, *gecos, *p_gid );
4795 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4796 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4797 domain->name ));
4798 set_domain_offline( domain );
4801 return nt_status;
4804 #endif
4806 /* the cache backend methods are exposed via this structure */
4807 struct winbindd_methods cache_methods = {
4808 true,
4809 query_user_list,
4810 enum_dom_groups,
4811 enum_local_groups,
4812 name_to_sid,
4813 sid_to_name,
4814 rids_to_names,
4815 query_user,
4816 lookup_usergroups,
4817 lookup_useraliases,
4818 lookup_groupmem,
4819 sequence_number,
4820 lockout_policy,
4821 password_policy,
4822 trusted_domains
4825 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4826 uint32_t opnum, const DATA_BLOB *req,
4827 TDB_DATA *pkey)
4829 char *key;
4830 size_t keylen;
4832 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4833 if (key == NULL) {
4834 return false;
4836 keylen = talloc_get_size(key) - 1;
4838 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4839 if (key == NULL) {
4840 return false;
4842 memcpy(key + keylen, req->data, req->length);
4844 pkey->dptr = (uint8_t *)key;
4845 pkey->dsize = talloc_get_size(key);
4846 return true;
4849 static bool wcache_opnum_cacheable(uint32_t opnum)
4851 switch (opnum) {
4852 case NDR_WBINT_PING:
4853 case NDR_WBINT_QUERYSEQUENCENUMBER:
4854 case NDR_WBINT_ALLOCATEUID:
4855 case NDR_WBINT_ALLOCATEGID:
4856 case NDR_WBINT_CHECKMACHINEACCOUNT:
4857 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4858 case NDR_WBINT_PINGDC:
4859 return false;
4861 return true;
4864 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4865 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4867 TDB_DATA key, data;
4868 bool ret = false;
4870 if (!wcache_opnum_cacheable(opnum) ||
4871 is_my_own_sam_domain(domain) ||
4872 is_builtin_domain(domain)) {
4873 return false;
4876 if (wcache->tdb == NULL) {
4877 return false;
4880 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4881 return false;
4883 data = tdb_fetch_compat(wcache->tdb, key);
4884 TALLOC_FREE(key.dptr);
4886 if (data.dptr == NULL) {
4887 return false;
4889 if (data.dsize < 12) {
4890 goto fail;
4893 if (!is_domain_offline(domain)) {
4894 uint32_t entry_seqnum, dom_seqnum, last_check;
4895 uint64_t entry_timeout;
4897 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4898 &last_check)) {
4899 goto fail;
4901 entry_seqnum = IVAL(data.dptr, 0);
4902 if (entry_seqnum != dom_seqnum) {
4903 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4904 (int)entry_seqnum));
4905 goto fail;
4907 entry_timeout = BVAL(data.dptr, 4);
4908 if (time(NULL) > entry_timeout) {
4909 DEBUG(10, ("Entry has timed out\n"));
4910 goto fail;
4914 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4915 data.dsize - 12);
4916 if (resp->data == NULL) {
4917 DEBUG(10, ("talloc failed\n"));
4918 goto fail;
4920 resp->length = data.dsize - 12;
4922 ret = true;
4923 fail:
4924 SAFE_FREE(data.dptr);
4925 return ret;
4928 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4929 const DATA_BLOB *req, const DATA_BLOB *resp)
4931 TDB_DATA key, data;
4932 uint32_t dom_seqnum, last_check;
4933 uint64_t timeout;
4935 if (!wcache_opnum_cacheable(opnum) ||
4936 is_my_own_sam_domain(domain) ||
4937 is_builtin_domain(domain)) {
4938 return;
4941 if (wcache->tdb == NULL) {
4942 return;
4945 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4946 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4947 domain->name));
4948 return;
4951 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4952 return;
4955 timeout = time(NULL) + lp_winbind_cache_time();
4957 data.dsize = resp->length + 12;
4958 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4959 if (data.dptr == NULL) {
4960 goto done;
4963 SIVAL(data.dptr, 0, dom_seqnum);
4964 SBVAL(data.dptr, 4, timeout);
4965 memcpy(data.dptr + 12, resp->data, resp->length);
4967 tdb_store(wcache->tdb, key, data, 0);
4969 done:
4970 TALLOC_FREE(key.dptr);
4971 return;