s3-pdbtest: only test trusted domains when pdb backends offers trusted domain support.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_cache.c
blobffe3f389685d7188d86735d2ce570d35b99ff7ed
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_wbint.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
60 static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "WINBINDD_OFFLINE",
63 WINBINDD_CACHE_VERSION_KEYSTR,
64 NULL
67 /************************************************************************
68 Is this key a non-centry type ?
69 ************************************************************************/
71 static bool is_non_centry_key(TDB_DATA kbuf)
73 int i;
75 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76 return false;
78 for (i = 0; non_centry_keys[i] != NULL; i++) {
79 size_t namelen = strlen(non_centry_keys[i]);
80 if (kbuf.dsize < namelen) {
81 continue;
83 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84 return true;
87 return false;
90 /* Global online/offline state - False when online. winbindd starts up online
91 and sets this to true if the first query fails and there's an entry in
92 the cache tdb telling us to stay offline. */
94 static bool global_winbindd_offline_state;
96 struct winbind_cache {
97 TDB_CONTEXT *tdb;
100 struct cache_entry {
101 NTSTATUS status;
102 uint32 sequence_number;
103 uint64_t timeout;
104 uint8 *data;
105 uint32 len, ofs;
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
112 static struct winbind_cache *wcache;
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
117 struct winbind_cache *ret = wcache;
119 /* We have to know what type of domain we are dealing with first. */
121 if (domain->internal) {
122 domain->backend = &builtin_passdb_methods;
123 domain->initialized = True;
126 if (strequal(domain->name, get_global_sam_name()) &&
127 sid_check_is_domain(&domain->sid)) {
128 domain->backend = &sam_passdb_methods;
129 domain->initialized = True;
132 if ( !domain->initialized ) {
133 init_dc_connection( domain );
137 OK. listen up becasue I'm only going to say this once.
138 We have the following scenarios to consider
139 (a) trusted AD domains on a Samba DC,
140 (b) trusted AD domains and we are joined to a non-kerberos domain
141 (c) trusted AD domains and we are joined to a kerberos (AD) domain
143 For (a) we can always contact the trusted domain using krb5
144 since we have the domain trust account password
146 For (b) we can only use RPC since we have no way of
147 getting a krb5 ticket in our own domain
149 For (c) we can always use krb5 since we have a kerberos trust
151 --jerry
154 if (!domain->backend) {
155 #ifdef HAVE_ADS
156 struct winbindd_domain *our_domain = domain;
158 /* find our domain first so we can figure out if we
159 are joined to a kerberized domain */
161 if ( !domain->primary )
162 our_domain = find_our_domain();
164 if ((our_domain->active_directory || IS_DC)
165 && domain->active_directory
166 && !lp_winbind_rpc_only()) {
167 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
168 domain->backend = &ads_methods;
169 } else {
170 #endif /* HAVE_ADS */
171 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
172 domain->backend = &reconnect_methods;
173 #ifdef HAVE_ADS
175 #endif /* HAVE_ADS */
178 if (ret)
179 return ret;
181 ret = SMB_XMALLOC_P(struct winbind_cache);
182 ZERO_STRUCTP(ret);
184 wcache = ret;
185 wcache_flush_cache();
187 return ret;
191 free a centry structure
193 static void centry_free(struct cache_entry *centry)
195 if (!centry)
196 return;
197 SAFE_FREE(centry->data);
198 free(centry);
201 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
203 if (centry->len - centry->ofs < nbytes) {
204 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
205 (unsigned int)nbytes,
206 centry->len - centry->ofs));
207 return false;
209 return true;
213 pull a uint64_t from a cache entry
215 static uint64_t centry_uint64_t(struct cache_entry *centry)
217 uint64_t ret;
219 if (!centry_check_bytes(centry, 8)) {
220 smb_panic_fn("centry_uint64_t");
222 ret = BVAL(centry->data, centry->ofs);
223 centry->ofs += 8;
224 return ret;
228 pull a uint32 from a cache entry
230 static uint32 centry_uint32(struct cache_entry *centry)
232 uint32 ret;
234 if (!centry_check_bytes(centry, 4)) {
235 smb_panic_fn("centry_uint32");
237 ret = IVAL(centry->data, centry->ofs);
238 centry->ofs += 4;
239 return ret;
243 pull a uint16 from a cache entry
245 static uint16 centry_uint16(struct cache_entry *centry)
247 uint16 ret;
248 if (!centry_check_bytes(centry, 2)) {
249 smb_panic_fn("centry_uint16");
251 ret = SVAL(centry->data, centry->ofs);
252 centry->ofs += 2;
253 return ret;
257 pull a uint8 from a cache entry
259 static uint8 centry_uint8(struct cache_entry *centry)
261 uint8 ret;
262 if (!centry_check_bytes(centry, 1)) {
263 smb_panic_fn("centry_uint8");
265 ret = CVAL(centry->data, centry->ofs);
266 centry->ofs += 1;
267 return ret;
271 pull a NTTIME from a cache entry
273 static NTTIME centry_nttime(struct cache_entry *centry)
275 NTTIME ret;
276 if (!centry_check_bytes(centry, 8)) {
277 smb_panic_fn("centry_nttime");
279 ret = IVAL(centry->data, centry->ofs);
280 centry->ofs += 4;
281 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
282 centry->ofs += 4;
283 return ret;
287 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
289 static time_t centry_time(struct cache_entry *centry)
291 return (time_t)centry_nttime(centry);
294 /* pull a string from a cache entry, using the supplied
295 talloc context
297 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
299 uint32 len;
300 char *ret;
302 len = centry_uint8(centry);
304 if (len == 0xFF) {
305 /* a deliberate NULL string */
306 return NULL;
309 if (!centry_check_bytes(centry, (size_t)len)) {
310 smb_panic_fn("centry_string");
313 ret = talloc_array(mem_ctx, char, len+1);
314 if (!ret) {
315 smb_panic_fn("centry_string out of memory\n");
317 memcpy(ret,centry->data + centry->ofs, len);
318 ret[len] = 0;
319 centry->ofs += len;
320 return ret;
323 /* pull a hash16 from a cache entry, using the supplied
324 talloc context
326 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
328 uint32 len;
329 char *ret;
331 len = centry_uint8(centry);
333 if (len != 16) {
334 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 len ));
336 return NULL;
339 if (!centry_check_bytes(centry, 16)) {
340 return NULL;
343 ret = talloc_array(mem_ctx, char, 16);
344 if (!ret) {
345 smb_panic_fn("centry_hash out of memory\n");
347 memcpy(ret,centry->data + centry->ofs, 16);
348 centry->ofs += 16;
349 return ret;
352 /* pull a sid from a cache entry, using the supplied
353 talloc context
355 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
357 char *sid_string;
358 bool ret;
360 sid_string = centry_string(centry, talloc_tos());
361 if (sid_string == NULL) {
362 return false;
364 ret = string_to_sid(sid, sid_string);
365 TALLOC_FREE(sid_string);
366 return ret;
371 pull a NTSTATUS from a cache entry
373 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 NTSTATUS status;
377 status = NT_STATUS(centry_uint32(centry));
378 return status;
382 /* the server is considered down if it can't give us a sequence number */
383 static bool wcache_server_down(struct winbindd_domain *domain)
385 bool ret;
387 if (!wcache->tdb)
388 return false;
390 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
392 if (ret)
393 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
394 domain->name ));
395 return ret;
398 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
399 uint32_t *last_seq_check)
401 char *key;
402 TDB_DATA data;
404 if (wcache->tdb == NULL) {
405 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
406 return false;
409 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
410 if (key == NULL) {
411 DEBUG(10, ("talloc failed\n"));
412 return false;
415 data = tdb_fetch_bystring(wcache->tdb, key);
416 TALLOC_FREE(key);
418 if (data.dptr == NULL) {
419 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
420 domain_name));
421 return false;
423 if (data.dsize != 8) {
424 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
425 (int)data.dsize));
426 SAFE_FREE(data.dptr);
427 return false;
430 *seqnum = IVAL(data.dptr, 0);
431 *last_seq_check = IVAL(data.dptr, 4);
432 SAFE_FREE(data.dptr);
434 return true;
437 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
439 uint32 last_check, time_diff;
441 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
442 &last_check)) {
443 return NT_STATUS_UNSUCCESSFUL;
445 domain->last_seq_check = last_check;
447 /* have we expired? */
449 time_diff = now - domain->last_seq_check;
450 if ( time_diff > lp_winbind_cache_time() ) {
451 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
452 domain->name, domain->sequence_number,
453 (uint32)domain->last_seq_check));
454 return NT_STATUS_UNSUCCESSFUL;
457 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
458 domain->name, domain->sequence_number,
459 (uint32)domain->last_seq_check));
461 return NT_STATUS_OK;
464 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
465 time_t last_seq_check)
467 char *key_str;
468 uint8_t buf[8];
469 int ret;
471 if (wcache->tdb == NULL) {
472 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
473 return false;
476 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
477 if (key_str == NULL) {
478 DEBUG(10, ("talloc_asprintf failed\n"));
479 return false;
482 SIVAL(buf, 0, seqnum);
483 SIVAL(buf, 4, last_seq_check);
485 ret = tdb_store_bystring(wcache->tdb, key_str,
486 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
487 TALLOC_FREE(key_str);
488 if (ret != 0) {
489 DEBUG(10, ("tdb_store_bystring failed: %s\n",
490 tdb_errorstr_compat(wcache->tdb)));
491 TALLOC_FREE(key_str);
492 return false;
495 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
496 domain_name, seqnum, (unsigned)last_seq_check));
498 return true;
501 static bool store_cache_seqnum( struct winbindd_domain *domain )
503 return wcache_store_seqnum(domain->name, domain->sequence_number,
504 domain->last_seq_check);
508 refresh the domain sequence number. If force is true
509 then always refresh it, no matter how recently we fetched it
512 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 NTSTATUS status;
515 unsigned time_diff;
516 time_t t = time(NULL);
517 unsigned cache_time = lp_winbind_cache_time();
519 if (is_domain_offline(domain)) {
520 return;
523 get_cache( domain );
525 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
526 /* trying to reconnect is expensive, don't do it too often */
527 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
528 cache_time *= 8;
530 #endif
532 time_diff = t - domain->last_seq_check;
534 /* see if we have to refetch the domain sequence number */
535 if (!force && (time_diff < cache_time) &&
536 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
537 NT_STATUS_IS_OK(domain->last_status)) {
538 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
539 goto done;
542 /* try to get the sequence number from the tdb cache first */
543 /* this will update the timestamp as well */
545 status = fetch_cache_seqnum( domain, t );
546 if (NT_STATUS_IS_OK(status) &&
547 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
548 NT_STATUS_IS_OK(domain->last_status)) {
549 goto done;
552 /* important! make sure that we know if this is a native
553 mode domain or not. And that we can contact it. */
555 if ( winbindd_can_contact_domain( domain ) ) {
556 status = domain->backend->sequence_number(domain,
557 &domain->sequence_number);
558 } else {
559 /* just use the current time */
560 status = NT_STATUS_OK;
561 domain->sequence_number = time(NULL);
565 /* the above call could have set our domain->backend to NULL when
566 * coming from offline to online mode, make sure to reinitialize the
567 * backend - Guenther */
568 get_cache( domain );
570 if (!NT_STATUS_IS_OK(status)) {
571 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
572 domain->sequence_number = DOM_SEQUENCE_NONE;
575 domain->last_status = status;
576 domain->last_seq_check = time(NULL);
578 /* save the new sequence number in the cache */
579 store_cache_seqnum( domain );
581 done:
582 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
583 domain->name, domain->sequence_number));
585 return;
589 decide if a cache entry has expired
591 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
593 /* If we've been told to be offline - stay in that state... */
594 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
595 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
596 keystr, domain->name ));
597 return false;
600 /* when the domain is offline return the cached entry.
601 * This deals with transient offline states... */
603 if (!domain->online) {
604 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
605 keystr, domain->name ));
606 return false;
609 /* if the server is OK and our cache entry came from when it was down then
610 the entry is invalid */
611 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
612 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
613 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
614 keystr, domain->name ));
615 return true;
618 /* if the server is down or the cache entry is not older than the
619 current sequence number or it did not timeout then it is OK */
620 if (wcache_server_down(domain)
621 || ((centry->sequence_number == domain->sequence_number)
622 && (centry->timeout > time(NULL)))) {
623 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
624 keystr, domain->name ));
625 return false;
628 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
629 keystr, domain->name ));
631 /* it's expired */
632 return true;
635 static struct cache_entry *wcache_fetch_raw(char *kstr)
637 TDB_DATA data;
638 struct cache_entry *centry;
639 TDB_DATA key;
641 key = string_tdb_data(kstr);
642 data = tdb_fetch_compat(wcache->tdb, key);
643 if (!data.dptr) {
644 /* a cache miss */
645 return NULL;
648 centry = SMB_XMALLOC_P(struct cache_entry);
649 centry->data = (unsigned char *)data.dptr;
650 centry->len = data.dsize;
651 centry->ofs = 0;
653 if (centry->len < 16) {
654 /* huh? corrupt cache? */
655 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
656 "(len < 16)?\n", kstr));
657 centry_free(centry);
658 return NULL;
661 centry->status = centry_ntstatus(centry);
662 centry->sequence_number = centry_uint32(centry);
663 centry->timeout = centry_uint64_t(centry);
665 return centry;
668 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
670 if (strequal(domain->name, get_global_sam_name()) &&
671 sid_check_is_domain(&domain->sid)) {
672 return true;
675 return false;
678 static bool is_builtin_domain(struct winbindd_domain *domain)
680 if (strequal(domain->name, "BUILTIN") &&
681 sid_check_is_builtin(&domain->sid)) {
682 return true;
685 return false;
689 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
690 number and return status
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
696 struct winbindd_domain *domain,
697 const char *format, ...)
699 va_list ap;
700 char *kstr;
701 struct cache_entry *centry;
703 if (!winbindd_use_cache() ||
704 is_my_own_sam_domain(domain) ||
705 is_builtin_domain(domain)) {
706 return NULL;
709 refresh_sequence_number(domain, false);
711 va_start(ap, format);
712 smb_xvasprintf(&kstr, format, ap);
713 va_end(ap);
715 centry = wcache_fetch_raw(kstr);
716 if (centry == NULL) {
717 free(kstr);
718 return NULL;
721 if (centry_expired(domain, kstr, centry)) {
723 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
724 kstr, domain->name ));
726 centry_free(centry);
727 free(kstr);
728 return NULL;
731 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
732 kstr, domain->name ));
734 free(kstr);
735 return centry;
738 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
739 static void wcache_delete(const char *format, ...)
741 va_list ap;
742 char *kstr;
743 TDB_DATA key;
745 va_start(ap, format);
746 smb_xvasprintf(&kstr, format, ap);
747 va_end(ap);
749 key = string_tdb_data(kstr);
751 tdb_delete(wcache->tdb, key);
752 free(kstr);
756 make sure we have at least len bytes available in a centry
758 static void centry_expand(struct cache_entry *centry, uint32 len)
760 if (centry->len - centry->ofs >= len)
761 return;
762 centry->len *= 2;
763 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
764 centry->len);
765 if (!centry->data) {
766 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
767 smb_panic_fn("out of memory in centry_expand");
772 push a uint64_t into a centry
774 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
776 centry_expand(centry, 8);
777 SBVAL(centry->data, centry->ofs, v);
778 centry->ofs += 8;
782 push a uint32 into a centry
784 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
786 centry_expand(centry, 4);
787 SIVAL(centry->data, centry->ofs, v);
788 centry->ofs += 4;
792 push a uint16 into a centry
794 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
796 centry_expand(centry, 2);
797 SSVAL(centry->data, centry->ofs, v);
798 centry->ofs += 2;
802 push a uint8 into a centry
804 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
806 centry_expand(centry, 1);
807 SCVAL(centry->data, centry->ofs, v);
808 centry->ofs += 1;
812 push a string into a centry
814 static void centry_put_string(struct cache_entry *centry, const char *s)
816 int len;
818 if (!s) {
819 /* null strings are marked as len 0xFFFF */
820 centry_put_uint8(centry, 0xFF);
821 return;
824 len = strlen(s);
825 /* can't handle more than 254 char strings. Truncating is probably best */
826 if (len > 254) {
827 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
828 len = 254;
830 centry_put_uint8(centry, len);
831 centry_expand(centry, len);
832 memcpy(centry->data + centry->ofs, s, len);
833 centry->ofs += len;
837 push a 16 byte hash into a centry - treat as 16 byte string.
839 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
841 centry_put_uint8(centry, 16);
842 centry_expand(centry, 16);
843 memcpy(centry->data + centry->ofs, val, 16);
844 centry->ofs += 16;
847 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
849 fstring sid_string;
850 centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 put NTSTATUS into a centry
857 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
859 uint32 status_value = NT_STATUS_V(status);
860 centry_put_uint32(centry, status_value);
865 push a NTTIME into a centry
867 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
869 centry_expand(centry, 8);
870 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
871 centry->ofs += 4;
872 SIVAL(centry->data, centry->ofs, nt >> 32);
873 centry->ofs += 4;
877 push a time_t into a centry - use a 64 bit size.
878 NTTIME here is being used as a convenient 64-bit size.
880 static void centry_put_time(struct cache_entry *centry, time_t t)
882 NTTIME nt = (NTTIME)t;
883 centry_put_nttime(centry, nt);
887 start a centry for output. When finished, call centry_end()
889 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
891 struct cache_entry *centry;
893 if (!wcache->tdb)
894 return NULL;
896 centry = SMB_XMALLOC_P(struct cache_entry);
898 centry->len = 8192; /* reasonable default */
899 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
900 centry->ofs = 0;
901 centry->sequence_number = domain->sequence_number;
902 centry->timeout = lp_winbind_cache_time() + time(NULL);
903 centry_put_ntstatus(centry, status);
904 centry_put_uint32(centry, centry->sequence_number);
905 centry_put_uint64_t(centry, centry->timeout);
906 return centry;
910 finish a centry and write it to the tdb
912 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
913 static void centry_end(struct cache_entry *centry, const char *format, ...)
915 va_list ap;
916 char *kstr;
917 TDB_DATA key, data;
919 if (!winbindd_use_cache()) {
920 return;
923 va_start(ap, format);
924 smb_xvasprintf(&kstr, format, ap);
925 va_end(ap);
927 key = string_tdb_data(kstr);
928 data.dptr = centry->data;
929 data.dsize = centry->ofs;
931 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
932 free(kstr);
935 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
936 NTSTATUS status, const char *domain_name,
937 const char *name, const struct dom_sid *sid,
938 enum lsa_SidType type)
940 struct cache_entry *centry;
941 fstring uname;
943 centry = centry_start(domain, status);
944 if (!centry)
945 return;
946 centry_put_uint32(centry, type);
947 centry_put_sid(centry, sid);
948 fstrcpy(uname, name);
949 strupper_m(uname);
950 centry_end(centry, "NS/%s/%s", domain_name, uname);
951 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
952 uname, sid_string_dbg(sid), nt_errstr(status)));
953 centry_free(centry);
956 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
957 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
959 struct cache_entry *centry;
960 fstring sid_string;
962 centry = centry_start(domain, status);
963 if (!centry)
964 return;
966 if (NT_STATUS_IS_OK(status)) {
967 centry_put_uint32(centry, type);
968 centry_put_string(centry, domain_name);
969 centry_put_string(centry, name);
972 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
973 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
974 domain_name, name, nt_errstr(status)));
975 centry_free(centry);
979 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
980 struct wbint_userinfo *info)
982 struct cache_entry *centry;
983 fstring sid_string;
985 if (is_null_sid(&info->user_sid)) {
986 return;
989 centry = centry_start(domain, status);
990 if (!centry)
991 return;
992 centry_put_string(centry, info->acct_name);
993 centry_put_string(centry, info->full_name);
994 centry_put_string(centry, info->homedir);
995 centry_put_string(centry, info->shell);
996 centry_put_uint32(centry, info->primary_gid);
997 centry_put_sid(centry, &info->user_sid);
998 centry_put_sid(centry, &info->group_sid);
999 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1000 &info->user_sid));
1001 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1002 centry_free(centry);
1005 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1006 NTSTATUS status,
1007 struct samr_DomInfo12 *lockout_policy)
1009 struct cache_entry *centry;
1011 centry = centry_start(domain, status);
1012 if (!centry)
1013 return;
1015 centry_put_nttime(centry, lockout_policy->lockout_duration);
1016 centry_put_nttime(centry, lockout_policy->lockout_window);
1017 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1019 centry_end(centry, "LOC_POL/%s", domain->name);
1021 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1023 centry_free(centry);
1028 static void wcache_save_password_policy(struct winbindd_domain *domain,
1029 NTSTATUS status,
1030 struct samr_DomInfo1 *policy)
1032 struct cache_entry *centry;
1034 centry = centry_start(domain, status);
1035 if (!centry)
1036 return;
1038 centry_put_uint16(centry, policy->min_password_length);
1039 centry_put_uint16(centry, policy->password_history_length);
1040 centry_put_uint32(centry, policy->password_properties);
1041 centry_put_nttime(centry, policy->max_password_age);
1042 centry_put_nttime(centry, policy->min_password_age);
1044 centry_end(centry, "PWD_POL/%s", domain->name);
1046 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1048 centry_free(centry);
1051 /***************************************************************************
1052 ***************************************************************************/
1054 static void wcache_save_username_alias(struct winbindd_domain *domain,
1055 NTSTATUS status,
1056 const char *name, const char *alias)
1058 struct cache_entry *centry;
1059 fstring uname;
1061 if ( (centry = centry_start(domain, status)) == NULL )
1062 return;
1064 centry_put_string( centry, alias );
1066 fstrcpy(uname, name);
1067 strupper_m(uname);
1068 centry_end(centry, "NSS/NA/%s", uname);
1070 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1072 centry_free(centry);
1075 static void wcache_save_alias_username(struct winbindd_domain *domain,
1076 NTSTATUS status,
1077 const char *alias, const char *name)
1079 struct cache_entry *centry;
1080 fstring uname;
1082 if ( (centry = centry_start(domain, status)) == NULL )
1083 return;
1085 centry_put_string( centry, name );
1087 fstrcpy(uname, alias);
1088 strupper_m(uname);
1089 centry_end(centry, "NSS/AN/%s", uname);
1091 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1093 centry_free(centry);
1096 /***************************************************************************
1097 ***************************************************************************/
1099 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1100 struct winbindd_domain *domain,
1101 const char *name, char **alias )
1103 struct winbind_cache *cache = get_cache(domain);
1104 struct cache_entry *centry = NULL;
1105 NTSTATUS status;
1106 char *upper_name;
1108 if ( domain->internal )
1109 return NT_STATUS_NOT_SUPPORTED;
1111 if (!cache->tdb)
1112 goto do_query;
1114 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1115 return NT_STATUS_NO_MEMORY;
1116 strupper_m(upper_name);
1118 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1120 SAFE_FREE( upper_name );
1122 if (!centry)
1123 goto do_query;
1125 status = centry->status;
1127 if (!NT_STATUS_IS_OK(status)) {
1128 centry_free(centry);
1129 return status;
1132 *alias = centry_string( centry, mem_ctx );
1134 centry_free(centry);
1136 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1137 name, *alias ? *alias : "(none)"));
1139 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1141 do_query:
1143 /* If its not in cache and we are offline, then fail */
1145 if ( get_global_winbindd_state_offline() || !domain->online ) {
1146 DEBUG(8,("resolve_username_to_alias: rejecting query "
1147 "in offline mode\n"));
1148 return NT_STATUS_NOT_FOUND;
1151 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1153 if ( NT_STATUS_IS_OK( status ) ) {
1154 wcache_save_username_alias(domain, status, name, *alias);
1157 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1158 wcache_save_username_alias(domain, status, name, "(NULL)");
1161 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1162 nt_errstr(status)));
1164 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1165 set_domain_offline( domain );
1168 return status;
1171 /***************************************************************************
1172 ***************************************************************************/
1174 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1175 struct winbindd_domain *domain,
1176 const char *alias, char **name )
1178 struct winbind_cache *cache = get_cache(domain);
1179 struct cache_entry *centry = NULL;
1180 NTSTATUS status;
1181 char *upper_name;
1183 if ( domain->internal )
1184 return NT_STATUS_NOT_SUPPORTED;
1186 if (!cache->tdb)
1187 goto do_query;
1189 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1190 return NT_STATUS_NO_MEMORY;
1191 strupper_m(upper_name);
1193 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1195 SAFE_FREE( upper_name );
1197 if (!centry)
1198 goto do_query;
1200 status = centry->status;
1202 if (!NT_STATUS_IS_OK(status)) {
1203 centry_free(centry);
1204 return status;
1207 *name = centry_string( centry, mem_ctx );
1209 centry_free(centry);
1211 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1212 alias, *name ? *name : "(none)"));
1214 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1216 do_query:
1218 /* If its not in cache and we are offline, then fail */
1220 if ( get_global_winbindd_state_offline() || !domain->online ) {
1221 DEBUG(8,("resolve_alias_to_username: rejecting query "
1222 "in offline mode\n"));
1223 return NT_STATUS_NOT_FOUND;
1226 /* an alias cannot contain a domain prefix or '@' */
1228 if (strchr(alias, '\\') || strchr(alias, '@')) {
1229 DEBUG(10,("resolve_alias_to_username: skipping fully "
1230 "qualified name %s\n", alias));
1231 return NT_STATUS_OBJECT_NAME_INVALID;
1234 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1236 if ( NT_STATUS_IS_OK( status ) ) {
1237 wcache_save_alias_username( domain, status, alias, *name );
1240 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1241 wcache_save_alias_username(domain, status, alias, "(NULL)");
1244 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1245 nt_errstr(status)));
1247 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1248 set_domain_offline( domain );
1251 return status;
1254 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1256 struct winbind_cache *cache = get_cache(domain);
1257 TDB_DATA data;
1258 fstring key_str, tmp;
1259 uint32 rid;
1261 if (!cache->tdb) {
1262 return NT_STATUS_INTERNAL_DB_ERROR;
1265 if (is_null_sid(sid)) {
1266 return NT_STATUS_INVALID_SID;
1269 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1270 return NT_STATUS_INVALID_SID;
1273 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1275 data = tdb_fetch_compat(cache->tdb, string_tdb_data(key_str));
1276 if (!data.dptr) {
1277 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1280 SAFE_FREE(data.dptr);
1281 return NT_STATUS_OK;
1284 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1285 as new salted ones. */
1287 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1288 TALLOC_CTX *mem_ctx,
1289 const struct dom_sid *sid,
1290 const uint8 **cached_nt_pass,
1291 const uint8 **cached_salt)
1293 struct winbind_cache *cache = get_cache(domain);
1294 struct cache_entry *centry = NULL;
1295 NTSTATUS status;
1296 time_t t;
1297 uint32 rid;
1298 fstring tmp;
1300 if (!winbindd_use_cache()) {
1301 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1304 if (!cache->tdb) {
1305 return NT_STATUS_INTERNAL_DB_ERROR;
1308 if (is_null_sid(sid)) {
1309 return NT_STATUS_INVALID_SID;
1312 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1313 return NT_STATUS_INVALID_SID;
1316 /* Try and get a salted cred first. If we can't
1317 fall back to an unsalted cred. */
1319 centry = wcache_fetch(cache, domain, "CRED/%s",
1320 sid_to_fstring(tmp, sid));
1321 if (!centry) {
1322 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1323 sid_string_dbg(sid)));
1324 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1327 t = centry_time(centry);
1329 /* In the salted case this isn't actually the nt_hash itself,
1330 but the MD5 of the salt + nt_hash. Let the caller
1331 sort this out. It can tell as we only return the cached_salt
1332 if we are returning a salted cred. */
1334 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1335 if (*cached_nt_pass == NULL) {
1336 fstring sidstr;
1338 sid_to_fstring(sidstr, sid);
1340 /* Bad (old) cred cache. Delete and pretend we
1341 don't have it. */
1342 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1343 sidstr));
1344 wcache_delete("CRED/%s", sidstr);
1345 centry_free(centry);
1346 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1349 /* We only have 17 bytes more data in the salted cred case. */
1350 if (centry->len - centry->ofs == 17) {
1351 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1352 } else {
1353 *cached_salt = NULL;
1356 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1357 if (*cached_salt) {
1358 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1361 status = centry->status;
1363 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1364 sid_string_dbg(sid), nt_errstr(status) ));
1366 centry_free(centry);
1367 return status;
1370 /* Store creds for a SID - only writes out new salted ones. */
1372 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1373 const struct dom_sid *sid,
1374 const uint8 nt_pass[NT_HASH_LEN])
1376 struct cache_entry *centry;
1377 fstring sid_string;
1378 uint32 rid;
1379 uint8 cred_salt[NT_HASH_LEN];
1380 uint8 salted_hash[NT_HASH_LEN];
1382 if (is_null_sid(sid)) {
1383 return NT_STATUS_INVALID_SID;
1386 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1387 return NT_STATUS_INVALID_SID;
1390 centry = centry_start(domain, NT_STATUS_OK);
1391 if (!centry) {
1392 return NT_STATUS_INTERNAL_DB_ERROR;
1395 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1397 centry_put_time(centry, time(NULL));
1399 /* Create a salt and then salt the hash. */
1400 generate_random_buffer(cred_salt, NT_HASH_LEN);
1401 E_md5hash(cred_salt, nt_pass, salted_hash);
1403 centry_put_hash16(centry, salted_hash);
1404 centry_put_hash16(centry, cred_salt);
1405 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1407 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1409 centry_free(centry);
1411 return NT_STATUS_OK;
1415 /* Query display info. This is the basic user list fn */
1416 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1417 TALLOC_CTX *mem_ctx,
1418 uint32 *num_entries,
1419 struct wbint_userinfo **info)
1421 struct winbind_cache *cache = get_cache(domain);
1422 struct cache_entry *centry = NULL;
1423 NTSTATUS status;
1424 unsigned int i, retry;
1425 bool old_status = domain->online;
1427 if (!cache->tdb)
1428 goto do_query;
1430 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1431 if (!centry)
1432 goto do_query;
1434 do_fetch_cache:
1435 *num_entries = centry_uint32(centry);
1437 if (*num_entries == 0)
1438 goto do_cached;
1440 (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1441 if (! (*info)) {
1442 smb_panic_fn("query_user_list out of memory");
1444 for (i=0; i<(*num_entries); i++) {
1445 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1446 (*info)[i].full_name = centry_string(centry, mem_ctx);
1447 (*info)[i].homedir = centry_string(centry, mem_ctx);
1448 (*info)[i].shell = centry_string(centry, mem_ctx);
1449 centry_sid(centry, &(*info)[i].user_sid);
1450 centry_sid(centry, &(*info)[i].group_sid);
1453 do_cached:
1454 status = centry->status;
1456 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1457 domain->name, nt_errstr(status) ));
1459 centry_free(centry);
1460 return status;
1462 do_query:
1463 *num_entries = 0;
1464 *info = NULL;
1466 /* Return status value returned by seq number check */
1468 if (!NT_STATUS_IS_OK(domain->last_status))
1469 return domain->last_status;
1471 /* Put the query_user_list() in a retry loop. There appears to be
1472 * some bug either with Windows 2000 or Samba's handling of large
1473 * rpc replies. This manifests itself as sudden disconnection
1474 * at a random point in the enumeration of a large (60k) user list.
1475 * The retry loop simply tries the operation again. )-: It's not
1476 * pretty but an acceptable workaround until we work out what the
1477 * real problem is. */
1479 retry = 0;
1480 do {
1482 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1483 domain->name ));
1485 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1486 if (!NT_STATUS_IS_OK(status)) {
1487 DEBUG(3, ("query_user_list: returned 0x%08x, "
1488 "retrying\n", NT_STATUS_V(status)));
1490 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1491 DEBUG(3, ("query_user_list: flushing "
1492 "connection cache\n"));
1493 invalidate_cm_connection(&domain->conn);
1495 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1496 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1497 if (!domain->internal && old_status) {
1498 set_domain_offline(domain);
1500 /* store partial response. */
1501 if (*num_entries > 0) {
1503 * humm, what about the status used for cache?
1504 * Should it be NT_STATUS_OK?
1506 break;
1509 * domain is offline now, and there is no user entries,
1510 * try to fetch from cache again.
1512 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1513 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1514 /* partial response... */
1515 if (!centry) {
1516 goto skip_save;
1517 } else {
1518 goto do_fetch_cache;
1520 } else {
1521 goto skip_save;
1525 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1526 (retry++ < 5));
1528 /* and save it */
1529 refresh_sequence_number(domain, false);
1530 if (!NT_STATUS_IS_OK(status)) {
1531 return status;
1533 centry = centry_start(domain, status);
1534 if (!centry)
1535 goto skip_save;
1536 centry_put_uint32(centry, *num_entries);
1537 for (i=0; i<(*num_entries); i++) {
1538 centry_put_string(centry, (*info)[i].acct_name);
1539 centry_put_string(centry, (*info)[i].full_name);
1540 centry_put_string(centry, (*info)[i].homedir);
1541 centry_put_string(centry, (*info)[i].shell);
1542 centry_put_sid(centry, &(*info)[i].user_sid);
1543 centry_put_sid(centry, &(*info)[i].group_sid);
1544 if (domain->backend && domain->backend->consistent) {
1545 /* when the backend is consistent we can pre-prime some mappings */
1546 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1547 domain->name,
1548 (*info)[i].acct_name,
1549 &(*info)[i].user_sid,
1550 SID_NAME_USER);
1551 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1552 &(*info)[i].user_sid,
1553 domain->name,
1554 (*info)[i].acct_name,
1555 SID_NAME_USER);
1556 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1559 centry_end(centry, "UL/%s", domain->name);
1560 centry_free(centry);
1562 skip_save:
1563 return status;
1566 /* list all domain groups */
1567 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1568 TALLOC_CTX *mem_ctx,
1569 uint32 *num_entries,
1570 struct wb_acct_info **info)
1572 struct winbind_cache *cache = get_cache(domain);
1573 struct cache_entry *centry = NULL;
1574 NTSTATUS status;
1575 unsigned int i;
1576 bool old_status;
1578 old_status = domain->online;
1579 if (!cache->tdb)
1580 goto do_query;
1582 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1583 if (!centry)
1584 goto do_query;
1586 do_fetch_cache:
1587 *num_entries = centry_uint32(centry);
1589 if (*num_entries == 0)
1590 goto do_cached;
1592 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1593 if (! (*info)) {
1594 smb_panic_fn("enum_dom_groups out of memory");
1596 for (i=0; i<(*num_entries); i++) {
1597 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1598 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1599 (*info)[i].rid = centry_uint32(centry);
1602 do_cached:
1603 status = centry->status;
1605 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1606 domain->name, nt_errstr(status) ));
1608 centry_free(centry);
1609 return status;
1611 do_query:
1612 *num_entries = 0;
1613 *info = NULL;
1615 /* Return status value returned by seq number check */
1617 if (!NT_STATUS_IS_OK(domain->last_status))
1618 return domain->last_status;
1620 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1621 domain->name ));
1623 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1625 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1626 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1627 if (!domain->internal && old_status) {
1628 set_domain_offline(domain);
1630 if (cache->tdb &&
1631 !domain->online &&
1632 !domain->internal &&
1633 old_status) {
1634 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1635 if (centry) {
1636 goto do_fetch_cache;
1640 /* and save it */
1641 refresh_sequence_number(domain, false);
1642 if (!NT_STATUS_IS_OK(status)) {
1643 return status;
1645 centry = centry_start(domain, status);
1646 if (!centry)
1647 goto skip_save;
1648 centry_put_uint32(centry, *num_entries);
1649 for (i=0; i<(*num_entries); i++) {
1650 centry_put_string(centry, (*info)[i].acct_name);
1651 centry_put_string(centry, (*info)[i].acct_desc);
1652 centry_put_uint32(centry, (*info)[i].rid);
1654 centry_end(centry, "GL/%s/domain", domain->name);
1655 centry_free(centry);
1657 skip_save:
1658 return status;
1661 /* list all domain groups */
1662 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1663 TALLOC_CTX *mem_ctx,
1664 uint32 *num_entries,
1665 struct wb_acct_info **info)
1667 struct winbind_cache *cache = get_cache(domain);
1668 struct cache_entry *centry = NULL;
1669 NTSTATUS status;
1670 unsigned int i;
1671 bool old_status;
1673 old_status = domain->online;
1674 if (!cache->tdb)
1675 goto do_query;
1677 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1678 if (!centry)
1679 goto do_query;
1681 do_fetch_cache:
1682 *num_entries = centry_uint32(centry);
1684 if (*num_entries == 0)
1685 goto do_cached;
1687 (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1688 if (! (*info)) {
1689 smb_panic_fn("enum_dom_groups out of memory");
1691 for (i=0; i<(*num_entries); i++) {
1692 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1693 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1694 (*info)[i].rid = centry_uint32(centry);
1697 do_cached:
1699 /* If we are returning cached data and the domain controller
1700 is down then we don't know whether the data is up to date
1701 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1702 indicate this. */
1704 if (wcache_server_down(domain)) {
1705 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1706 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1707 } else
1708 status = centry->status;
1710 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1711 domain->name, nt_errstr(status) ));
1713 centry_free(centry);
1714 return status;
1716 do_query:
1717 *num_entries = 0;
1718 *info = NULL;
1720 /* Return status value returned by seq number check */
1722 if (!NT_STATUS_IS_OK(domain->last_status))
1723 return domain->last_status;
1725 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1726 domain->name ));
1728 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1730 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1731 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1732 if (!domain->internal && old_status) {
1733 set_domain_offline(domain);
1735 if (cache->tdb &&
1736 !domain->internal &&
1737 !domain->online &&
1738 old_status) {
1739 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1740 if (centry) {
1741 goto do_fetch_cache;
1745 /* and save it */
1746 refresh_sequence_number(domain, false);
1747 if (!NT_STATUS_IS_OK(status)) {
1748 return status;
1750 centry = centry_start(domain, status);
1751 if (!centry)
1752 goto skip_save;
1753 centry_put_uint32(centry, *num_entries);
1754 for (i=0; i<(*num_entries); i++) {
1755 centry_put_string(centry, (*info)[i].acct_name);
1756 centry_put_string(centry, (*info)[i].acct_desc);
1757 centry_put_uint32(centry, (*info)[i].rid);
1759 centry_end(centry, "GL/%s/local", domain->name);
1760 centry_free(centry);
1762 skip_save:
1763 return status;
1766 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1767 const char *domain_name,
1768 const char *name,
1769 struct dom_sid *sid,
1770 enum lsa_SidType *type)
1772 struct winbind_cache *cache = get_cache(domain);
1773 struct cache_entry *centry;
1774 NTSTATUS status;
1775 char *uname;
1777 if (cache->tdb == NULL) {
1778 return NT_STATUS_NOT_FOUND;
1781 uname = talloc_strdup_upper(talloc_tos(), name);
1782 if (uname == NULL) {
1783 return NT_STATUS_NO_MEMORY;
1786 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1787 TALLOC_FREE(uname);
1788 if (centry == NULL) {
1789 return NT_STATUS_NOT_FOUND;
1792 status = centry->status;
1793 if (NT_STATUS_IS_OK(status)) {
1794 *type = (enum lsa_SidType)centry_uint32(centry);
1795 centry_sid(centry, sid);
1798 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1799 "%s\n", domain->name, nt_errstr(status) ));
1801 centry_free(centry);
1802 return status;
1805 /* convert a single name to a sid in a domain */
1806 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1807 TALLOC_CTX *mem_ctx,
1808 const char *domain_name,
1809 const char *name,
1810 uint32_t flags,
1811 struct dom_sid *sid,
1812 enum lsa_SidType *type)
1814 NTSTATUS status;
1815 bool old_status;
1817 old_status = domain->online;
1819 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1820 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1821 return status;
1824 ZERO_STRUCTP(sid);
1826 /* If the seq number check indicated that there is a problem
1827 * with this DC, then return that status... except for
1828 * access_denied. This is special because the dc may be in
1829 * "restrict anonymous = 1" mode, in which case it will deny
1830 * most unauthenticated operations, but *will* allow the LSA
1831 * name-to-sid that we try as a fallback. */
1833 if (!(NT_STATUS_IS_OK(domain->last_status)
1834 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1835 return domain->last_status;
1837 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1838 domain->name ));
1840 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1841 name, flags, sid, type);
1843 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1844 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1845 if (!domain->internal && old_status) {
1846 set_domain_offline(domain);
1848 if (!domain->internal &&
1849 !domain->online &&
1850 old_status) {
1851 NTSTATUS cache_status;
1852 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1853 return cache_status;
1856 /* and save it */
1857 refresh_sequence_number(domain, false);
1859 if (domain->online &&
1860 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1861 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1863 /* Only save the reverse mapping if this was not a UPN */
1864 if (!strchr(name, '@')) {
1865 strupper_m(discard_const_p(char, domain_name));
1866 strlower_m(discard_const_p(char, name));
1867 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1871 return status;
1874 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1875 const struct dom_sid *sid,
1876 TALLOC_CTX *mem_ctx,
1877 char **domain_name,
1878 char **name,
1879 enum lsa_SidType *type)
1881 struct winbind_cache *cache = get_cache(domain);
1882 struct cache_entry *centry;
1883 char *sid_string;
1884 NTSTATUS status;
1886 if (cache->tdb == NULL) {
1887 return NT_STATUS_NOT_FOUND;
1890 sid_string = sid_string_tos(sid);
1891 if (sid_string == NULL) {
1892 return NT_STATUS_NO_MEMORY;
1895 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1896 TALLOC_FREE(sid_string);
1897 if (centry == NULL) {
1898 return NT_STATUS_NOT_FOUND;
1901 if (NT_STATUS_IS_OK(centry->status)) {
1902 *type = (enum lsa_SidType)centry_uint32(centry);
1903 *domain_name = centry_string(centry, mem_ctx);
1904 *name = centry_string(centry, mem_ctx);
1907 status = centry->status;
1908 centry_free(centry);
1910 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1911 "%s\n", domain->name, nt_errstr(status) ));
1913 return status;
1916 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1917 given */
1918 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1919 TALLOC_CTX *mem_ctx,
1920 const struct dom_sid *sid,
1921 char **domain_name,
1922 char **name,
1923 enum lsa_SidType *type)
1925 NTSTATUS status;
1926 bool old_status;
1928 old_status = domain->online;
1929 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1930 type);
1931 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1932 return status;
1935 *name = NULL;
1936 *domain_name = NULL;
1938 /* If the seq number check indicated that there is a problem
1939 * with this DC, then return that status... except for
1940 * access_denied. This is special because the dc may be in
1941 * "restrict anonymous = 1" mode, in which case it will deny
1942 * most unauthenticated operations, but *will* allow the LSA
1943 * sid-to-name that we try as a fallback. */
1945 if (!(NT_STATUS_IS_OK(domain->last_status)
1946 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1947 return domain->last_status;
1949 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1950 domain->name ));
1952 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1954 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1955 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1956 if (!domain->internal && old_status) {
1957 set_domain_offline(domain);
1959 if (!domain->internal &&
1960 !domain->online &&
1961 old_status) {
1962 NTSTATUS cache_status;
1963 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1964 domain_name, name, type);
1965 return cache_status;
1968 /* and save it */
1969 refresh_sequence_number(domain, false);
1970 if (!NT_STATUS_IS_OK(status)) {
1971 return status;
1973 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1975 /* We can't save the name to sid mapping here, as with sid history a
1976 * later name2sid would give the wrong sid. */
1978 return status;
1981 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1982 TALLOC_CTX *mem_ctx,
1983 const struct dom_sid *domain_sid,
1984 uint32 *rids,
1985 size_t num_rids,
1986 char **domain_name,
1987 char ***names,
1988 enum lsa_SidType **types)
1990 struct winbind_cache *cache = get_cache(domain);
1991 size_t i;
1992 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1993 bool have_mapped;
1994 bool have_unmapped;
1995 bool old_status;
1997 old_status = domain->online;
1998 *domain_name = NULL;
1999 *names = NULL;
2000 *types = NULL;
2002 if (!cache->tdb) {
2003 goto do_query;
2006 if (num_rids == 0) {
2007 return NT_STATUS_OK;
2010 *names = talloc_array(mem_ctx, char *, num_rids);
2011 *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2013 if ((*names == NULL) || (*types == NULL)) {
2014 result = NT_STATUS_NO_MEMORY;
2015 goto error;
2018 have_mapped = have_unmapped = false;
2020 for (i=0; i<num_rids; i++) {
2021 struct dom_sid sid;
2022 struct cache_entry *centry;
2023 fstring tmp;
2025 if (!sid_compose(&sid, domain_sid, rids[i])) {
2026 result = NT_STATUS_INTERNAL_ERROR;
2027 goto error;
2030 centry = wcache_fetch(cache, domain, "SN/%s",
2031 sid_to_fstring(tmp, &sid));
2032 if (!centry) {
2033 goto do_query;
2036 (*types)[i] = SID_NAME_UNKNOWN;
2037 (*names)[i] = talloc_strdup(*names, "");
2039 if (NT_STATUS_IS_OK(centry->status)) {
2040 char *dom;
2041 have_mapped = true;
2042 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2044 dom = centry_string(centry, mem_ctx);
2045 if (*domain_name == NULL) {
2046 *domain_name = dom;
2047 } else {
2048 talloc_free(dom);
2051 (*names)[i] = centry_string(centry, *names);
2053 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2054 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2055 have_unmapped = true;
2057 } else {
2058 /* something's definitely wrong */
2059 result = centry->status;
2060 goto error;
2063 centry_free(centry);
2066 if (!have_mapped) {
2067 return NT_STATUS_NONE_MAPPED;
2069 if (!have_unmapped) {
2070 return NT_STATUS_OK;
2072 return STATUS_SOME_UNMAPPED;
2074 do_query:
2076 TALLOC_FREE(*names);
2077 TALLOC_FREE(*types);
2079 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2080 rids, num_rids, domain_name,
2081 names, types);
2083 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2084 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2085 if (!domain->internal && old_status) {
2086 set_domain_offline(domain);
2088 if (cache->tdb &&
2089 !domain->internal &&
2090 !domain->online &&
2091 old_status) {
2092 have_mapped = have_unmapped = false;
2094 for (i=0; i<num_rids; i++) {
2095 struct dom_sid sid;
2096 struct cache_entry *centry;
2097 fstring tmp;
2099 if (!sid_compose(&sid, domain_sid, rids[i])) {
2100 result = NT_STATUS_INTERNAL_ERROR;
2101 goto error;
2104 centry = wcache_fetch(cache, domain, "SN/%s",
2105 sid_to_fstring(tmp, &sid));
2106 if (!centry) {
2107 (*types)[i] = SID_NAME_UNKNOWN;
2108 (*names)[i] = talloc_strdup(*names, "");
2109 continue;
2112 (*types)[i] = SID_NAME_UNKNOWN;
2113 (*names)[i] = talloc_strdup(*names, "");
2115 if (NT_STATUS_IS_OK(centry->status)) {
2116 char *dom;
2117 have_mapped = true;
2118 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2120 dom = centry_string(centry, mem_ctx);
2121 if (*domain_name == NULL) {
2122 *domain_name = dom;
2123 } else {
2124 talloc_free(dom);
2127 (*names)[i] = centry_string(centry, *names);
2129 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2130 have_unmapped = true;
2132 } else {
2133 /* something's definitely wrong */
2134 result = centry->status;
2135 goto error;
2138 centry_free(centry);
2141 if (!have_mapped) {
2142 return NT_STATUS_NONE_MAPPED;
2144 if (!have_unmapped) {
2145 return NT_STATUS_OK;
2147 return STATUS_SOME_UNMAPPED;
2151 None of the queried rids has been found so save all negative entries
2153 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2154 for (i = 0; i < num_rids; i++) {
2155 struct dom_sid sid;
2156 const char *name = "";
2157 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2158 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2160 if (!sid_compose(&sid, domain_sid, rids[i])) {
2161 return NT_STATUS_INTERNAL_ERROR;
2164 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2165 name, type);
2168 return result;
2172 Some or all of the queried rids have been found.
2174 if (!NT_STATUS_IS_OK(result) &&
2175 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2176 return result;
2179 refresh_sequence_number(domain, false);
2181 for (i=0; i<num_rids; i++) {
2182 struct dom_sid sid;
2183 NTSTATUS status;
2185 if (!sid_compose(&sid, domain_sid, rids[i])) {
2186 result = NT_STATUS_INTERNAL_ERROR;
2187 goto error;
2190 status = (*types)[i] == SID_NAME_UNKNOWN ?
2191 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2193 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2194 (*names)[i], (*types)[i]);
2197 return result;
2199 error:
2200 TALLOC_FREE(*names);
2201 TALLOC_FREE(*types);
2202 return result;
2205 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2206 TALLOC_CTX *mem_ctx,
2207 const struct dom_sid *user_sid,
2208 struct wbint_userinfo *info)
2210 struct winbind_cache *cache = get_cache(domain);
2211 struct cache_entry *centry = NULL;
2212 NTSTATUS status;
2213 char *sid_string;
2215 if (cache->tdb == NULL) {
2216 return NT_STATUS_NOT_FOUND;
2219 sid_string = sid_string_tos(user_sid);
2220 if (sid_string == NULL) {
2221 return NT_STATUS_NO_MEMORY;
2224 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2225 TALLOC_FREE(sid_string);
2226 if (centry == NULL) {
2227 return NT_STATUS_NOT_FOUND;
2231 * If we have an access denied cache entry and a cached info3
2232 * in the samlogon cache then do a query. This will force the
2233 * rpc back end to return the info3 data.
2236 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2237 netsamlogon_cache_have(user_sid)) {
2238 DEBUG(10, ("query_user: cached access denied and have cached "
2239 "info3\n"));
2240 domain->last_status = NT_STATUS_OK;
2241 centry_free(centry);
2242 return NT_STATUS_NOT_FOUND;
2245 /* if status is not ok then this is a negative hit
2246 and the rest of the data doesn't matter */
2247 status = centry->status;
2248 if (NT_STATUS_IS_OK(status)) {
2249 info->acct_name = centry_string(centry, mem_ctx);
2250 info->full_name = centry_string(centry, mem_ctx);
2251 info->homedir = centry_string(centry, mem_ctx);
2252 info->shell = centry_string(centry, mem_ctx);
2253 info->primary_gid = centry_uint32(centry);
2254 centry_sid(centry, &info->user_sid);
2255 centry_sid(centry, &info->group_sid);
2258 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2259 "%s\n", domain->name, nt_errstr(status) ));
2261 centry_free(centry);
2262 return status;
2265 /* Lookup user information from a rid */
2266 static NTSTATUS query_user(struct winbindd_domain *domain,
2267 TALLOC_CTX *mem_ctx,
2268 const struct dom_sid *user_sid,
2269 struct wbint_userinfo *info)
2271 NTSTATUS status;
2272 bool old_status;
2274 old_status = domain->online;
2275 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2276 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2277 return status;
2280 ZERO_STRUCTP(info);
2282 /* Return status value returned by seq number check */
2284 if (!NT_STATUS_IS_OK(domain->last_status))
2285 return domain->last_status;
2287 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2288 domain->name ));
2290 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2292 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2293 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2294 if (!domain->internal && old_status) {
2295 set_domain_offline(domain);
2297 if (!domain->internal &&
2298 !domain->online &&
2299 old_status) {
2300 NTSTATUS cache_status;
2301 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2302 return cache_status;
2305 /* and save it */
2306 refresh_sequence_number(domain, false);
2307 if (!NT_STATUS_IS_OK(status)) {
2308 return status;
2310 wcache_save_user(domain, status, info);
2312 return status;
2315 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2316 TALLOC_CTX *mem_ctx,
2317 const struct dom_sid *user_sid,
2318 uint32_t *pnum_sids,
2319 struct dom_sid **psids)
2321 struct winbind_cache *cache = get_cache(domain);
2322 struct cache_entry *centry = NULL;
2323 NTSTATUS status;
2324 uint32_t i, num_sids;
2325 struct dom_sid *sids;
2326 fstring sid_string;
2328 if (cache->tdb == NULL) {
2329 return NT_STATUS_NOT_FOUND;
2332 centry = wcache_fetch(cache, domain, "UG/%s",
2333 sid_to_fstring(sid_string, user_sid));
2334 if (centry == NULL) {
2335 return NT_STATUS_NOT_FOUND;
2338 /* If we have an access denied cache entry and a cached info3 in the
2339 samlogon cache then do a query. This will force the rpc back end
2340 to return the info3 data. */
2342 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2343 && netsamlogon_cache_have(user_sid)) {
2344 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2345 "cached info3\n"));
2346 domain->last_status = NT_STATUS_OK;
2347 centry_free(centry);
2348 return NT_STATUS_NOT_FOUND;
2351 num_sids = centry_uint32(centry);
2352 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2353 if (sids == NULL) {
2354 centry_free(centry);
2355 return NT_STATUS_NO_MEMORY;
2358 for (i=0; i<num_sids; i++) {
2359 centry_sid(centry, &sids[i]);
2362 status = centry->status;
2364 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2365 "status: %s\n", domain->name, nt_errstr(status)));
2367 centry_free(centry);
2369 *pnum_sids = num_sids;
2370 *psids = sids;
2371 return status;
2374 /* Lookup groups a user is a member of. */
2375 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2376 TALLOC_CTX *mem_ctx,
2377 const struct dom_sid *user_sid,
2378 uint32 *num_groups, struct dom_sid **user_gids)
2380 struct cache_entry *centry = NULL;
2381 NTSTATUS status;
2382 unsigned int i;
2383 fstring sid_string;
2384 bool old_status;
2386 old_status = domain->online;
2387 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2388 num_groups, user_gids);
2389 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2390 return status;
2393 (*num_groups) = 0;
2394 (*user_gids) = NULL;
2396 /* Return status value returned by seq number check */
2398 if (!NT_STATUS_IS_OK(domain->last_status))
2399 return domain->last_status;
2401 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2402 domain->name ));
2404 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2406 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2407 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2408 if (!domain->internal && old_status) {
2409 set_domain_offline(domain);
2411 if (!domain->internal &&
2412 !domain->online &&
2413 old_status) {
2414 NTSTATUS cache_status;
2415 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2416 num_groups, user_gids);
2417 return cache_status;
2420 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2421 goto skip_save;
2423 /* and save it */
2424 refresh_sequence_number(domain, false);
2425 if (!NT_STATUS_IS_OK(status)) {
2426 return status;
2428 centry = centry_start(domain, status);
2429 if (!centry)
2430 goto skip_save;
2432 centry_put_uint32(centry, *num_groups);
2433 for (i=0; i<(*num_groups); i++) {
2434 centry_put_sid(centry, &(*user_gids)[i]);
2437 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2438 centry_free(centry);
2440 skip_save:
2441 return status;
2444 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2445 const struct dom_sid *sids)
2447 uint32_t i;
2448 char *sidlist;
2450 sidlist = talloc_strdup(mem_ctx, "");
2451 if (sidlist == NULL) {
2452 return NULL;
2454 for (i=0; i<num_sids; i++) {
2455 fstring tmp;
2456 sidlist = talloc_asprintf_append_buffer(
2457 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2458 if (sidlist == NULL) {
2459 return NULL;
2462 return sidlist;
2465 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2466 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2467 const struct dom_sid *sids,
2468 uint32_t *pnum_aliases, uint32_t **paliases)
2470 struct winbind_cache *cache = get_cache(domain);
2471 struct cache_entry *centry = NULL;
2472 uint32_t num_aliases;
2473 uint32_t *aliases;
2474 NTSTATUS status;
2475 char *sidlist;
2476 int i;
2478 if (cache->tdb == NULL) {
2479 return NT_STATUS_NOT_FOUND;
2482 if (num_sids == 0) {
2483 *pnum_aliases = 0;
2484 *paliases = NULL;
2485 return NT_STATUS_OK;
2488 /* We need to cache indexed by the whole list of SIDs, the aliases
2489 * resulting might come from any of the SIDs. */
2491 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2492 if (sidlist == NULL) {
2493 return NT_STATUS_NO_MEMORY;
2496 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2497 TALLOC_FREE(sidlist);
2498 if (centry == NULL) {
2499 return NT_STATUS_NOT_FOUND;
2502 num_aliases = centry_uint32(centry);
2503 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2504 if (aliases == NULL) {
2505 centry_free(centry);
2506 return NT_STATUS_NO_MEMORY;
2509 for (i=0; i<num_aliases; i++) {
2510 aliases[i] = centry_uint32(centry);
2513 status = centry->status;
2515 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2516 "status %s\n", domain->name, nt_errstr(status)));
2518 centry_free(centry);
2520 *pnum_aliases = num_aliases;
2521 *paliases = aliases;
2523 return status;
2526 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2527 TALLOC_CTX *mem_ctx,
2528 uint32 num_sids, const struct dom_sid *sids,
2529 uint32 *num_aliases, uint32 **alias_rids)
2531 struct cache_entry *centry = NULL;
2532 NTSTATUS status;
2533 char *sidlist;
2534 int i;
2535 bool old_status;
2537 old_status = domain->online;
2538 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2539 num_aliases, alias_rids);
2540 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2541 return status;
2544 (*num_aliases) = 0;
2545 (*alias_rids) = NULL;
2547 if (!NT_STATUS_IS_OK(domain->last_status))
2548 return domain->last_status;
2550 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2551 "for domain %s\n", domain->name ));
2553 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2554 if (sidlist == NULL) {
2555 return NT_STATUS_NO_MEMORY;
2558 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2559 num_sids, sids,
2560 num_aliases, alias_rids);
2562 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2563 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2564 if (!domain->internal && old_status) {
2565 set_domain_offline(domain);
2567 if (!domain->internal &&
2568 !domain->online &&
2569 old_status) {
2570 NTSTATUS cache_status;
2571 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2572 sids, num_aliases, alias_rids);
2573 return cache_status;
2576 /* and save it */
2577 refresh_sequence_number(domain, false);
2578 if (!NT_STATUS_IS_OK(status)) {
2579 return status;
2581 centry = centry_start(domain, status);
2582 if (!centry)
2583 goto skip_save;
2584 centry_put_uint32(centry, *num_aliases);
2585 for (i=0; i<(*num_aliases); i++)
2586 centry_put_uint32(centry, (*alias_rids)[i]);
2587 centry_end(centry, "UA%s", sidlist);
2588 centry_free(centry);
2590 skip_save:
2591 return status;
2594 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2595 TALLOC_CTX *mem_ctx,
2596 const struct dom_sid *group_sid,
2597 uint32_t *num_names,
2598 struct dom_sid **sid_mem, char ***names,
2599 uint32_t **name_types)
2601 struct winbind_cache *cache = get_cache(domain);
2602 struct cache_entry *centry = NULL;
2603 NTSTATUS status;
2604 unsigned int i;
2605 char *sid_string;
2607 if (cache->tdb == NULL) {
2608 return NT_STATUS_NOT_FOUND;
2611 sid_string = sid_string_tos(group_sid);
2612 if (sid_string == NULL) {
2613 return NT_STATUS_NO_MEMORY;
2616 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2617 TALLOC_FREE(sid_string);
2618 if (centry == NULL) {
2619 return NT_STATUS_NOT_FOUND;
2622 *sid_mem = NULL;
2623 *names = NULL;
2624 *name_types = NULL;
2626 *num_names = centry_uint32(centry);
2627 if (*num_names == 0) {
2628 centry_free(centry);
2629 return NT_STATUS_OK;
2632 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2633 *names = talloc_array(mem_ctx, char *, *num_names);
2634 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2636 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2637 TALLOC_FREE(*sid_mem);
2638 TALLOC_FREE(*names);
2639 TALLOC_FREE(*name_types);
2640 centry_free(centry);
2641 return NT_STATUS_NO_MEMORY;
2644 for (i=0; i<(*num_names); i++) {
2645 centry_sid(centry, &(*sid_mem)[i]);
2646 (*names)[i] = centry_string(centry, mem_ctx);
2647 (*name_types)[i] = centry_uint32(centry);
2650 status = centry->status;
2652 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2653 "status: %s\n", domain->name, nt_errstr(status)));
2655 centry_free(centry);
2656 return status;
2659 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2660 TALLOC_CTX *mem_ctx,
2661 const struct dom_sid *group_sid,
2662 enum lsa_SidType type,
2663 uint32 *num_names,
2664 struct dom_sid **sid_mem, char ***names,
2665 uint32 **name_types)
2667 struct cache_entry *centry = NULL;
2668 NTSTATUS status;
2669 unsigned int i;
2670 fstring sid_string;
2671 bool old_status;
2673 old_status = domain->online;
2674 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2675 sid_mem, names, name_types);
2676 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2677 return status;
2680 (*num_names) = 0;
2681 (*sid_mem) = NULL;
2682 (*names) = NULL;
2683 (*name_types) = NULL;
2685 /* Return status value returned by seq number check */
2687 if (!NT_STATUS_IS_OK(domain->last_status))
2688 return domain->last_status;
2690 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2691 domain->name ));
2693 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2694 type, num_names,
2695 sid_mem, names, name_types);
2697 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2698 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2699 if (!domain->internal && old_status) {
2700 set_domain_offline(domain);
2702 if (!domain->internal &&
2703 !domain->online &&
2704 old_status) {
2705 NTSTATUS cache_status;
2706 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2707 num_names, sid_mem, names,
2708 name_types);
2709 return cache_status;
2712 /* and save it */
2713 refresh_sequence_number(domain, false);
2714 if (!NT_STATUS_IS_OK(status)) {
2715 return status;
2717 centry = centry_start(domain, status);
2718 if (!centry)
2719 goto skip_save;
2720 centry_put_uint32(centry, *num_names);
2721 for (i=0; i<(*num_names); i++) {
2722 centry_put_sid(centry, &(*sid_mem)[i]);
2723 centry_put_string(centry, (*names)[i]);
2724 centry_put_uint32(centry, (*name_types)[i]);
2726 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2727 centry_free(centry);
2729 skip_save:
2730 return status;
2733 /* find the sequence number for a domain */
2734 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2736 refresh_sequence_number(domain, false);
2738 *seq = domain->sequence_number;
2740 return NT_STATUS_OK;
2743 /* enumerate trusted domains
2744 * (we need to have the list of trustdoms in the cache when we go offline) -
2745 * Guenther */
2746 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2747 TALLOC_CTX *mem_ctx,
2748 struct netr_DomainTrustList *trusts)
2750 NTSTATUS status;
2751 struct winbind_cache *cache;
2752 struct winbindd_tdc_domain *dom_list = NULL;
2753 size_t num_domains = 0;
2754 bool retval = false;
2755 int i;
2756 bool old_status;
2758 old_status = domain->online;
2759 trusts->count = 0;
2760 trusts->array = NULL;
2762 cache = get_cache(domain);
2763 if (!cache || !cache->tdb) {
2764 goto do_query;
2767 if (domain->online) {
2768 goto do_query;
2771 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2772 if (!retval || !num_domains || !dom_list) {
2773 TALLOC_FREE(dom_list);
2774 goto do_query;
2777 do_fetch_cache:
2778 trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2779 if (!trusts->array) {
2780 TALLOC_FREE(dom_list);
2781 return NT_STATUS_NO_MEMORY;
2784 for (i = 0; i < num_domains; i++) {
2785 struct netr_DomainTrust *trust;
2786 struct dom_sid *sid;
2787 struct winbindd_domain *dom;
2789 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2790 if (dom && dom->internal) {
2791 continue;
2794 trust = &trusts->array[trusts->count];
2795 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2796 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2797 sid = talloc(trusts->array, struct dom_sid);
2798 if (!trust->netbios_name || !trust->dns_name ||
2799 !sid) {
2800 TALLOC_FREE(dom_list);
2801 TALLOC_FREE(trusts->array);
2802 return NT_STATUS_NO_MEMORY;
2805 trust->trust_flags = dom_list[i].trust_flags;
2806 trust->trust_attributes = dom_list[i].trust_attribs;
2807 trust->trust_type = dom_list[i].trust_type;
2808 sid_copy(sid, &dom_list[i].sid);
2809 trust->sid = sid;
2810 trusts->count++;
2813 TALLOC_FREE(dom_list);
2814 return NT_STATUS_OK;
2816 do_query:
2817 /* Return status value returned by seq number check */
2819 if (!NT_STATUS_IS_OK(domain->last_status))
2820 return domain->last_status;
2822 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2823 domain->name ));
2825 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2827 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2828 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2829 if (!domain->internal && old_status) {
2830 set_domain_offline(domain);
2832 if (!domain->internal &&
2833 !domain->online &&
2834 old_status) {
2835 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2836 if (retval && num_domains && dom_list) {
2837 TALLOC_FREE(trusts->array);
2838 trusts->count = 0;
2839 goto do_fetch_cache;
2843 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2844 * so that the generic centry handling still applies correctly -
2845 * Guenther*/
2847 if (!NT_STATUS_IS_ERR(status)) {
2848 status = NT_STATUS_OK;
2850 return status;
2853 /* get lockout policy */
2854 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2855 TALLOC_CTX *mem_ctx,
2856 struct samr_DomInfo12 *policy)
2858 struct winbind_cache *cache = get_cache(domain);
2859 struct cache_entry *centry = NULL;
2860 NTSTATUS status;
2861 bool old_status;
2863 old_status = domain->online;
2864 if (!cache->tdb)
2865 goto do_query;
2867 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2869 if (!centry)
2870 goto do_query;
2872 do_fetch_cache:
2873 policy->lockout_duration = centry_nttime(centry);
2874 policy->lockout_window = centry_nttime(centry);
2875 policy->lockout_threshold = centry_uint16(centry);
2877 status = centry->status;
2879 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2880 domain->name, nt_errstr(status) ));
2882 centry_free(centry);
2883 return status;
2885 do_query:
2886 ZERO_STRUCTP(policy);
2888 /* Return status value returned by seq number check */
2890 if (!NT_STATUS_IS_OK(domain->last_status))
2891 return domain->last_status;
2893 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2894 domain->name ));
2896 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2898 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2899 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2900 if (!domain->internal && old_status) {
2901 set_domain_offline(domain);
2903 if (cache->tdb &&
2904 !domain->internal &&
2905 !domain->online &&
2906 old_status) {
2907 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2908 if (centry) {
2909 goto do_fetch_cache;
2913 /* and save it */
2914 refresh_sequence_number(domain, false);
2915 if (!NT_STATUS_IS_OK(status)) {
2916 return status;
2918 wcache_save_lockout_policy(domain, status, policy);
2920 return status;
2923 /* get password policy */
2924 static NTSTATUS password_policy(struct winbindd_domain *domain,
2925 TALLOC_CTX *mem_ctx,
2926 struct samr_DomInfo1 *policy)
2928 struct winbind_cache *cache = get_cache(domain);
2929 struct cache_entry *centry = NULL;
2930 NTSTATUS status;
2931 bool old_status;
2933 old_status = domain->online;
2934 if (!cache->tdb)
2935 goto do_query;
2937 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2939 if (!centry)
2940 goto do_query;
2942 do_fetch_cache:
2943 policy->min_password_length = centry_uint16(centry);
2944 policy->password_history_length = centry_uint16(centry);
2945 policy->password_properties = centry_uint32(centry);
2946 policy->max_password_age = centry_nttime(centry);
2947 policy->min_password_age = centry_nttime(centry);
2949 status = centry->status;
2951 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2952 domain->name, nt_errstr(status) ));
2954 centry_free(centry);
2955 return status;
2957 do_query:
2958 ZERO_STRUCTP(policy);
2960 /* Return status value returned by seq number check */
2962 if (!NT_STATUS_IS_OK(domain->last_status))
2963 return domain->last_status;
2965 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2966 domain->name ));
2968 status = domain->backend->password_policy(domain, mem_ctx, policy);
2970 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2971 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2972 if (!domain->internal && old_status) {
2973 set_domain_offline(domain);
2975 if (cache->tdb &&
2976 !domain->internal &&
2977 !domain->online &&
2978 old_status) {
2979 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2980 if (centry) {
2981 goto do_fetch_cache;
2985 /* and save it */
2986 refresh_sequence_number(domain, false);
2987 if (!NT_STATUS_IS_OK(status)) {
2988 return status;
2990 wcache_save_password_policy(domain, status, policy);
2992 return status;
2996 /* Invalidate cached user and group lists coherently */
2998 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2999 void *state)
3001 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3002 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3003 tdb_delete(the_tdb, kbuf);
3005 return 0;
3008 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3010 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3011 const struct dom_sid *sid)
3013 fstring key_str, sid_string;
3014 struct winbind_cache *cache;
3016 /* dont clear cached U/SID and UG/SID entries when we want to logon
3017 * offline - gd */
3019 if (lp_winbind_offline_logon()) {
3020 return;
3023 if (!domain)
3024 return;
3026 cache = get_cache(domain);
3028 if (!cache->tdb) {
3029 return;
3032 /* Clear U/SID cache entry */
3033 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3034 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3035 tdb_delete(cache->tdb, string_tdb_data(key_str));
3037 /* Clear UG/SID cache entry */
3038 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3039 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3040 tdb_delete(cache->tdb, string_tdb_data(key_str));
3042 /* Samba/winbindd never needs this. */
3043 netsamlogon_clear_cached_user(sid);
3046 bool wcache_invalidate_cache(void)
3048 struct winbindd_domain *domain;
3050 for (domain = domain_list(); domain; domain = domain->next) {
3051 struct winbind_cache *cache = get_cache(domain);
3053 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3054 "entries for %s\n", domain->name));
3055 if (cache) {
3056 if (cache->tdb) {
3057 tdb_traverse(cache->tdb, traverse_fn, NULL);
3058 } else {
3059 return false;
3063 return true;
3066 bool wcache_invalidate_cache_noinit(void)
3068 struct winbindd_domain *domain;
3070 for (domain = domain_list(); domain; domain = domain->next) {
3071 struct winbind_cache *cache;
3073 /* Skip uninitialized domains. */
3074 if (!domain->initialized && !domain->internal) {
3075 continue;
3078 cache = get_cache(domain);
3080 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3081 "entries for %s\n", domain->name));
3082 if (cache) {
3083 if (cache->tdb) {
3084 tdb_traverse(cache->tdb, traverse_fn, NULL);
3086 * Flushing cache has nothing to with domains.
3087 * return here if we successfully flushed once.
3088 * To avoid unnecessary traversing the cache.
3090 return true;
3091 } else {
3092 return false;
3096 return true;
3099 bool init_wcache(void)
3101 if (wcache == NULL) {
3102 wcache = SMB_XMALLOC_P(struct winbind_cache);
3103 ZERO_STRUCTP(wcache);
3106 if (wcache->tdb != NULL)
3107 return true;
3109 /* when working offline we must not clear the cache on restart */
3110 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3111 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3112 TDB_INCOMPATIBLE_HASH |
3113 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3114 O_RDWR|O_CREAT, 0600);
3116 if (wcache->tdb == NULL) {
3117 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3118 return false;
3121 return true;
3124 /************************************************************************
3125 This is called by the parent to initialize the cache file.
3126 We don't need sophisticated locking here as we know we're the
3127 only opener.
3128 ************************************************************************/
3130 bool initialize_winbindd_cache(void)
3132 bool cache_bad = true;
3133 uint32 vers;
3135 if (!init_wcache()) {
3136 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3137 return false;
3140 /* Check version number. */
3141 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3142 vers == WINBINDD_CACHE_VERSION) {
3143 cache_bad = false;
3146 if (cache_bad) {
3147 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3148 "and re-creating with version number %d\n",
3149 WINBINDD_CACHE_VERSION ));
3151 tdb_close(wcache->tdb);
3152 wcache->tdb = NULL;
3154 if (unlink(state_path("winbindd_cache.tdb")) == -1) {
3155 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3156 state_path("winbindd_cache.tdb"),
3157 strerror(errno) ));
3158 return false;
3160 if (!init_wcache()) {
3161 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3162 "init_wcache failed.\n"));
3163 return false;
3166 /* Write the version. */
3167 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3168 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3169 tdb_errorstr_compat(wcache->tdb) ));
3170 return false;
3174 tdb_close(wcache->tdb);
3175 wcache->tdb = NULL;
3176 return true;
3179 void close_winbindd_cache(void)
3181 if (!wcache) {
3182 return;
3184 if (wcache->tdb) {
3185 tdb_close(wcache->tdb);
3186 wcache->tdb = NULL;
3190 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3191 char **domain_name, char **name,
3192 enum lsa_SidType *type)
3194 struct winbindd_domain *domain;
3195 NTSTATUS status;
3197 domain = find_lookup_domain_from_sid(sid);
3198 if (domain == NULL) {
3199 return false;
3201 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3202 type);
3203 return NT_STATUS_IS_OK(status);
3206 bool lookup_cached_name(const char *domain_name,
3207 const char *name,
3208 struct dom_sid *sid,
3209 enum lsa_SidType *type)
3211 struct winbindd_domain *domain;
3212 NTSTATUS status;
3213 bool original_online_state;
3215 domain = find_lookup_domain_from_name(domain_name);
3216 if (domain == NULL) {
3217 return false;
3220 /* If we are doing a cached logon, temporarily set the domain
3221 offline so the cache won't expire the entry */
3223 original_online_state = domain->online;
3224 domain->online = false;
3225 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3226 domain->online = original_online_state;
3228 return NT_STATUS_IS_OK(status);
3231 void cache_name2sid(struct winbindd_domain *domain,
3232 const char *domain_name, const char *name,
3233 enum lsa_SidType type, const struct dom_sid *sid)
3235 refresh_sequence_number(domain, false);
3236 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3237 sid, type);
3241 * The original idea that this cache only contains centries has
3242 * been blurred - now other stuff gets put in here. Ensure we
3243 * ignore these things on cleanup.
3246 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3247 TDB_DATA dbuf, void *state)
3249 struct cache_entry *centry;
3251 if (is_non_centry_key(kbuf)) {
3252 return 0;
3255 centry = wcache_fetch_raw((char *)kbuf.dptr);
3256 if (!centry) {
3257 return 0;
3260 if (!NT_STATUS_IS_OK(centry->status)) {
3261 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3262 tdb_delete(the_tdb, kbuf);
3265 centry_free(centry);
3266 return 0;
3269 /* flush the cache */
3270 void wcache_flush_cache(void)
3272 if (!wcache)
3273 return;
3274 if (wcache->tdb) {
3275 tdb_close(wcache->tdb);
3276 wcache->tdb = NULL;
3278 if (!winbindd_use_cache()) {
3279 return;
3282 /* when working offline we must not clear the cache on restart */
3283 wcache->tdb = tdb_open_log(state_path("winbindd_cache.tdb"),
3284 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3285 TDB_INCOMPATIBLE_HASH |
3286 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3287 O_RDWR|O_CREAT, 0600);
3289 if (!wcache->tdb) {
3290 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3291 return;
3294 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3296 DEBUG(10,("wcache_flush_cache success\n"));
3299 /* Count cached creds */
3301 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3302 void *state)
3304 int *cred_count = (int*)state;
3306 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3307 (*cred_count)++;
3309 return 0;
3312 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3314 struct winbind_cache *cache = get_cache(domain);
3316 *count = 0;
3318 if (!cache->tdb) {
3319 return NT_STATUS_INTERNAL_DB_ERROR;
3322 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3324 return NT_STATUS_OK;
3327 struct cred_list {
3328 struct cred_list *prev, *next;
3329 TDB_DATA key;
3330 fstring name;
3331 time_t created;
3333 static struct cred_list *wcache_cred_list;
3335 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3336 void *state)
3338 struct cred_list *cred;
3340 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3342 cred = SMB_MALLOC_P(struct cred_list);
3343 if (cred == NULL) {
3344 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3345 return -1;
3348 ZERO_STRUCTP(cred);
3350 /* save a copy of the key */
3352 fstrcpy(cred->name, (const char *)kbuf.dptr);
3353 DLIST_ADD(wcache_cred_list, cred);
3356 return 0;
3359 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3361 struct winbind_cache *cache = get_cache(domain);
3362 NTSTATUS status;
3363 int ret;
3364 struct cred_list *cred, *oldest = NULL;
3366 if (!cache->tdb) {
3367 return NT_STATUS_INTERNAL_DB_ERROR;
3370 /* we possibly already have an entry */
3371 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3373 fstring key_str, tmp;
3375 DEBUG(11,("we already have an entry, deleting that\n"));
3377 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3379 tdb_delete(cache->tdb, string_tdb_data(key_str));
3381 return NT_STATUS_OK;
3384 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3385 if (ret == 0) {
3386 return NT_STATUS_OK;
3387 } else if ((ret < 0) || (wcache_cred_list == NULL)) {
3388 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3391 ZERO_STRUCTP(oldest);
3393 for (cred = wcache_cred_list; cred; cred = cred->next) {
3395 TDB_DATA data;
3396 time_t t;
3398 data = tdb_fetch_compat(cache->tdb, string_tdb_data(cred->name));
3399 if (!data.dptr) {
3400 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3401 cred->name));
3402 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3403 goto done;
3406 t = IVAL(data.dptr, 0);
3407 SAFE_FREE(data.dptr);
3409 if (!oldest) {
3410 oldest = SMB_MALLOC_P(struct cred_list);
3411 if (oldest == NULL) {
3412 status = NT_STATUS_NO_MEMORY;
3413 goto done;
3416 fstrcpy(oldest->name, cred->name);
3417 oldest->created = t;
3418 continue;
3421 if (t < oldest->created) {
3422 fstrcpy(oldest->name, cred->name);
3423 oldest->created = t;
3427 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3428 status = NT_STATUS_OK;
3429 } else {
3430 status = NT_STATUS_UNSUCCESSFUL;
3432 done:
3433 SAFE_FREE(wcache_cred_list);
3434 SAFE_FREE(oldest);
3436 return status;
3439 /* Change the global online/offline state. */
3440 bool set_global_winbindd_state_offline(void)
3442 TDB_DATA data;
3444 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3446 /* Only go offline if someone has created
3447 the key "WINBINDD_OFFLINE" in the cache tdb. */
3449 if (wcache == NULL || wcache->tdb == NULL) {
3450 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3451 return false;
3454 if (!lp_winbind_offline_logon()) {
3455 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3456 return false;
3459 if (global_winbindd_offline_state) {
3460 /* Already offline. */
3461 return true;
3464 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3466 if (!data.dptr || data.dsize != 4) {
3467 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3468 SAFE_FREE(data.dptr);
3469 return false;
3470 } else {
3471 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3472 global_winbindd_offline_state = true;
3473 SAFE_FREE(data.dptr);
3474 return true;
3478 void set_global_winbindd_state_online(void)
3480 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3482 if (!lp_winbind_offline_logon()) {
3483 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3484 return;
3487 if (!global_winbindd_offline_state) {
3488 /* Already online. */
3489 return;
3491 global_winbindd_offline_state = false;
3493 if (!wcache->tdb) {
3494 return;
3497 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3498 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3501 bool get_global_winbindd_state_offline(void)
3503 return global_winbindd_offline_state;
3506 /***********************************************************************
3507 Validate functions for all possible cache tdb keys.
3508 ***********************************************************************/
3510 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3511 struct tdb_validation_status *state)
3513 struct cache_entry *centry;
3515 centry = SMB_XMALLOC_P(struct cache_entry);
3516 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3517 if (!centry->data) {
3518 SAFE_FREE(centry);
3519 return NULL;
3521 centry->len = data.dsize;
3522 centry->ofs = 0;
3524 if (centry->len < 16) {
3525 /* huh? corrupt cache? */
3526 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3527 "(len < 16) ?\n", kstr));
3528 centry_free(centry);
3529 state->bad_entry = true;
3530 state->success = false;
3531 return NULL;
3534 centry->status = NT_STATUS(centry_uint32(centry));
3535 centry->sequence_number = centry_uint32(centry);
3536 centry->timeout = centry_uint64_t(centry);
3537 return centry;
3540 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3541 struct tdb_validation_status *state)
3543 if (dbuf.dsize != 8) {
3544 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3545 keystr, (unsigned int)dbuf.dsize ));
3546 state->bad_entry = true;
3547 return 1;
3549 return 0;
3552 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3553 struct tdb_validation_status *state)
3555 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3556 if (!centry) {
3557 return 1;
3560 (void)centry_uint32(centry);
3561 if (NT_STATUS_IS_OK(centry->status)) {
3562 struct dom_sid sid;
3563 (void)centry_sid(centry, &sid);
3566 centry_free(centry);
3568 if (!(state->success)) {
3569 return 1;
3571 DEBUG(10,("validate_ns: %s ok\n", keystr));
3572 return 0;
3575 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3576 struct tdb_validation_status *state)
3578 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3579 if (!centry) {
3580 return 1;
3583 if (NT_STATUS_IS_OK(centry->status)) {
3584 (void)centry_uint32(centry);
3585 (void)centry_string(centry, mem_ctx);
3586 (void)centry_string(centry, mem_ctx);
3589 centry_free(centry);
3591 if (!(state->success)) {
3592 return 1;
3594 DEBUG(10,("validate_sn: %s ok\n", keystr));
3595 return 0;
3598 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3599 struct tdb_validation_status *state)
3601 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3602 struct dom_sid sid;
3604 if (!centry) {
3605 return 1;
3608 (void)centry_string(centry, mem_ctx);
3609 (void)centry_string(centry, mem_ctx);
3610 (void)centry_string(centry, mem_ctx);
3611 (void)centry_string(centry, mem_ctx);
3612 (void)centry_uint32(centry);
3613 (void)centry_sid(centry, &sid);
3614 (void)centry_sid(centry, &sid);
3616 centry_free(centry);
3618 if (!(state->success)) {
3619 return 1;
3621 DEBUG(10,("validate_u: %s ok\n", keystr));
3622 return 0;
3625 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3626 struct tdb_validation_status *state)
3628 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3630 if (!centry) {
3631 return 1;
3634 (void)centry_nttime(centry);
3635 (void)centry_nttime(centry);
3636 (void)centry_uint16(centry);
3638 centry_free(centry);
3640 if (!(state->success)) {
3641 return 1;
3643 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3644 return 0;
3647 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3648 struct tdb_validation_status *state)
3650 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3652 if (!centry) {
3653 return 1;
3656 (void)centry_uint16(centry);
3657 (void)centry_uint16(centry);
3658 (void)centry_uint32(centry);
3659 (void)centry_nttime(centry);
3660 (void)centry_nttime(centry);
3662 centry_free(centry);
3664 if (!(state->success)) {
3665 return 1;
3667 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3668 return 0;
3671 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3672 struct tdb_validation_status *state)
3674 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3676 if (!centry) {
3677 return 1;
3680 (void)centry_time(centry);
3681 (void)centry_hash16(centry, mem_ctx);
3683 /* We only have 17 bytes more data in the salted cred case. */
3684 if (centry->len - centry->ofs == 17) {
3685 (void)centry_hash16(centry, mem_ctx);
3688 centry_free(centry);
3690 if (!(state->success)) {
3691 return 1;
3693 DEBUG(10,("validate_cred: %s ok\n", keystr));
3694 return 0;
3697 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3698 struct tdb_validation_status *state)
3700 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3701 int32 num_entries, i;
3703 if (!centry) {
3704 return 1;
3707 num_entries = (int32)centry_uint32(centry);
3709 for (i=0; i< num_entries; i++) {
3710 struct dom_sid sid;
3711 (void)centry_string(centry, mem_ctx);
3712 (void)centry_string(centry, mem_ctx);
3713 (void)centry_string(centry, mem_ctx);
3714 (void)centry_string(centry, mem_ctx);
3715 (void)centry_sid(centry, &sid);
3716 (void)centry_sid(centry, &sid);
3719 centry_free(centry);
3721 if (!(state->success)) {
3722 return 1;
3724 DEBUG(10,("validate_ul: %s ok\n", keystr));
3725 return 0;
3728 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3729 struct tdb_validation_status *state)
3731 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3732 int32 num_entries, i;
3734 if (!centry) {
3735 return 1;
3738 num_entries = centry_uint32(centry);
3740 for (i=0; i< num_entries; i++) {
3741 (void)centry_string(centry, mem_ctx);
3742 (void)centry_string(centry, mem_ctx);
3743 (void)centry_uint32(centry);
3746 centry_free(centry);
3748 if (!(state->success)) {
3749 return 1;
3751 DEBUG(10,("validate_gl: %s ok\n", keystr));
3752 return 0;
3755 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3756 struct tdb_validation_status *state)
3758 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3759 int32 num_groups, i;
3761 if (!centry) {
3762 return 1;
3765 num_groups = centry_uint32(centry);
3767 for (i=0; i< num_groups; i++) {
3768 struct dom_sid sid;
3769 centry_sid(centry, &sid);
3772 centry_free(centry);
3774 if (!(state->success)) {
3775 return 1;
3777 DEBUG(10,("validate_ug: %s ok\n", keystr));
3778 return 0;
3781 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3782 struct tdb_validation_status *state)
3784 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3785 int32 num_aliases, i;
3787 if (!centry) {
3788 return 1;
3791 num_aliases = centry_uint32(centry);
3793 for (i=0; i < num_aliases; i++) {
3794 (void)centry_uint32(centry);
3797 centry_free(centry);
3799 if (!(state->success)) {
3800 return 1;
3802 DEBUG(10,("validate_ua: %s ok\n", keystr));
3803 return 0;
3806 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3807 struct tdb_validation_status *state)
3809 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3810 int32 num_names, i;
3812 if (!centry) {
3813 return 1;
3816 num_names = centry_uint32(centry);
3818 for (i=0; i< num_names; i++) {
3819 struct dom_sid sid;
3820 centry_sid(centry, &sid);
3821 (void)centry_string(centry, mem_ctx);
3822 (void)centry_uint32(centry);
3825 centry_free(centry);
3827 if (!(state->success)) {
3828 return 1;
3830 DEBUG(10,("validate_gm: %s ok\n", keystr));
3831 return 0;
3834 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3835 struct tdb_validation_status *state)
3837 /* Can't say anything about this other than must be nonzero. */
3838 if (dbuf.dsize == 0) {
3839 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3840 keystr));
3841 state->bad_entry = true;
3842 state->success = false;
3843 return 1;
3846 DEBUG(10,("validate_dr: %s ok\n", keystr));
3847 return 0;
3850 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3851 struct tdb_validation_status *state)
3853 /* Can't say anything about this other than must be nonzero. */
3854 if (dbuf.dsize == 0) {
3855 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3856 keystr));
3857 state->bad_entry = true;
3858 state->success = false;
3859 return 1;
3862 DEBUG(10,("validate_de: %s ok\n", keystr));
3863 return 0;
3866 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3867 TDB_DATA dbuf, struct tdb_validation_status *state)
3869 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3871 if (!centry) {
3872 return 1;
3875 (void)centry_string(centry, mem_ctx);
3876 (void)centry_string(centry, mem_ctx);
3877 (void)centry_string(centry, mem_ctx);
3878 (void)centry_uint32(centry);
3880 centry_free(centry);
3882 if (!(state->success)) {
3883 return 1;
3885 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3886 return 0;
3889 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3890 TDB_DATA dbuf,
3891 struct tdb_validation_status *state)
3893 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3895 if (!centry) {
3896 return 1;
3899 (void)centry_string( centry, mem_ctx );
3901 centry_free(centry);
3903 if (!(state->success)) {
3904 return 1;
3906 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3907 return 0;
3910 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3911 TDB_DATA dbuf,
3912 struct tdb_validation_status *state)
3914 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3916 if (!centry) {
3917 return 1;
3920 (void)centry_string( centry, mem_ctx );
3922 centry_free(centry);
3924 if (!(state->success)) {
3925 return 1;
3927 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3928 return 0;
3931 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3932 TDB_DATA dbuf,
3933 struct tdb_validation_status *state)
3935 if (dbuf.dsize == 0) {
3936 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3937 "key %s (len ==0) ?\n", keystr));
3938 state->bad_entry = true;
3939 state->success = false;
3940 return 1;
3943 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3944 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3945 return 0;
3948 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3949 struct tdb_validation_status *state)
3951 if (dbuf.dsize != 4) {
3952 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3953 keystr, (unsigned int)dbuf.dsize ));
3954 state->bad_entry = true;
3955 state->success = false;
3956 return 1;
3958 DEBUG(10,("validate_offline: %s ok\n", keystr));
3959 return 0;
3962 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3963 struct tdb_validation_status *state)
3966 * Ignore validation for now. The proper way to do this is with a
3967 * checksum. Just pure parsing does not really catch much.
3969 return 0;
3972 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3973 struct tdb_validation_status *state)
3975 if (dbuf.dsize != 4) {
3976 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3977 "key %s (len %u != 4) ?\n",
3978 keystr, (unsigned int)dbuf.dsize));
3979 state->bad_entry = true;
3980 state->success = false;
3981 return 1;
3984 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3985 return 0;
3988 /***********************************************************************
3989 A list of all possible cache tdb keys with associated validation
3990 functions.
3991 ***********************************************************************/
3993 struct key_val_struct {
3994 const char *keyname;
3995 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3996 } key_val[] = {
3997 {"SEQNUM/", validate_seqnum},
3998 {"NS/", validate_ns},
3999 {"SN/", validate_sn},
4000 {"U/", validate_u},
4001 {"LOC_POL/", validate_loc_pol},
4002 {"PWD_POL/", validate_pwd_pol},
4003 {"CRED/", validate_cred},
4004 {"UL/", validate_ul},
4005 {"GL/", validate_gl},
4006 {"UG/", validate_ug},
4007 {"UA", validate_ua},
4008 {"GM/", validate_gm},
4009 {"DR/", validate_dr},
4010 {"DE/", validate_de},
4011 {"NSS/PWINFO/", validate_pwinfo},
4012 {"TRUSTDOMCACHE/", validate_trustdomcache},
4013 {"NSS/NA/", validate_nss_na},
4014 {"NSS/AN/", validate_nss_an},
4015 {"WINBINDD_OFFLINE", validate_offline},
4016 {"NDR/", validate_ndr},
4017 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4018 {NULL, NULL}
4021 /***********************************************************************
4022 Function to look at every entry in the tdb and validate it as far as
4023 possible.
4024 ***********************************************************************/
4026 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4028 int i;
4029 unsigned int max_key_len = 1024;
4030 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4032 /* Paranoia check. */
4033 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4034 max_key_len = 1024 * 1024;
4036 if (kbuf.dsize > max_key_len) {
4037 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4038 "(%u) > (%u)\n\n",
4039 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4040 return 1;
4043 for (i = 0; key_val[i].keyname; i++) {
4044 size_t namelen = strlen(key_val[i].keyname);
4045 if (kbuf.dsize >= namelen && (
4046 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4047 TALLOC_CTX *mem_ctx;
4048 char *keystr;
4049 int ret;
4051 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4052 if (!keystr) {
4053 return 1;
4055 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4056 keystr[kbuf.dsize] = '\0';
4058 mem_ctx = talloc_init("validate_ctx");
4059 if (!mem_ctx) {
4060 SAFE_FREE(keystr);
4061 return 1;
4064 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4065 v_state);
4067 SAFE_FREE(keystr);
4068 talloc_destroy(mem_ctx);
4069 return ret;
4073 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4074 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4075 DEBUG(0,("data :\n"));
4076 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4077 v_state->unknown_key = true;
4078 v_state->success = false;
4079 return 1; /* terminate. */
4082 static void validate_panic(const char *const why)
4084 DEBUG(0,("validating cache: would panic %s\n", why ));
4085 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4086 exit(47);
4089 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4090 TDB_DATA key,
4091 TDB_DATA data,
4092 void *state)
4094 uint64_t ctimeout;
4095 TDB_DATA blob;
4097 if (is_non_centry_key(key)) {
4098 return 0;
4101 if (data.dptr == NULL || data.dsize == 0) {
4102 if (tdb_delete(tdb, key) < 0) {
4103 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4104 key.dptr));
4105 return 1;
4109 /* add timeout to blob (uint64_t) */
4110 blob.dsize = data.dsize + 8;
4112 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4113 if (blob.dptr == NULL) {
4114 return 1;
4116 memset(blob.dptr, 0, blob.dsize);
4118 /* copy status and seqnum */
4119 memcpy(blob.dptr, data.dptr, 8);
4121 /* add timeout */
4122 ctimeout = lp_winbind_cache_time() + time(NULL);
4123 SBVAL(blob.dptr, 8, ctimeout);
4125 /* copy the rest */
4126 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4128 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4129 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4130 key.dptr));
4131 SAFE_FREE(blob.dptr);
4132 return 1;
4135 SAFE_FREE(blob.dptr);
4136 return 0;
4139 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4141 int rc;
4143 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4145 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4146 if (rc < 0) {
4147 return false;
4150 return true;
4153 /***********************************************************************
4154 Try and validate every entry in the winbindd cache. If we fail here,
4155 delete the cache tdb and return non-zero.
4156 ***********************************************************************/
4158 int winbindd_validate_cache(void)
4160 int ret = -1;
4161 const char *tdb_path = state_path("winbindd_cache.tdb");
4162 TDB_CONTEXT *tdb = NULL;
4163 uint32_t vers_id;
4164 bool ok;
4166 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4167 smb_panic_fn = validate_panic;
4169 tdb = tdb_open_log(tdb_path,
4170 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4171 TDB_INCOMPATIBLE_HASH |
4172 ( lp_winbind_offline_logon()
4173 ? TDB_DEFAULT
4174 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4175 O_RDWR|O_CREAT,
4176 0600);
4177 if (!tdb) {
4178 DEBUG(0, ("winbindd_validate_cache: "
4179 "error opening/initializing tdb\n"));
4180 goto done;
4183 /* Version check and upgrade code. */
4184 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4185 DEBUG(10, ("Fresh database\n"));
4186 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4187 vers_id = WINBINDD_CACHE_VERSION;
4190 if (vers_id != WINBINDD_CACHE_VERSION) {
4191 if (vers_id == WINBINDD_CACHE_VER1) {
4192 ok = wbcache_upgrade_v1_to_v2(tdb);
4193 if (!ok) {
4194 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4195 unlink(tdb_path);
4196 goto done;
4199 tdb_store_uint32(tdb,
4200 WINBINDD_CACHE_VERSION_KEYSTR,
4201 WINBINDD_CACHE_VERSION);
4202 vers_id = WINBINDD_CACHE_VER2;
4206 tdb_close(tdb);
4208 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4210 if (ret != 0) {
4211 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4212 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4213 unlink(tdb_path);
4216 done:
4217 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4218 smb_panic_fn = smb_panic;
4219 return ret;
4222 /***********************************************************************
4223 Try and validate every entry in the winbindd cache.
4224 ***********************************************************************/
4226 int winbindd_validate_cache_nobackup(void)
4228 int ret = -1;
4229 const char *tdb_path = state_path("winbindd_cache.tdb");
4231 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4232 smb_panic_fn = validate_panic;
4235 if (wcache == NULL || wcache->tdb == NULL) {
4236 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4237 } else {
4238 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4241 if (ret != 0) {
4242 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4243 "successful.\n"));
4246 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4247 "function\n"));
4248 smb_panic_fn = smb_panic;
4249 return ret;
4252 bool winbindd_cache_validate_and_initialize(void)
4254 close_winbindd_cache();
4256 if (lp_winbind_offline_logon()) {
4257 if (winbindd_validate_cache() < 0) {
4258 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4259 "could be restored.\n"));
4263 return initialize_winbindd_cache();
4266 /*********************************************************************
4267 ********************************************************************/
4269 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4270 struct winbindd_tdc_domain **domains,
4271 size_t *num_domains )
4273 struct winbindd_tdc_domain *list = NULL;
4274 size_t idx;
4275 int i;
4276 bool set_only = false;
4278 /* don't allow duplicates */
4280 idx = *num_domains;
4281 list = *domains;
4283 for ( i=0; i< (*num_domains); i++ ) {
4284 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4285 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4286 new_dom->name));
4287 idx = i;
4288 set_only = true;
4290 break;
4294 if ( !set_only ) {
4295 if ( !*domains ) {
4296 list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
4297 idx = 0;
4298 } else {
4299 list = talloc_realloc( *domains, *domains,
4300 struct winbindd_tdc_domain,
4301 (*num_domains)+1);
4302 idx = *num_domains;
4305 ZERO_STRUCT( list[idx] );
4308 if ( !list )
4309 return false;
4311 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4312 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4314 if ( !is_null_sid( &new_dom->sid ) ) {
4315 sid_copy( &list[idx].sid, &new_dom->sid );
4316 } else {
4317 sid_copy(&list[idx].sid, &global_sid_NULL);
4320 if ( new_dom->domain_flags != 0x0 )
4321 list[idx].trust_flags = new_dom->domain_flags;
4323 if ( new_dom->domain_type != 0x0 )
4324 list[idx].trust_type = new_dom->domain_type;
4326 if ( new_dom->domain_trust_attribs != 0x0 )
4327 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4329 if ( !set_only ) {
4330 *domains = list;
4331 *num_domains = idx + 1;
4334 return true;
4337 /*********************************************************************
4338 ********************************************************************/
4340 static TDB_DATA make_tdc_key( const char *domain_name )
4342 char *keystr = NULL;
4343 TDB_DATA key = { NULL, 0 };
4345 if ( !domain_name ) {
4346 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4347 return key;
4350 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4351 return key;
4353 key = string_term_tdb_data(keystr);
4355 return key;
4358 /*********************************************************************
4359 ********************************************************************/
4361 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4362 size_t num_domains,
4363 unsigned char **buf )
4365 unsigned char *buffer = NULL;
4366 int len = 0;
4367 int buflen = 0;
4368 int i = 0;
4370 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4371 (int)num_domains));
4373 buflen = 0;
4375 again:
4376 len = 0;
4378 /* Store the number of array items first */
4379 len += tdb_pack( buffer+len, buflen-len, "d",
4380 num_domains );
4382 /* now pack each domain trust record */
4383 for ( i=0; i<num_domains; i++ ) {
4385 fstring tmp;
4387 if ( buflen > 0 ) {
4388 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4389 domains[i].domain_name,
4390 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4393 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4394 domains[i].domain_name,
4395 domains[i].dns_name,
4396 sid_to_fstring(tmp, &domains[i].sid),
4397 domains[i].trust_flags,
4398 domains[i].trust_attribs,
4399 domains[i].trust_type );
4402 if ( buflen < len ) {
4403 SAFE_FREE(buffer);
4404 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4405 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4406 buflen = -1;
4407 goto done;
4409 buflen = len;
4410 goto again;
4413 *buf = buffer;
4415 done:
4416 return buflen;
4419 /*********************************************************************
4420 ********************************************************************/
4422 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4423 struct winbindd_tdc_domain **domains )
4425 fstring domain_name, dns_name, sid_string;
4426 uint32 type, attribs, flags;
4427 int num_domains;
4428 int len = 0;
4429 int i;
4430 struct winbindd_tdc_domain *list = NULL;
4432 /* get the number of domains */
4433 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4434 if ( len == -1 ) {
4435 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4436 return 0;
4439 list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
4440 if ( !list ) {
4441 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4442 return 0;
4445 for ( i=0; i<num_domains; i++ ) {
4446 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4447 domain_name,
4448 dns_name,
4449 sid_string,
4450 &flags,
4451 &attribs,
4452 &type );
4454 if ( len == -1 ) {
4455 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4456 TALLOC_FREE( list );
4457 return 0;
4460 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4461 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4462 domain_name, dns_name, sid_string,
4463 flags, attribs, type));
4465 list[i].domain_name = talloc_strdup( list, domain_name );
4466 list[i].dns_name = talloc_strdup( list, dns_name );
4467 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4468 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4469 domain_name));
4471 list[i].trust_flags = flags;
4472 list[i].trust_attribs = attribs;
4473 list[i].trust_type = type;
4476 *domains = list;
4478 return num_domains;
4481 /*********************************************************************
4482 ********************************************************************/
4484 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4486 TDB_DATA key = make_tdc_key( lp_workgroup() );
4487 TDB_DATA data = { NULL, 0 };
4488 int ret;
4490 if ( !key.dptr )
4491 return false;
4493 /* See if we were asked to delete the cache entry */
4495 if ( !domains ) {
4496 ret = tdb_delete( wcache->tdb, key );
4497 goto done;
4500 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4502 if ( !data.dptr ) {
4503 ret = -1;
4504 goto done;
4507 ret = tdb_store( wcache->tdb, key, data, 0 );
4509 done:
4510 SAFE_FREE( data.dptr );
4511 SAFE_FREE( key.dptr );
4513 return ( ret == 0 );
4516 /*********************************************************************
4517 ********************************************************************/
4519 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4521 TDB_DATA key = make_tdc_key( lp_workgroup() );
4522 TDB_DATA data = { NULL, 0 };
4524 *domains = NULL;
4525 *num_domains = 0;
4527 if ( !key.dptr )
4528 return false;
4530 data = tdb_fetch_compat( wcache->tdb, key );
4532 SAFE_FREE( key.dptr );
4534 if ( !data.dptr )
4535 return false;
4537 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4539 SAFE_FREE( data.dptr );
4541 if ( !*domains )
4542 return false;
4544 return true;
4547 /*********************************************************************
4548 ********************************************************************/
4550 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4552 struct winbindd_tdc_domain *dom_list = NULL;
4553 size_t num_domains = 0;
4554 bool ret = false;
4556 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4557 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4558 domain->name, domain->alt_name,
4559 sid_string_dbg(&domain->sid),
4560 domain->domain_flags,
4561 domain->domain_trust_attribs,
4562 domain->domain_type));
4564 if ( !init_wcache() ) {
4565 return false;
4568 /* fetch the list */
4570 wcache_tdc_fetch_list( &dom_list, &num_domains );
4572 /* add the new domain */
4574 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4575 goto done;
4578 /* pack the domain */
4580 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4581 goto done;
4584 /* Success */
4586 ret = true;
4587 done:
4588 TALLOC_FREE( dom_list );
4590 return ret;
4593 /*********************************************************************
4594 ********************************************************************/
4596 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4598 struct winbindd_tdc_domain *dom_list = NULL;
4599 size_t num_domains = 0;
4600 int i;
4601 struct winbindd_tdc_domain *d = NULL;
4603 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4605 if ( !init_wcache() ) {
4606 return NULL;
4609 /* fetch the list */
4611 wcache_tdc_fetch_list( &dom_list, &num_domains );
4613 for ( i=0; i<num_domains; i++ ) {
4614 if ( strequal(name, dom_list[i].domain_name) ||
4615 strequal(name, dom_list[i].dns_name) )
4617 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4618 name));
4620 d = talloc( ctx, struct winbindd_tdc_domain );
4621 if ( !d )
4622 break;
4624 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4625 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4626 sid_copy( &d->sid, &dom_list[i].sid );
4627 d->trust_flags = dom_list[i].trust_flags;
4628 d->trust_type = dom_list[i].trust_type;
4629 d->trust_attribs = dom_list[i].trust_attribs;
4631 break;
4635 TALLOC_FREE( dom_list );
4637 return d;
4640 /*********************************************************************
4641 ********************************************************************/
4643 struct winbindd_tdc_domain*
4644 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4645 const struct dom_sid *sid)
4647 struct winbindd_tdc_domain *dom_list = NULL;
4648 size_t num_domains = 0;
4649 int i;
4650 struct winbindd_tdc_domain *d = NULL;
4652 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4653 sid_string_dbg(sid)));
4655 if (!init_wcache()) {
4656 return NULL;
4659 /* fetch the list */
4661 wcache_tdc_fetch_list(&dom_list, &num_domains);
4663 for (i = 0; i<num_domains; i++) {
4664 if (dom_sid_equal(sid, &(dom_list[i].sid))) {
4665 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4666 "Found domain %s for SID %s\n",
4667 dom_list[i].domain_name,
4668 sid_string_dbg(sid)));
4670 d = talloc(ctx, struct winbindd_tdc_domain);
4671 if (!d)
4672 break;
4674 d->domain_name = talloc_strdup(d,
4675 dom_list[i].domain_name);
4677 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4678 sid_copy(&d->sid, &dom_list[i].sid);
4679 d->trust_flags = dom_list[i].trust_flags;
4680 d->trust_type = dom_list[i].trust_type;
4681 d->trust_attribs = dom_list[i].trust_attribs;
4683 break;
4687 TALLOC_FREE(dom_list);
4689 return d;
4693 /*********************************************************************
4694 ********************************************************************/
4696 void wcache_tdc_clear( void )
4698 if ( !init_wcache() )
4699 return;
4701 wcache_tdc_store_list( NULL, 0 );
4703 return;
4707 /*********************************************************************
4708 ********************************************************************/
4710 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4711 NTSTATUS status,
4712 const struct dom_sid *user_sid,
4713 const char *homedir,
4714 const char *shell,
4715 const char *gecos,
4716 uint32 gid)
4718 struct cache_entry *centry;
4719 fstring tmp;
4721 if ( (centry = centry_start(domain, status)) == NULL )
4722 return;
4724 centry_put_string( centry, homedir );
4725 centry_put_string( centry, shell );
4726 centry_put_string( centry, gecos );
4727 centry_put_uint32( centry, gid );
4729 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4731 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4733 centry_free(centry);
4736 #ifdef HAVE_ADS
4738 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4739 const struct dom_sid *user_sid,
4740 TALLOC_CTX *ctx,
4741 const char **homedir, const char **shell,
4742 const char **gecos, gid_t *p_gid)
4744 struct winbind_cache *cache = get_cache(domain);
4745 struct cache_entry *centry = NULL;
4746 NTSTATUS nt_status;
4747 fstring tmp;
4749 if (!cache->tdb)
4750 goto do_query;
4752 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4753 sid_to_fstring(tmp, user_sid));
4755 if (!centry)
4756 goto do_query;
4758 *homedir = centry_string( centry, ctx );
4759 *shell = centry_string( centry, ctx );
4760 *gecos = centry_string( centry, ctx );
4761 *p_gid = centry_uint32( centry );
4763 centry_free(centry);
4765 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4766 sid_string_dbg(user_sid)));
4768 return NT_STATUS_OK;
4770 do_query:
4772 nt_status = nss_get_info( domain->name, user_sid, ctx,
4773 homedir, shell, gecos, p_gid );
4775 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4777 if ( NT_STATUS_IS_OK(nt_status) ) {
4778 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4779 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4780 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4781 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4783 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4784 *homedir, *shell, *gecos, *p_gid );
4787 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4788 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4789 domain->name ));
4790 set_domain_offline( domain );
4793 return nt_status;
4796 #endif
4798 /* the cache backend methods are exposed via this structure */
4799 struct winbindd_methods cache_methods = {
4800 true,
4801 query_user_list,
4802 enum_dom_groups,
4803 enum_local_groups,
4804 name_to_sid,
4805 sid_to_name,
4806 rids_to_names,
4807 query_user,
4808 lookup_usergroups,
4809 lookup_useraliases,
4810 lookup_groupmem,
4811 sequence_number,
4812 lockout_policy,
4813 password_policy,
4814 trusted_domains
4817 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4818 uint32_t opnum, const DATA_BLOB *req,
4819 TDB_DATA *pkey)
4821 char *key;
4822 size_t keylen;
4824 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4825 if (key == NULL) {
4826 return false;
4828 keylen = talloc_get_size(key) - 1;
4830 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4831 if (key == NULL) {
4832 return false;
4834 memcpy(key + keylen, req->data, req->length);
4836 pkey->dptr = (uint8_t *)key;
4837 pkey->dsize = talloc_get_size(key);
4838 return true;
4841 static bool wcache_opnum_cacheable(uint32_t opnum)
4843 switch (opnum) {
4844 case NDR_WBINT_PING:
4845 case NDR_WBINT_QUERYSEQUENCENUMBER:
4846 case NDR_WBINT_ALLOCATEUID:
4847 case NDR_WBINT_ALLOCATEGID:
4848 case NDR_WBINT_CHECKMACHINEACCOUNT:
4849 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4850 case NDR_WBINT_PINGDC:
4851 return false;
4853 return true;
4856 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4857 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4859 TDB_DATA key, data;
4860 bool ret = false;
4862 if (!wcache_opnum_cacheable(opnum) ||
4863 is_my_own_sam_domain(domain) ||
4864 is_builtin_domain(domain)) {
4865 return false;
4868 if (wcache->tdb == NULL) {
4869 return false;
4872 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4873 return false;
4875 data = tdb_fetch_compat(wcache->tdb, key);
4876 TALLOC_FREE(key.dptr);
4878 if (data.dptr == NULL) {
4879 return false;
4881 if (data.dsize < 12) {
4882 goto fail;
4885 if (!is_domain_offline(domain)) {
4886 uint32_t entry_seqnum, dom_seqnum, last_check;
4887 uint64_t entry_timeout;
4889 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4890 &last_check)) {
4891 goto fail;
4893 entry_seqnum = IVAL(data.dptr, 0);
4894 if (entry_seqnum != dom_seqnum) {
4895 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4896 (int)entry_seqnum));
4897 goto fail;
4899 entry_timeout = BVAL(data.dptr, 4);
4900 if (time(NULL) > entry_timeout) {
4901 DEBUG(10, ("Entry has timed out\n"));
4902 goto fail;
4906 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4907 data.dsize - 12);
4908 if (resp->data == NULL) {
4909 DEBUG(10, ("talloc failed\n"));
4910 goto fail;
4912 resp->length = data.dsize - 12;
4914 ret = true;
4915 fail:
4916 SAFE_FREE(data.dptr);
4917 return ret;
4920 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4921 const DATA_BLOB *req, const DATA_BLOB *resp)
4923 TDB_DATA key, data;
4924 uint32_t dom_seqnum, last_check;
4925 uint64_t timeout;
4927 if (!wcache_opnum_cacheable(opnum) ||
4928 is_my_own_sam_domain(domain) ||
4929 is_builtin_domain(domain)) {
4930 return;
4933 if (wcache->tdb == NULL) {
4934 return;
4937 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4938 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4939 domain->name));
4940 return;
4943 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4944 return;
4947 timeout = time(NULL) + lp_winbind_cache_time();
4949 data.dsize = resp->length + 12;
4950 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4951 if (data.dptr == NULL) {
4952 goto done;
4955 SIVAL(data.dptr, 0, dom_seqnum);
4956 SBVAL(data.dptr, 4, timeout);
4957 memcpy(data.dptr + 12, resp->data, resp->length);
4959 tdb_store(wcache->tdb, key, data, 0);
4961 done:
4962 TALLOC_FREE(key.dptr);
4963 return;