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/>.
28 #include "tdb_validate.h"
29 #include "../libcli/auth/libcli_auth.h"
30 #include "../librpc/gen_ndr/ndr_wbint.h"
33 #define DBGC_CLASS DBGC_WINBIND
35 #define WINBINDD_CACHE_VER1 1 /* initial db version */
36 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
38 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
39 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
41 extern struct winbindd_methods reconnect_methods;
43 extern struct winbindd_methods ads_methods;
45 extern struct winbindd_methods builtin_passdb_methods;
48 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
49 * Here are the list of entry types that are *not* stored
50 * as form struct cache_entry in the cache.
53 static const char *non_centry_keys[] = {
58 WINBINDD_CACHE_VERSION_KEYSTR,
62 /************************************************************************
63 Is this key a non-centry type ?
64 ************************************************************************/
66 static bool is_non_centry_key(TDB_DATA kbuf)
70 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
73 for (i = 0; non_centry_keys[i] != NULL; i++) {
74 size_t namelen = strlen(non_centry_keys[i]);
75 if (kbuf.dsize < namelen) {
78 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
85 /* Global online/offline state - False when online. winbindd starts up online
86 and sets this to true if the first query fails and there's an entry in
87 the cache tdb telling us to stay offline. */
89 static bool global_winbindd_offline_state;
91 struct winbind_cache {
97 uint32 sequence_number;
103 void (*smb_panic_fn)(const char *const why) = smb_panic;
105 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
107 static struct winbind_cache *wcache;
109 void winbindd_check_cache_size(time_t t)
111 static time_t last_check_time;
114 if (last_check_time == (time_t)0)
117 if (t - last_check_time < 60 && t - last_check_time > 0)
120 if (wcache == NULL || wcache->tdb == NULL) {
121 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
125 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
126 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
130 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
131 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
132 (unsigned long)st.st_size,
133 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
134 wcache_flush_cache();
138 /* get the winbind_cache structure */
139 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
141 struct winbind_cache *ret = wcache;
143 /* We have to know what type of domain we are dealing with first. */
145 if (domain->internal) {
146 domain->backend = &builtin_passdb_methods;
147 domain->initialized = True;
149 if ( !domain->initialized ) {
150 init_dc_connection( domain );
154 OK. listen up becasue I'm only going to say this once.
155 We have the following scenarios to consider
156 (a) trusted AD domains on a Samba DC,
157 (b) trusted AD domains and we are joined to a non-kerberos domain
158 (c) trusted AD domains and we are joined to a kerberos (AD) domain
160 For (a) we can always contact the trusted domain using krb5
161 since we have the domain trust account password
163 For (b) we can only use RPC since we have no way of
164 getting a krb5 ticket in our own domain
166 For (c) we can always use krb5 since we have a kerberos trust
171 if (!domain->backend) {
173 struct winbindd_domain *our_domain = domain;
175 /* find our domain first so we can figure out if we
176 are joined to a kerberized domain */
178 if ( !domain->primary )
179 our_domain = find_our_domain();
181 if ((our_domain->active_directory || IS_DC)
182 && domain->active_directory
183 && !lp_winbind_rpc_only()) {
184 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
185 domain->backend = &ads_methods;
187 #endif /* HAVE_ADS */
188 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
189 domain->backend = &reconnect_methods;
192 #endif /* HAVE_ADS */
198 ret = SMB_XMALLOC_P(struct winbind_cache);
202 wcache_flush_cache();
208 free a centry structure
210 static void centry_free(struct cache_entry *centry)
214 SAFE_FREE(centry->data);
218 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
220 if (centry->len - centry->ofs < nbytes) {
221 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
222 (unsigned int)nbytes,
223 centry->len - centry->ofs));
230 pull a uint64 from a cache entry
232 static uint64 centry_uint64(struct cache_entry *centry)
236 if (!centry_check_bytes(centry, 8)) {
237 smb_panic_fn("centry_uint64");
239 ret = BVAL(centry->data, centry->ofs);
245 pull a uint32 from a cache entry
247 static uint32 centry_uint32(struct cache_entry *centry)
251 if (!centry_check_bytes(centry, 4)) {
252 smb_panic_fn("centry_uint32");
254 ret = IVAL(centry->data, centry->ofs);
260 pull a uint16 from a cache entry
262 static uint16 centry_uint16(struct cache_entry *centry)
265 if (!centry_check_bytes(centry, 2)) {
266 smb_panic_fn("centry_uint16");
268 ret = SVAL(centry->data, centry->ofs);
274 pull a uint8 from a cache entry
276 static uint8 centry_uint8(struct cache_entry *centry)
279 if (!centry_check_bytes(centry, 1)) {
280 smb_panic_fn("centry_uint8");
282 ret = CVAL(centry->data, centry->ofs);
288 pull a NTTIME from a cache entry
290 static NTTIME centry_nttime(struct cache_entry *centry)
293 if (!centry_check_bytes(centry, 8)) {
294 smb_panic_fn("centry_nttime");
296 ret = IVAL(centry->data, centry->ofs);
298 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
304 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
306 static time_t centry_time(struct cache_entry *centry)
308 return (time_t)centry_nttime(centry);
311 /* pull a string from a cache entry, using the supplied
314 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
319 len = centry_uint8(centry);
322 /* a deliberate NULL string */
326 if (!centry_check_bytes(centry, (size_t)len)) {
327 smb_panic_fn("centry_string");
330 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
332 smb_panic_fn("centry_string out of memory\n");
334 memcpy(ret,centry->data + centry->ofs, len);
340 /* pull a hash16 from a cache entry, using the supplied
343 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
348 len = centry_uint8(centry);
351 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
356 if (!centry_check_bytes(centry, 16)) {
360 ret = TALLOC_ARRAY(mem_ctx, char, 16);
362 smb_panic_fn("centry_hash out of memory\n");
364 memcpy(ret,centry->data + centry->ofs, 16);
369 /* pull a sid from a cache entry, using the supplied
372 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
377 sid_string = centry_string(centry, talloc_tos());
378 if (sid_string == NULL) {
381 ret = string_to_sid(sid, sid_string);
382 TALLOC_FREE(sid_string);
388 pull a NTSTATUS from a cache entry
390 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
394 status = NT_STATUS(centry_uint32(centry));
399 /* the server is considered down if it can't give us a sequence number */
400 static bool wcache_server_down(struct winbindd_domain *domain)
407 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
410 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
415 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
416 uint32_t *last_seq_check)
421 if (wcache->tdb == NULL) {
422 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
426 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
428 DEBUG(10, ("talloc failed\n"));
432 data = tdb_fetch_bystring(wcache->tdb, key);
435 if (data.dptr == NULL) {
436 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
440 if (data.dsize != 8) {
441 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
443 SAFE_FREE(data.dptr);
447 *seqnum = IVAL(data.dptr, 0);
448 *last_seq_check = IVAL(data.dptr, 4);
449 SAFE_FREE(data.dptr);
454 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
456 uint32 last_check, time_diff;
458 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
460 return NT_STATUS_UNSUCCESSFUL;
462 domain->last_seq_check = last_check;
464 /* have we expired? */
466 time_diff = now - domain->last_seq_check;
467 if ( time_diff > lp_winbind_cache_time() ) {
468 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
469 domain->name, domain->sequence_number,
470 (uint32)domain->last_seq_check));
471 return NT_STATUS_UNSUCCESSFUL;
474 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
475 domain->name, domain->sequence_number,
476 (uint32)domain->last_seq_check));
481 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
482 time_t last_seq_check)
488 if (wcache->tdb == NULL) {
489 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
493 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
494 if (key_str == NULL) {
495 DEBUG(10, ("talloc_asprintf failed\n"));
499 SIVAL(buf, 0, seqnum);
500 SIVAL(buf, 4, last_seq_check);
502 ret = tdb_store_bystring(wcache->tdb, key_str,
503 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
504 TALLOC_FREE(key_str);
506 DEBUG(10, ("tdb_store_bystring failed: %s\n",
507 tdb_errorstr(wcache->tdb)));
508 TALLOC_FREE(key_str);
512 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
513 domain_name, seqnum, (unsigned)last_seq_check));
518 static bool store_cache_seqnum( struct winbindd_domain *domain )
520 return wcache_store_seqnum(domain->name, domain->sequence_number,
521 domain->last_seq_check);
525 refresh the domain sequence number. If force is true
526 then always refresh it, no matter how recently we fetched it
529 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
533 time_t t = time(NULL);
534 unsigned cache_time = lp_winbind_cache_time();
536 if (is_domain_offline(domain)) {
542 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
543 /* trying to reconnect is expensive, don't do it too often */
544 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
549 time_diff = t - domain->last_seq_check;
551 /* see if we have to refetch the domain sequence number */
552 if (!force && (time_diff < cache_time) &&
553 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
554 NT_STATUS_IS_OK(domain->last_status)) {
555 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
559 /* try to get the sequence number from the tdb cache first */
560 /* this will update the timestamp as well */
562 status = fetch_cache_seqnum( domain, t );
563 if (NT_STATUS_IS_OK(status) &&
564 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
565 NT_STATUS_IS_OK(domain->last_status)) {
569 /* important! make sure that we know if this is a native
570 mode domain or not. And that we can contact it. */
572 if ( winbindd_can_contact_domain( domain ) ) {
573 status = domain->backend->sequence_number(domain,
574 &domain->sequence_number);
576 /* just use the current time */
577 status = NT_STATUS_OK;
578 domain->sequence_number = time(NULL);
582 /* the above call could have set our domain->backend to NULL when
583 * coming from offline to online mode, make sure to reinitialize the
584 * backend - Guenther */
587 if (!NT_STATUS_IS_OK(status)) {
588 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
589 domain->sequence_number = DOM_SEQUENCE_NONE;
592 domain->last_status = status;
593 domain->last_seq_check = time(NULL);
595 /* save the new sequence number in the cache */
596 store_cache_seqnum( domain );
599 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
600 domain->name, domain->sequence_number));
606 decide if a cache entry has expired
608 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
610 /* If we've been told to be offline - stay in that state... */
611 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
612 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
613 keystr, domain->name ));
617 /* when the domain is offline return the cached entry.
618 * This deals with transient offline states... */
620 if (!domain->online) {
621 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
622 keystr, domain->name ));
626 /* if the server is OK and our cache entry came from when it was down then
627 the entry is invalid */
628 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
629 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
630 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
631 keystr, domain->name ));
635 /* if the server is down or the cache entry is not older than the
636 current sequence number or it did not timeout then it is OK */
637 if (wcache_server_down(domain)
638 || (centry->sequence_number == domain->sequence_number
639 && centry->timeout > time(NULL))) {
640 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
641 keystr, domain->name ));
645 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
646 keystr, domain->name ));
652 static struct cache_entry *wcache_fetch_raw(char *kstr)
655 struct cache_entry *centry;
658 key = string_tdb_data(kstr);
659 data = tdb_fetch(wcache->tdb, key);
665 centry = SMB_XMALLOC_P(struct cache_entry);
666 centry->data = (unsigned char *)data.dptr;
667 centry->len = data.dsize;
670 if (centry->len < 16) {
671 /* huh? corrupt cache? */
672 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
673 "(len < 16)?\n", kstr));
678 centry->status = centry_ntstatus(centry);
679 centry->sequence_number = centry_uint32(centry);
680 centry->timeout = centry_uint64(centry);
686 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
687 number and return status
689 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
690 struct winbindd_domain *domain,
691 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
692 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
693 struct winbindd_domain *domain,
694 const char *format, ...)
698 struct cache_entry *centry;
700 if (!winbindd_use_cache()) {
704 refresh_sequence_number(domain, false);
706 va_start(ap, format);
707 smb_xvasprintf(&kstr, format, ap);
710 centry = wcache_fetch_raw(kstr);
711 if (centry == NULL) {
716 if (centry_expired(domain, kstr, centry)) {
718 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
719 kstr, domain->name ));
726 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
727 kstr, domain->name ));
733 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
734 static void wcache_delete(const char *format, ...)
740 va_start(ap, format);
741 smb_xvasprintf(&kstr, format, ap);
744 key = string_tdb_data(kstr);
746 tdb_delete(wcache->tdb, key);
751 make sure we have at least len bytes available in a centry
753 static void centry_expand(struct cache_entry *centry, uint32 len)
755 if (centry->len - centry->ofs >= len)
758 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
761 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
762 smb_panic_fn("out of memory in centry_expand");
767 push a uint64 into a centry
769 static void centry_put_uint64(struct cache_entry *centry, uint64 v)
771 centry_expand(centry, 8);
772 SBVAL(centry->data, centry->ofs, v);
777 push a uint32 into a centry
779 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
781 centry_expand(centry, 4);
782 SIVAL(centry->data, centry->ofs, v);
787 push a uint16 into a centry
789 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
791 centry_expand(centry, 2);
792 SSVAL(centry->data, centry->ofs, v);
797 push a uint8 into a centry
799 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
801 centry_expand(centry, 1);
802 SCVAL(centry->data, centry->ofs, v);
807 push a string into a centry
809 static void centry_put_string(struct cache_entry *centry, const char *s)
814 /* null strings are marked as len 0xFFFF */
815 centry_put_uint8(centry, 0xFF);
820 /* can't handle more than 254 char strings. Truncating is probably best */
822 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
825 centry_put_uint8(centry, len);
826 centry_expand(centry, len);
827 memcpy(centry->data + centry->ofs, s, len);
832 push a 16 byte hash into a centry - treat as 16 byte string.
834 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
836 centry_put_uint8(centry, 16);
837 centry_expand(centry, 16);
838 memcpy(centry->data + centry->ofs, val, 16);
842 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
845 centry_put_string(centry, sid_to_fstring(sid_string, sid));
850 put NTSTATUS into a centry
852 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
854 uint32 status_value = NT_STATUS_V(status);
855 centry_put_uint32(centry, status_value);
860 push a NTTIME into a centry
862 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
864 centry_expand(centry, 8);
865 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
867 SIVAL(centry->data, centry->ofs, nt >> 32);
872 push a time_t into a centry - use a 64 bit size.
873 NTTIME here is being used as a convenient 64-bit size.
875 static void centry_put_time(struct cache_entry *centry, time_t t)
877 NTTIME nt = (NTTIME)t;
878 centry_put_nttime(centry, nt);
882 start a centry for output. When finished, call centry_end()
884 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
886 struct cache_entry *centry;
891 centry = SMB_XMALLOC_P(struct cache_entry);
893 centry->len = 8192; /* reasonable default */
894 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
896 centry->sequence_number = domain->sequence_number;
897 centry->timeout = lp_winbind_cache_time() + time(NULL);
898 centry_put_ntstatus(centry, status);
899 centry_put_uint32(centry, centry->sequence_number);
900 centry_put_uint64(centry, centry->timeout);
905 finish a centry and write it to the tdb
907 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
908 static void centry_end(struct cache_entry *centry, const char *format, ...)
914 if (!winbindd_use_cache()) {
918 va_start(ap, format);
919 smb_xvasprintf(&kstr, format, ap);
922 key = string_tdb_data(kstr);
923 data.dptr = centry->data;
924 data.dsize = centry->ofs;
926 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
930 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
931 NTSTATUS status, const char *domain_name,
932 const char *name, const DOM_SID *sid,
933 enum lsa_SidType type)
935 struct cache_entry *centry;
938 centry = centry_start(domain, status);
941 centry_put_uint32(centry, type);
942 centry_put_sid(centry, sid);
943 fstrcpy(uname, name);
945 centry_end(centry, "NS/%s/%s", domain_name, uname);
946 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
947 uname, sid_string_dbg(sid), nt_errstr(status)));
951 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
952 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
954 struct cache_entry *centry;
957 centry = centry_start(domain, status);
961 if (NT_STATUS_IS_OK(status)) {
962 centry_put_uint32(centry, type);
963 centry_put_string(centry, domain_name);
964 centry_put_string(centry, name);
967 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
968 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
969 name, nt_errstr(status)));
974 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
975 struct wbint_userinfo *info)
977 struct cache_entry *centry;
980 if (is_null_sid(&info->user_sid)) {
984 centry = centry_start(domain, status);
987 centry_put_string(centry, info->acct_name);
988 centry_put_string(centry, info->full_name);
989 centry_put_string(centry, info->homedir);
990 centry_put_string(centry, info->shell);
991 centry_put_uint32(centry, info->primary_gid);
992 centry_put_sid(centry, &info->user_sid);
993 centry_put_sid(centry, &info->group_sid);
994 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
996 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1000 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1002 struct samr_DomInfo12 *lockout_policy)
1004 struct cache_entry *centry;
1006 centry = centry_start(domain, status);
1010 centry_put_nttime(centry, lockout_policy->lockout_duration);
1011 centry_put_nttime(centry, lockout_policy->lockout_window);
1012 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1014 centry_end(centry, "LOC_POL/%s", domain->name);
1016 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1018 centry_free(centry);
1023 static void wcache_save_password_policy(struct winbindd_domain *domain,
1025 struct samr_DomInfo1 *policy)
1027 struct cache_entry *centry;
1029 centry = centry_start(domain, status);
1033 centry_put_uint16(centry, policy->min_password_length);
1034 centry_put_uint16(centry, policy->password_history_length);
1035 centry_put_uint32(centry, policy->password_properties);
1036 centry_put_nttime(centry, policy->max_password_age);
1037 centry_put_nttime(centry, policy->min_password_age);
1039 centry_end(centry, "PWD_POL/%s", domain->name);
1041 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1043 centry_free(centry);
1046 /***************************************************************************
1047 ***************************************************************************/
1049 static void wcache_save_username_alias(struct winbindd_domain *domain,
1051 const char *name, const char *alias)
1053 struct cache_entry *centry;
1056 if ( (centry = centry_start(domain, status)) == NULL )
1059 centry_put_string( centry, alias );
1061 fstrcpy(uname, name);
1063 centry_end(centry, "NSS/NA/%s", uname);
1065 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1067 centry_free(centry);
1070 static void wcache_save_alias_username(struct winbindd_domain *domain,
1072 const char *alias, const char *name)
1074 struct cache_entry *centry;
1077 if ( (centry = centry_start(domain, status)) == NULL )
1080 centry_put_string( centry, name );
1082 fstrcpy(uname, alias);
1084 centry_end(centry, "NSS/AN/%s", uname);
1086 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1088 centry_free(centry);
1091 /***************************************************************************
1092 ***************************************************************************/
1094 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1095 struct winbindd_domain *domain,
1096 const char *name, char **alias )
1098 struct winbind_cache *cache = get_cache(domain);
1099 struct cache_entry *centry = NULL;
1103 if ( domain->internal )
1104 return NT_STATUS_NOT_SUPPORTED;
1109 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1110 return NT_STATUS_NO_MEMORY;
1111 strupper_m(upper_name);
1113 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1115 SAFE_FREE( upper_name );
1120 status = centry->status;
1122 if (!NT_STATUS_IS_OK(status)) {
1123 centry_free(centry);
1127 *alias = centry_string( centry, mem_ctx );
1129 centry_free(centry);
1131 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1132 name, *alias ? *alias : "(none)"));
1134 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1138 /* If its not in cache and we are offline, then fail */
1140 if ( get_global_winbindd_state_offline() || !domain->online ) {
1141 DEBUG(8,("resolve_username_to_alias: rejecting query "
1142 "in offline mode\n"));
1143 return NT_STATUS_NOT_FOUND;
1146 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1148 if ( NT_STATUS_IS_OK( status ) ) {
1149 wcache_save_username_alias(domain, status, name, *alias);
1152 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1153 wcache_save_username_alias(domain, status, name, "(NULL)");
1156 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1157 nt_errstr(status)));
1159 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1160 set_domain_offline( domain );
1166 /***************************************************************************
1167 ***************************************************************************/
1169 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1170 struct winbindd_domain *domain,
1171 const char *alias, char **name )
1173 struct winbind_cache *cache = get_cache(domain);
1174 struct cache_entry *centry = NULL;
1178 if ( domain->internal )
1179 return NT_STATUS_NOT_SUPPORTED;
1184 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1185 return NT_STATUS_NO_MEMORY;
1186 strupper_m(upper_name);
1188 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1190 SAFE_FREE( upper_name );
1195 status = centry->status;
1197 if (!NT_STATUS_IS_OK(status)) {
1198 centry_free(centry);
1202 *name = centry_string( centry, mem_ctx );
1204 centry_free(centry);
1206 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1207 alias, *name ? *name : "(none)"));
1209 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1213 /* If its not in cache and we are offline, then fail */
1215 if ( get_global_winbindd_state_offline() || !domain->online ) {
1216 DEBUG(8,("resolve_alias_to_username: rejecting query "
1217 "in offline mode\n"));
1218 return NT_STATUS_NOT_FOUND;
1221 /* an alias cannot contain a domain prefix or '@' */
1223 if (strchr(alias, '\\') || strchr(alias, '@')) {
1224 DEBUG(10,("resolve_alias_to_username: skipping fully "
1225 "qualified name %s\n", alias));
1226 return NT_STATUS_OBJECT_NAME_INVALID;
1229 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1231 if ( NT_STATUS_IS_OK( status ) ) {
1232 wcache_save_alias_username( domain, status, alias, *name );
1235 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1236 wcache_save_alias_username(domain, status, alias, "(NULL)");
1239 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1240 nt_errstr(status)));
1242 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1243 set_domain_offline( domain );
1249 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1251 struct winbind_cache *cache = get_cache(domain);
1253 fstring key_str, tmp;
1257 return NT_STATUS_INTERNAL_DB_ERROR;
1260 if (is_null_sid(sid)) {
1261 return NT_STATUS_INVALID_SID;
1264 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1265 return NT_STATUS_INVALID_SID;
1268 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1270 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1272 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1275 SAFE_FREE(data.dptr);
1276 return NT_STATUS_OK;
1279 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1280 as new salted ones. */
1282 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1283 TALLOC_CTX *mem_ctx,
1285 const uint8 **cached_nt_pass,
1286 const uint8 **cached_salt)
1288 struct winbind_cache *cache = get_cache(domain);
1289 struct cache_entry *centry = NULL;
1296 return NT_STATUS_INTERNAL_DB_ERROR;
1299 if (is_null_sid(sid)) {
1300 return NT_STATUS_INVALID_SID;
1303 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1304 return NT_STATUS_INVALID_SID;
1307 /* Try and get a salted cred first. If we can't
1308 fall back to an unsalted cred. */
1310 centry = wcache_fetch(cache, domain, "CRED/%s",
1311 sid_to_fstring(tmp, sid));
1313 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1314 sid_string_dbg(sid)));
1315 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1318 t = centry_time(centry);
1320 /* In the salted case this isn't actually the nt_hash itself,
1321 but the MD5 of the salt + nt_hash. Let the caller
1322 sort this out. It can tell as we only return the cached_salt
1323 if we are returning a salted cred. */
1325 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1326 if (*cached_nt_pass == NULL) {
1329 sid_to_fstring(sidstr, sid);
1331 /* Bad (old) cred cache. Delete and pretend we
1333 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1335 wcache_delete("CRED/%s", sidstr);
1336 centry_free(centry);
1337 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1340 /* We only have 17 bytes more data in the salted cred case. */
1341 if (centry->len - centry->ofs == 17) {
1342 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1344 *cached_salt = NULL;
1347 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1349 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1352 status = centry->status;
1354 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1355 sid_string_dbg(sid), nt_errstr(status) ));
1357 centry_free(centry);
1361 /* Store creds for a SID - only writes out new salted ones. */
1363 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1364 TALLOC_CTX *mem_ctx,
1366 const uint8 nt_pass[NT_HASH_LEN])
1368 struct cache_entry *centry;
1371 uint8 cred_salt[NT_HASH_LEN];
1372 uint8 salted_hash[NT_HASH_LEN];
1374 if (is_null_sid(sid)) {
1375 return NT_STATUS_INVALID_SID;
1378 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1379 return NT_STATUS_INVALID_SID;
1382 centry = centry_start(domain, NT_STATUS_OK);
1384 return NT_STATUS_INTERNAL_DB_ERROR;
1387 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1389 centry_put_time(centry, time(NULL));
1391 /* Create a salt and then salt the hash. */
1392 generate_random_buffer(cred_salt, NT_HASH_LEN);
1393 E_md5hash(cred_salt, nt_pass, salted_hash);
1395 centry_put_hash16(centry, salted_hash);
1396 centry_put_hash16(centry, cred_salt);
1397 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1399 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1401 centry_free(centry);
1403 return NT_STATUS_OK;
1407 /* Query display info. This is the basic user list fn */
1408 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1409 TALLOC_CTX *mem_ctx,
1410 uint32 *num_entries,
1411 struct wbint_userinfo **info)
1413 struct winbind_cache *cache = get_cache(domain);
1414 struct cache_entry *centry = NULL;
1416 unsigned int i, retry;
1417 bool old_status = domain->online;
1422 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1427 *num_entries = centry_uint32(centry);
1429 if (*num_entries == 0)
1432 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1434 smb_panic_fn("query_user_list out of memory");
1436 for (i=0; i<(*num_entries); i++) {
1437 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1438 (*info)[i].full_name = centry_string(centry, mem_ctx);
1439 (*info)[i].homedir = centry_string(centry, mem_ctx);
1440 (*info)[i].shell = centry_string(centry, mem_ctx);
1441 centry_sid(centry, &(*info)[i].user_sid);
1442 centry_sid(centry, &(*info)[i].group_sid);
1446 status = centry->status;
1448 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1449 domain->name, nt_errstr(status) ));
1451 centry_free(centry);
1458 /* Return status value returned by seq number check */
1460 if (!NT_STATUS_IS_OK(domain->last_status))
1461 return domain->last_status;
1463 /* Put the query_user_list() in a retry loop. There appears to be
1464 * some bug either with Windows 2000 or Samba's handling of large
1465 * rpc replies. This manifests itself as sudden disconnection
1466 * at a random point in the enumeration of a large (60k) user list.
1467 * The retry loop simply tries the operation again. )-: It's not
1468 * pretty but an acceptable workaround until we work out what the
1469 * real problem is. */
1474 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1477 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1478 if (!NT_STATUS_IS_OK(status)) {
1479 DEBUG(3, ("query_user_list: returned 0x%08x, "
1480 "retrying\n", NT_STATUS_V(status)));
1482 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1483 DEBUG(3, ("query_user_list: flushing "
1484 "connection cache\n"));
1485 invalidate_cm_connection(&domain->conn);
1487 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1488 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1489 if (!domain->internal && old_status) {
1490 set_domain_offline(domain);
1492 /* store partial response. */
1493 if (*num_entries > 0) {
1495 * humm, what about the status used for cache?
1496 * Should it be NT_STATUS_OK?
1501 * domain is offline now, and there is no user entries,
1502 * try to fetch from cache again.
1504 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1505 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1506 /* partial response... */
1510 goto do_fetch_cache;
1517 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1521 refresh_sequence_number(domain, false);
1522 if (!NT_STATUS_IS_OK(status)) {
1525 centry = centry_start(domain, status);
1528 centry_put_uint32(centry, *num_entries);
1529 for (i=0; i<(*num_entries); i++) {
1530 centry_put_string(centry, (*info)[i].acct_name);
1531 centry_put_string(centry, (*info)[i].full_name);
1532 centry_put_string(centry, (*info)[i].homedir);
1533 centry_put_string(centry, (*info)[i].shell);
1534 centry_put_sid(centry, &(*info)[i].user_sid);
1535 centry_put_sid(centry, &(*info)[i].group_sid);
1536 if (domain->backend && domain->backend->consistent) {
1537 /* when the backend is consistent we can pre-prime some mappings */
1538 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1540 (*info)[i].acct_name,
1541 &(*info)[i].user_sid,
1543 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1544 &(*info)[i].user_sid,
1546 (*info)[i].acct_name,
1548 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1551 centry_end(centry, "UL/%s", domain->name);
1552 centry_free(centry);
1558 /* list all domain groups */
1559 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1560 TALLOC_CTX *mem_ctx,
1561 uint32 *num_entries,
1562 struct acct_info **info)
1564 struct winbind_cache *cache = get_cache(domain);
1565 struct cache_entry *centry = NULL;
1570 old_status = domain->online;
1574 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1579 *num_entries = centry_uint32(centry);
1581 if (*num_entries == 0)
1584 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1586 smb_panic_fn("enum_dom_groups out of memory");
1588 for (i=0; i<(*num_entries); i++) {
1589 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1590 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1591 (*info)[i].rid = centry_uint32(centry);
1595 status = centry->status;
1597 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1598 domain->name, nt_errstr(status) ));
1600 centry_free(centry);
1607 /* Return status value returned by seq number check */
1609 if (!NT_STATUS_IS_OK(domain->last_status))
1610 return domain->last_status;
1612 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1615 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1617 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1618 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1619 if (!domain->internal && old_status) {
1620 set_domain_offline(domain);
1624 !domain->internal &&
1626 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1628 goto do_fetch_cache;
1633 refresh_sequence_number(domain, false);
1634 if (!NT_STATUS_IS_OK(status)) {
1637 centry = centry_start(domain, status);
1640 centry_put_uint32(centry, *num_entries);
1641 for (i=0; i<(*num_entries); i++) {
1642 centry_put_string(centry, (*info)[i].acct_name);
1643 centry_put_string(centry, (*info)[i].acct_desc);
1644 centry_put_uint32(centry, (*info)[i].rid);
1646 centry_end(centry, "GL/%s/domain", domain->name);
1647 centry_free(centry);
1653 /* list all domain groups */
1654 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1655 TALLOC_CTX *mem_ctx,
1656 uint32 *num_entries,
1657 struct acct_info **info)
1659 struct winbind_cache *cache = get_cache(domain);
1660 struct cache_entry *centry = NULL;
1665 old_status = domain->online;
1669 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1674 *num_entries = centry_uint32(centry);
1676 if (*num_entries == 0)
1679 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1681 smb_panic_fn("enum_dom_groups out of memory");
1683 for (i=0; i<(*num_entries); i++) {
1684 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1685 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1686 (*info)[i].rid = centry_uint32(centry);
1691 /* If we are returning cached data and the domain controller
1692 is down then we don't know whether the data is up to date
1693 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1696 if (wcache_server_down(domain)) {
1697 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1698 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1700 status = centry->status;
1702 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1703 domain->name, nt_errstr(status) ));
1705 centry_free(centry);
1712 /* Return status value returned by seq number check */
1714 if (!NT_STATUS_IS_OK(domain->last_status))
1715 return domain->last_status;
1717 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1720 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1722 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1723 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1724 if (!domain->internal && old_status) {
1725 set_domain_offline(domain);
1728 !domain->internal &&
1731 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1733 goto do_fetch_cache;
1738 refresh_sequence_number(domain, false);
1739 if (!NT_STATUS_IS_OK(status)) {
1742 centry = centry_start(domain, status);
1745 centry_put_uint32(centry, *num_entries);
1746 for (i=0; i<(*num_entries); i++) {
1747 centry_put_string(centry, (*info)[i].acct_name);
1748 centry_put_string(centry, (*info)[i].acct_desc);
1749 centry_put_uint32(centry, (*info)[i].rid);
1751 centry_end(centry, "GL/%s/local", domain->name);
1752 centry_free(centry);
1758 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1759 const char *domain_name,
1761 struct dom_sid *sid,
1762 enum lsa_SidType *type)
1764 struct winbind_cache *cache = get_cache(domain);
1765 struct cache_entry *centry;
1769 if (cache->tdb == NULL) {
1770 return NT_STATUS_NOT_FOUND;
1773 uname = talloc_strdup_upper(talloc_tos(), name);
1774 if (uname == NULL) {
1775 return NT_STATUS_NO_MEMORY;
1778 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1780 if (centry == NULL) {
1781 return NT_STATUS_NOT_FOUND;
1784 status = centry->status;
1785 if (NT_STATUS_IS_OK(status)) {
1786 *type = (enum lsa_SidType)centry_uint32(centry);
1787 centry_sid(centry, sid);
1790 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1791 "%s\n", domain->name, nt_errstr(status) ));
1793 centry_free(centry);
1797 /* convert a single name to a sid in a domain */
1798 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1799 TALLOC_CTX *mem_ctx,
1800 const char *domain_name,
1804 enum lsa_SidType *type)
1809 old_status = domain->online;
1811 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1812 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1818 /* If the seq number check indicated that there is a problem
1819 * with this DC, then return that status... except for
1820 * access_denied. This is special because the dc may be in
1821 * "restrict anonymous = 1" mode, in which case it will deny
1822 * most unauthenticated operations, but *will* allow the LSA
1823 * name-to-sid that we try as a fallback. */
1825 if (!(NT_STATUS_IS_OK(domain->last_status)
1826 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1827 return domain->last_status;
1829 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1832 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1833 name, flags, sid, type);
1835 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1836 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1837 if (!domain->internal && old_status) {
1838 set_domain_offline(domain);
1840 if (!domain->internal &&
1843 NTSTATUS cache_status;
1844 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1845 return cache_status;
1849 refresh_sequence_number(domain, false);
1851 if (domain->online &&
1852 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1853 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1855 /* Only save the reverse mapping if this was not a UPN */
1856 if (!strchr(name, '@')) {
1857 strupper_m(CONST_DISCARD(char *,domain_name));
1858 strlower_m(CONST_DISCARD(char *,name));
1859 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1866 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1867 const struct dom_sid *sid,
1868 TALLOC_CTX *mem_ctx,
1871 enum lsa_SidType *type)
1873 struct winbind_cache *cache = get_cache(domain);
1874 struct cache_entry *centry;
1878 if (cache->tdb == NULL) {
1879 return NT_STATUS_NOT_FOUND;
1882 sid_string = sid_string_tos(sid);
1883 if (sid_string == NULL) {
1884 return NT_STATUS_NO_MEMORY;
1887 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1888 TALLOC_FREE(sid_string);
1889 if (centry == NULL) {
1890 return NT_STATUS_NOT_FOUND;
1893 if (NT_STATUS_IS_OK(centry->status)) {
1894 *type = (enum lsa_SidType)centry_uint32(centry);
1895 *domain_name = centry_string(centry, mem_ctx);
1896 *name = centry_string(centry, mem_ctx);
1899 status = centry->status;
1900 centry_free(centry);
1902 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1903 "%s\n", domain->name, nt_errstr(status) ));
1908 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1910 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1911 TALLOC_CTX *mem_ctx,
1915 enum lsa_SidType *type)
1920 old_status = domain->online;
1921 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1923 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1928 *domain_name = NULL;
1930 /* If the seq number check indicated that there is a problem
1931 * with this DC, then return that status... except for
1932 * access_denied. This is special because the dc may be in
1933 * "restrict anonymous = 1" mode, in which case it will deny
1934 * most unauthenticated operations, but *will* allow the LSA
1935 * sid-to-name that we try as a fallback. */
1937 if (!(NT_STATUS_IS_OK(domain->last_status)
1938 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1939 return domain->last_status;
1941 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1944 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1946 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1947 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1948 if (!domain->internal && old_status) {
1949 set_domain_offline(domain);
1951 if (!domain->internal &&
1954 NTSTATUS cache_status;
1955 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1956 domain_name, name, type);
1957 return cache_status;
1961 refresh_sequence_number(domain, false);
1962 if (!NT_STATUS_IS_OK(status)) {
1965 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1967 /* We can't save the name to sid mapping here, as with sid history a
1968 * later name2sid would give the wrong sid. */
1973 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1974 TALLOC_CTX *mem_ctx,
1975 const DOM_SID *domain_sid,
1980 enum lsa_SidType **types)
1982 struct winbind_cache *cache = get_cache(domain);
1984 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1989 old_status = domain->online;
1990 *domain_name = NULL;
1998 if (num_rids == 0) {
1999 return NT_STATUS_OK;
2002 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2003 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2005 if ((*names == NULL) || (*types == NULL)) {
2006 result = NT_STATUS_NO_MEMORY;
2010 have_mapped = have_unmapped = false;
2012 for (i=0; i<num_rids; i++) {
2014 struct cache_entry *centry;
2017 if (!sid_compose(&sid, domain_sid, rids[i])) {
2018 result = NT_STATUS_INTERNAL_ERROR;
2022 centry = wcache_fetch(cache, domain, "SN/%s",
2023 sid_to_fstring(tmp, &sid));
2028 (*types)[i] = SID_NAME_UNKNOWN;
2029 (*names)[i] = talloc_strdup(*names, "");
2031 if (NT_STATUS_IS_OK(centry->status)) {
2034 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2036 dom = centry_string(centry, mem_ctx);
2037 if (*domain_name == NULL) {
2043 (*names)[i] = centry_string(centry, *names);
2045 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2046 have_unmapped = true;
2049 /* something's definitely wrong */
2050 result = centry->status;
2054 centry_free(centry);
2058 return NT_STATUS_NONE_MAPPED;
2060 if (!have_unmapped) {
2061 return NT_STATUS_OK;
2063 return STATUS_SOME_UNMAPPED;
2067 TALLOC_FREE(*names);
2068 TALLOC_FREE(*types);
2070 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2071 rids, num_rids, domain_name,
2074 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2075 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2076 if (!domain->internal && old_status) {
2077 set_domain_offline(domain);
2080 !domain->internal &&
2083 have_mapped = have_unmapped = false;
2085 for (i=0; i<num_rids; i++) {
2087 struct cache_entry *centry;
2090 if (!sid_compose(&sid, domain_sid, rids[i])) {
2091 result = NT_STATUS_INTERNAL_ERROR;
2095 centry = wcache_fetch(cache, domain, "SN/%s",
2096 sid_to_fstring(tmp, &sid));
2098 (*types)[i] = SID_NAME_UNKNOWN;
2099 (*names)[i] = talloc_strdup(*names, "");
2103 (*types)[i] = SID_NAME_UNKNOWN;
2104 (*names)[i] = talloc_strdup(*names, "");
2106 if (NT_STATUS_IS_OK(centry->status)) {
2109 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2111 dom = centry_string(centry, mem_ctx);
2112 if (*domain_name == NULL) {
2118 (*names)[i] = centry_string(centry, *names);
2120 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2121 have_unmapped = true;
2124 /* something's definitely wrong */
2125 result = centry->status;
2129 centry_free(centry);
2133 return NT_STATUS_NONE_MAPPED;
2135 if (!have_unmapped) {
2136 return NT_STATUS_OK;
2138 return STATUS_SOME_UNMAPPED;
2142 None of the queried rids has been found so save all negative entries
2144 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2145 for (i = 0; i < num_rids; i++) {
2147 const char *name = "";
2148 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2149 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2151 if (!sid_compose(&sid, domain_sid, rids[i])) {
2152 return NT_STATUS_INTERNAL_ERROR;
2155 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2163 Some or all of the queried rids have been found.
2165 if (!NT_STATUS_IS_OK(result) &&
2166 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2170 refresh_sequence_number(domain, false);
2172 for (i=0; i<num_rids; i++) {
2176 if (!sid_compose(&sid, domain_sid, rids[i])) {
2177 result = NT_STATUS_INTERNAL_ERROR;
2181 status = (*types)[i] == SID_NAME_UNKNOWN ?
2182 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2184 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185 (*names)[i], (*types)[i]);
2191 TALLOC_FREE(*names);
2192 TALLOC_FREE(*types);
2196 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2197 TALLOC_CTX *mem_ctx,
2198 const struct dom_sid *user_sid,
2199 struct wbint_userinfo *info)
2201 struct winbind_cache *cache = get_cache(domain);
2202 struct cache_entry *centry = NULL;
2206 if (cache->tdb == NULL) {
2207 return NT_STATUS_NOT_FOUND;
2210 sid_string = sid_string_tos(user_sid);
2211 if (sid_string == NULL) {
2212 return NT_STATUS_NO_MEMORY;
2215 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2216 TALLOC_FREE(sid_string);
2217 if (centry == NULL) {
2218 return NT_STATUS_NOT_FOUND;
2222 * If we have an access denied cache entry and a cached info3
2223 * in the samlogon cache then do a query. This will force the
2224 * rpc back end to return the info3 data.
2227 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2228 netsamlogon_cache_have(user_sid)) {
2229 DEBUG(10, ("query_user: cached access denied and have cached "
2231 domain->last_status = NT_STATUS_OK;
2232 centry_free(centry);
2233 return NT_STATUS_NOT_FOUND;
2236 /* if status is not ok then this is a negative hit
2237 and the rest of the data doesn't matter */
2238 status = centry->status;
2239 if (NT_STATUS_IS_OK(status)) {
2240 info->acct_name = centry_string(centry, mem_ctx);
2241 info->full_name = centry_string(centry, mem_ctx);
2242 info->homedir = centry_string(centry, mem_ctx);
2243 info->shell = centry_string(centry, mem_ctx);
2244 info->primary_gid = centry_uint32(centry);
2245 centry_sid(centry, &info->user_sid);
2246 centry_sid(centry, &info->group_sid);
2249 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2250 "%s\n", domain->name, nt_errstr(status) ));
2252 centry_free(centry);
2256 /* Lookup user information from a rid */
2257 static NTSTATUS query_user(struct winbindd_domain *domain,
2258 TALLOC_CTX *mem_ctx,
2259 const DOM_SID *user_sid,
2260 struct wbint_userinfo *info)
2265 old_status = domain->online;
2266 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2267 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2273 /* Return status value returned by seq number check */
2275 if (!NT_STATUS_IS_OK(domain->last_status))
2276 return domain->last_status;
2278 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2281 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2283 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2284 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2285 if (!domain->internal && old_status) {
2286 set_domain_offline(domain);
2288 if (!domain->internal &&
2291 NTSTATUS cache_status;
2292 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2293 return cache_status;
2297 refresh_sequence_number(domain, false);
2298 if (!NT_STATUS_IS_OK(status)) {
2301 wcache_save_user(domain, status, info);
2306 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2307 TALLOC_CTX *mem_ctx,
2308 const struct dom_sid *user_sid,
2309 uint32_t *pnum_sids,
2310 struct dom_sid **psids)
2312 struct winbind_cache *cache = get_cache(domain);
2313 struct cache_entry *centry = NULL;
2315 uint32_t i, num_sids;
2316 struct dom_sid *sids;
2319 if (cache->tdb == NULL) {
2320 return NT_STATUS_NOT_FOUND;
2323 centry = wcache_fetch(cache, domain, "UG/%s",
2324 sid_to_fstring(sid_string, user_sid));
2325 if (centry == NULL) {
2326 return NT_STATUS_NOT_FOUND;
2329 /* If we have an access denied cache entry and a cached info3 in the
2330 samlogon cache then do a query. This will force the rpc back end
2331 to return the info3 data. */
2333 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2334 && netsamlogon_cache_have(user_sid)) {
2335 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2337 domain->last_status = NT_STATUS_OK;
2338 centry_free(centry);
2339 return NT_STATUS_NOT_FOUND;
2342 num_sids = centry_uint32(centry);
2343 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2345 centry_free(centry);
2346 return NT_STATUS_NO_MEMORY;
2349 for (i=0; i<num_sids; i++) {
2350 centry_sid(centry, &sids[i]);
2353 status = centry->status;
2355 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2356 "status: %s\n", domain->name, nt_errstr(status)));
2358 centry_free(centry);
2360 *pnum_sids = num_sids;
2365 /* Lookup groups a user is a member of. */
2366 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2367 TALLOC_CTX *mem_ctx,
2368 const DOM_SID *user_sid,
2369 uint32 *num_groups, DOM_SID **user_gids)
2371 struct cache_entry *centry = NULL;
2377 old_status = domain->online;
2378 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2379 num_groups, user_gids);
2380 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2385 (*user_gids) = NULL;
2387 /* Return status value returned by seq number check */
2389 if (!NT_STATUS_IS_OK(domain->last_status))
2390 return domain->last_status;
2392 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2395 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2397 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2398 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2399 if (!domain->internal && old_status) {
2400 set_domain_offline(domain);
2402 if (!domain->internal &&
2405 NTSTATUS cache_status;
2406 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2407 num_groups, user_gids);
2408 return cache_status;
2411 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2415 refresh_sequence_number(domain, false);
2416 if (!NT_STATUS_IS_OK(status)) {
2419 centry = centry_start(domain, status);
2423 centry_put_uint32(centry, *num_groups);
2424 for (i=0; i<(*num_groups); i++) {
2425 centry_put_sid(centry, &(*user_gids)[i]);
2428 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2429 centry_free(centry);
2435 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2436 const struct dom_sid *sids)
2441 sidlist = talloc_strdup(mem_ctx, "");
2442 if (sidlist == NULL) {
2445 for (i=0; i<num_sids; i++) {
2447 sidlist = talloc_asprintf_append_buffer(
2448 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2449 if (sidlist == NULL) {
2456 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2457 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2458 const struct dom_sid *sids,
2459 uint32_t *pnum_aliases, uint32_t **paliases)
2461 struct winbind_cache *cache = get_cache(domain);
2462 struct cache_entry *centry = NULL;
2463 uint32_t num_aliases;
2469 if (cache->tdb == NULL) {
2470 return NT_STATUS_NOT_FOUND;
2473 if (num_sids == 0) {
2476 return NT_STATUS_OK;
2479 /* We need to cache indexed by the whole list of SIDs, the aliases
2480 * resulting might come from any of the SIDs. */
2482 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2483 if (sidlist == NULL) {
2484 return NT_STATUS_NO_MEMORY;
2487 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2488 TALLOC_FREE(sidlist);
2489 if (centry == NULL) {
2490 return NT_STATUS_NOT_FOUND;
2493 num_aliases = centry_uint32(centry);
2494 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2495 if (aliases == NULL) {
2496 centry_free(centry);
2497 return NT_STATUS_NO_MEMORY;
2500 for (i=0; i<num_aliases; i++) {
2501 aliases[i] = centry_uint32(centry);
2504 status = centry->status;
2506 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2507 "status %s\n", domain->name, nt_errstr(status)));
2509 centry_free(centry);
2511 *pnum_aliases = num_aliases;
2512 *paliases = aliases;
2517 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2518 TALLOC_CTX *mem_ctx,
2519 uint32 num_sids, const DOM_SID *sids,
2520 uint32 *num_aliases, uint32 **alias_rids)
2522 struct cache_entry *centry = NULL;
2528 old_status = domain->online;
2529 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2530 num_aliases, alias_rids);
2531 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2536 (*alias_rids) = NULL;
2538 if (!NT_STATUS_IS_OK(domain->last_status))
2539 return domain->last_status;
2541 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2542 "for domain %s\n", domain->name ));
2544 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2545 if (sidlist == NULL) {
2546 return NT_STATUS_NO_MEMORY;
2549 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2551 num_aliases, alias_rids);
2553 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2554 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2555 if (!domain->internal && old_status) {
2556 set_domain_offline(domain);
2558 if (!domain->internal &&
2561 NTSTATUS cache_status;
2562 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2563 sids, num_aliases, alias_rids);
2564 return cache_status;
2568 refresh_sequence_number(domain, false);
2569 if (!NT_STATUS_IS_OK(status)) {
2572 centry = centry_start(domain, status);
2575 centry_put_uint32(centry, *num_aliases);
2576 for (i=0; i<(*num_aliases); i++)
2577 centry_put_uint32(centry, (*alias_rids)[i]);
2578 centry_end(centry, "UA%s", sidlist);
2579 centry_free(centry);
2585 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2586 TALLOC_CTX *mem_ctx,
2587 const struct dom_sid *group_sid,
2588 uint32_t *num_names,
2589 struct dom_sid **sid_mem, char ***names,
2590 uint32_t **name_types)
2592 struct winbind_cache *cache = get_cache(domain);
2593 struct cache_entry *centry = NULL;
2598 if (cache->tdb == NULL) {
2599 return NT_STATUS_NOT_FOUND;
2602 sid_string = sid_string_tos(group_sid);
2603 if (sid_string == NULL) {
2604 return NT_STATUS_NO_MEMORY;
2607 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2608 TALLOC_FREE(sid_string);
2609 if (centry == NULL) {
2610 return NT_STATUS_NOT_FOUND;
2617 *num_names = centry_uint32(centry);
2618 if (*num_names == 0) {
2619 centry_free(centry);
2620 return NT_STATUS_OK;
2623 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2624 *names = talloc_array(mem_ctx, char *, *num_names);
2625 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2627 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2628 TALLOC_FREE(*sid_mem);
2629 TALLOC_FREE(*names);
2630 TALLOC_FREE(*name_types);
2631 centry_free(centry);
2632 return NT_STATUS_NO_MEMORY;
2635 for (i=0; i<(*num_names); i++) {
2636 centry_sid(centry, &(*sid_mem)[i]);
2637 (*names)[i] = centry_string(centry, mem_ctx);
2638 (*name_types)[i] = centry_uint32(centry);
2641 status = centry->status;
2643 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2644 "status: %s\n", domain->name, nt_errstr(status)));
2646 centry_free(centry);
2650 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2651 TALLOC_CTX *mem_ctx,
2652 const DOM_SID *group_sid,
2653 enum lsa_SidType type,
2655 DOM_SID **sid_mem, char ***names,
2656 uint32 **name_types)
2658 struct cache_entry *centry = NULL;
2664 old_status = domain->online;
2665 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2666 sid_mem, names, name_types);
2667 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2674 (*name_types) = NULL;
2676 /* Return status value returned by seq number check */
2678 if (!NT_STATUS_IS_OK(domain->last_status))
2679 return domain->last_status;
2681 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2684 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2686 sid_mem, names, name_types);
2688 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2689 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2690 if (!domain->internal && old_status) {
2691 set_domain_offline(domain);
2693 if (!domain->internal &&
2696 NTSTATUS cache_status;
2697 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2698 num_names, sid_mem, names,
2700 return cache_status;
2704 refresh_sequence_number(domain, false);
2705 if (!NT_STATUS_IS_OK(status)) {
2708 centry = centry_start(domain, status);
2711 centry_put_uint32(centry, *num_names);
2712 for (i=0; i<(*num_names); i++) {
2713 centry_put_sid(centry, &(*sid_mem)[i]);
2714 centry_put_string(centry, (*names)[i]);
2715 centry_put_uint32(centry, (*name_types)[i]);
2717 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2718 centry_free(centry);
2724 /* find the sequence number for a domain */
2725 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2727 refresh_sequence_number(domain, false);
2729 *seq = domain->sequence_number;
2731 return NT_STATUS_OK;
2734 /* enumerate trusted domains
2735 * (we need to have the list of trustdoms in the cache when we go offline) -
2737 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2738 TALLOC_CTX *mem_ctx,
2739 struct netr_DomainTrustList *trusts)
2742 struct winbind_cache *cache;
2743 struct winbindd_tdc_domain *dom_list = NULL;
2744 size_t num_domains = 0;
2745 bool retval = false;
2749 old_status = domain->online;
2751 trusts->array = NULL;
2753 cache = get_cache(domain);
2754 if (!cache || !cache->tdb) {
2758 if (domain->online) {
2762 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2763 if (!retval || !num_domains || !dom_list) {
2764 TALLOC_FREE(dom_list);
2769 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2770 if (!trusts->array) {
2771 TALLOC_FREE(dom_list);
2772 return NT_STATUS_NO_MEMORY;
2775 for (i = 0; i < num_domains; i++) {
2776 struct netr_DomainTrust *trust;
2777 struct dom_sid *sid;
2778 struct winbindd_domain *dom;
2780 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2781 if (dom && dom->internal) {
2785 trust = &trusts->array[trusts->count];
2786 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2787 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2788 sid = talloc(trusts->array, struct dom_sid);
2789 if (!trust->netbios_name || !trust->dns_name ||
2791 TALLOC_FREE(dom_list);
2792 TALLOC_FREE(trusts->array);
2793 return NT_STATUS_NO_MEMORY;
2796 trust->trust_flags = dom_list[i].trust_flags;
2797 trust->trust_attributes = dom_list[i].trust_attribs;
2798 trust->trust_type = dom_list[i].trust_type;
2799 sid_copy(sid, &dom_list[i].sid);
2804 TALLOC_FREE(dom_list);
2805 return NT_STATUS_OK;
2808 /* Return status value returned by seq number check */
2810 if (!NT_STATUS_IS_OK(domain->last_status))
2811 return domain->last_status;
2813 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2816 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2818 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2819 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2820 if (!domain->internal && old_status) {
2821 set_domain_offline(domain);
2823 if (!domain->internal &&
2826 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2827 if (retval && num_domains && dom_list) {
2828 TALLOC_FREE(trusts->array);
2830 goto do_fetch_cache;
2834 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2835 * so that the generic centry handling still applies correctly -
2838 if (!NT_STATUS_IS_ERR(status)) {
2839 status = NT_STATUS_OK;
2844 /* get lockout policy */
2845 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2846 TALLOC_CTX *mem_ctx,
2847 struct samr_DomInfo12 *policy)
2849 struct winbind_cache *cache = get_cache(domain);
2850 struct cache_entry *centry = NULL;
2854 old_status = domain->online;
2858 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2864 policy->lockout_duration = centry_nttime(centry);
2865 policy->lockout_window = centry_nttime(centry);
2866 policy->lockout_threshold = centry_uint16(centry);
2868 status = centry->status;
2870 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2871 domain->name, nt_errstr(status) ));
2873 centry_free(centry);
2877 ZERO_STRUCTP(policy);
2879 /* Return status value returned by seq number check */
2881 if (!NT_STATUS_IS_OK(domain->last_status))
2882 return domain->last_status;
2884 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2887 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2889 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2890 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2891 if (!domain->internal && old_status) {
2892 set_domain_offline(domain);
2895 !domain->internal &&
2898 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2900 goto do_fetch_cache;
2905 refresh_sequence_number(domain, false);
2906 if (!NT_STATUS_IS_OK(status)) {
2909 wcache_save_lockout_policy(domain, status, policy);
2914 /* get password policy */
2915 static NTSTATUS password_policy(struct winbindd_domain *domain,
2916 TALLOC_CTX *mem_ctx,
2917 struct samr_DomInfo1 *policy)
2919 struct winbind_cache *cache = get_cache(domain);
2920 struct cache_entry *centry = NULL;
2924 old_status = domain->online;
2928 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2934 policy->min_password_length = centry_uint16(centry);
2935 policy->password_history_length = centry_uint16(centry);
2936 policy->password_properties = centry_uint32(centry);
2937 policy->max_password_age = centry_nttime(centry);
2938 policy->min_password_age = centry_nttime(centry);
2940 status = centry->status;
2942 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2943 domain->name, nt_errstr(status) ));
2945 centry_free(centry);
2949 ZERO_STRUCTP(policy);
2951 /* Return status value returned by seq number check */
2953 if (!NT_STATUS_IS_OK(domain->last_status))
2954 return domain->last_status;
2956 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2959 status = domain->backend->password_policy(domain, mem_ctx, policy);
2961 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2962 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2963 if (!domain->internal && old_status) {
2964 set_domain_offline(domain);
2967 !domain->internal &&
2970 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2972 goto do_fetch_cache;
2977 refresh_sequence_number(domain, false);
2978 if (!NT_STATUS_IS_OK(status)) {
2981 wcache_save_password_policy(domain, status, policy);
2987 /* Invalidate cached user and group lists coherently */
2989 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2992 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2993 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2994 tdb_delete(the_tdb, kbuf);
2999 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3001 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3002 struct netr_SamInfo3 *info3)
3005 fstring key_str, sid_string;
3006 struct winbind_cache *cache;
3008 /* dont clear cached U/SID and UG/SID entries when we want to logon
3011 if (lp_winbind_offline_logon()) {
3018 cache = get_cache(domain);
3024 sid_copy(&sid, info3->base.domain_sid);
3025 sid_append_rid(&sid, info3->base.rid);
3027 /* Clear U/SID cache entry */
3028 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
3029 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3030 tdb_delete(cache->tdb, string_tdb_data(key_str));
3032 /* Clear UG/SID cache entry */
3033 fstr_sprintf(key_str, "UG/%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 /* Samba/winbindd never needs this. */
3038 netsamlogon_clear_cached_user(info3);
3041 bool wcache_invalidate_cache(void)
3043 struct winbindd_domain *domain;
3045 for (domain = domain_list(); domain; domain = domain->next) {
3046 struct winbind_cache *cache = get_cache(domain);
3048 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3049 "entries for %s\n", domain->name));
3052 tdb_traverse(cache->tdb, traverse_fn, NULL);
3061 bool init_wcache(void)
3063 if (wcache == NULL) {
3064 wcache = SMB_XMALLOC_P(struct winbind_cache);
3065 ZERO_STRUCTP(wcache);
3068 if (wcache->tdb != NULL)
3071 /* when working offline we must not clear the cache on restart */
3072 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3073 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3074 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3075 O_RDWR|O_CREAT, 0600);
3077 if (wcache->tdb == NULL) {
3078 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3085 /************************************************************************
3086 This is called by the parent to initialize the cache file.
3087 We don't need sophisticated locking here as we know we're the
3089 ************************************************************************/
3091 bool initialize_winbindd_cache(void)
3093 bool cache_bad = true;
3096 if (!init_wcache()) {
3097 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3101 /* Check version number. */
3102 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3103 vers == WINBINDD_CACHE_VERSION) {
3108 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3109 "and re-creating with version number %d\n",
3110 WINBINDD_CACHE_VERSION ));
3112 tdb_close(wcache->tdb);
3115 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3116 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3117 cache_path("winbindd_cache.tdb"),
3121 if (!init_wcache()) {
3122 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3123 "init_wcache failed.\n"));
3127 /* Write the version. */
3128 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3129 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3130 tdb_errorstr(wcache->tdb) ));
3135 tdb_close(wcache->tdb);
3140 void close_winbindd_cache(void)
3146 tdb_close(wcache->tdb);
3151 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
3152 char **domain_name, char **name,
3153 enum lsa_SidType *type)
3155 struct winbindd_domain *domain;
3158 domain = find_lookup_domain_from_sid(sid);
3159 if (domain == NULL) {
3162 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3164 return NT_STATUS_IS_OK(status);
3167 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3168 const char *domain_name,
3171 enum lsa_SidType *type)
3173 struct winbindd_domain *domain;
3175 bool original_online_state;
3177 domain = find_lookup_domain_from_name(domain_name);
3178 if (domain == NULL) {
3182 /* If we are doing a cached logon, temporarily set the domain
3183 offline so the cache won't expire the entry */
3185 original_online_state = domain->online;
3186 domain->online = false;
3187 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3188 domain->online = original_online_state;
3190 return NT_STATUS_IS_OK(status);
3193 void cache_name2sid(struct winbindd_domain *domain,
3194 const char *domain_name, const char *name,
3195 enum lsa_SidType type, const DOM_SID *sid)
3197 refresh_sequence_number(domain, false);
3198 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3203 * The original idea that this cache only contains centries has
3204 * been blurred - now other stuff gets put in here. Ensure we
3205 * ignore these things on cleanup.
3208 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3209 TDB_DATA dbuf, void *state)
3211 struct cache_entry *centry;
3213 if (is_non_centry_key(kbuf)) {
3217 centry = wcache_fetch_raw((char *)kbuf.dptr);
3222 if (!NT_STATUS_IS_OK(centry->status)) {
3223 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3224 tdb_delete(the_tdb, kbuf);
3227 centry_free(centry);
3231 /* flush the cache */
3232 void wcache_flush_cache(void)
3237 tdb_close(wcache->tdb);
3240 if (!winbindd_use_cache()) {
3244 /* when working offline we must not clear the cache on restart */
3245 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3246 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3247 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3248 O_RDWR|O_CREAT, 0600);
3251 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3255 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3257 DEBUG(10,("wcache_flush_cache success\n"));
3260 /* Count cached creds */
3262 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3265 int *cred_count = (int*)state;
3267 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3273 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3275 struct winbind_cache *cache = get_cache(domain);
3280 return NT_STATUS_INTERNAL_DB_ERROR;
3283 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3285 return NT_STATUS_OK;
3289 struct cred_list *prev, *next;
3294 static struct cred_list *wcache_cred_list;
3296 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3299 struct cred_list *cred;
3301 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3303 cred = SMB_MALLOC_P(struct cred_list);
3305 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3311 /* save a copy of the key */
3313 fstrcpy(cred->name, (const char *)kbuf.dptr);
3314 DLIST_ADD(wcache_cred_list, cred);
3320 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
3322 struct winbind_cache *cache = get_cache(domain);
3325 struct cred_list *cred, *oldest = NULL;
3328 return NT_STATUS_INTERNAL_DB_ERROR;
3331 /* we possibly already have an entry */
3332 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3334 fstring key_str, tmp;
3336 DEBUG(11,("we already have an entry, deleting that\n"));
3338 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3340 tdb_delete(cache->tdb, string_tdb_data(key_str));
3342 return NT_STATUS_OK;
3345 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3347 return NT_STATUS_OK;
3348 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3349 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3352 ZERO_STRUCTP(oldest);
3354 for (cred = wcache_cred_list; cred; cred = cred->next) {
3359 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3361 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3363 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3367 t = IVAL(data.dptr, 0);
3368 SAFE_FREE(data.dptr);
3371 oldest = SMB_MALLOC_P(struct cred_list);
3372 if (oldest == NULL) {
3373 status = NT_STATUS_NO_MEMORY;
3377 fstrcpy(oldest->name, cred->name);
3378 oldest->created = t;
3382 if (t < oldest->created) {
3383 fstrcpy(oldest->name, cred->name);
3384 oldest->created = t;
3388 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3389 status = NT_STATUS_OK;
3391 status = NT_STATUS_UNSUCCESSFUL;
3394 SAFE_FREE(wcache_cred_list);
3400 /* Change the global online/offline state. */
3401 bool set_global_winbindd_state_offline(void)
3405 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3407 /* Only go offline if someone has created
3408 the key "WINBINDD_OFFLINE" in the cache tdb. */
3410 if (wcache == NULL || wcache->tdb == NULL) {
3411 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3415 if (!lp_winbind_offline_logon()) {
3416 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3420 if (global_winbindd_offline_state) {
3421 /* Already offline. */
3425 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3427 if (!data.dptr || data.dsize != 4) {
3428 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3429 SAFE_FREE(data.dptr);
3432 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3433 global_winbindd_offline_state = true;
3434 SAFE_FREE(data.dptr);
3439 void set_global_winbindd_state_online(void)
3441 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3443 if (!lp_winbind_offline_logon()) {
3444 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3448 if (!global_winbindd_offline_state) {
3449 /* Already online. */
3452 global_winbindd_offline_state = false;
3458 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3459 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3462 bool get_global_winbindd_state_offline(void)
3464 return global_winbindd_offline_state;
3467 /***********************************************************************
3468 Validate functions for all possible cache tdb keys.
3469 ***********************************************************************/
3471 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3472 struct tdb_validation_status *state)
3474 struct cache_entry *centry;
3476 centry = SMB_XMALLOC_P(struct cache_entry);
3477 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3478 if (!centry->data) {
3482 centry->len = data.dsize;
3485 if (centry->len < 16) {
3486 /* huh? corrupt cache? */
3487 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3488 "(len < 16) ?\n", kstr));
3489 centry_free(centry);
3490 state->bad_entry = true;
3491 state->success = false;
3495 centry->status = NT_STATUS(centry_uint32(centry));
3496 centry->sequence_number = centry_uint32(centry);
3497 centry->timeout = centry_uint64(centry);
3501 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3502 struct tdb_validation_status *state)
3504 if (dbuf.dsize != 8) {
3505 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3506 keystr, (unsigned int)dbuf.dsize ));
3507 state->bad_entry = true;
3513 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3514 struct tdb_validation_status *state)
3516 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3521 (void)centry_uint32(centry);
3522 if (NT_STATUS_IS_OK(centry->status)) {
3524 (void)centry_sid(centry, &sid);
3527 centry_free(centry);
3529 if (!(state->success)) {
3532 DEBUG(10,("validate_ns: %s ok\n", keystr));
3536 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3537 struct tdb_validation_status *state)
3539 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3544 if (NT_STATUS_IS_OK(centry->status)) {
3545 (void)centry_uint32(centry);
3546 (void)centry_string(centry, mem_ctx);
3547 (void)centry_string(centry, mem_ctx);
3550 centry_free(centry);
3552 if (!(state->success)) {
3555 DEBUG(10,("validate_sn: %s ok\n", keystr));
3559 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3560 struct tdb_validation_status *state)
3562 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3569 (void)centry_string(centry, mem_ctx);
3570 (void)centry_string(centry, mem_ctx);
3571 (void)centry_string(centry, mem_ctx);
3572 (void)centry_string(centry, mem_ctx);
3573 (void)centry_uint32(centry);
3574 (void)centry_sid(centry, &sid);
3575 (void)centry_sid(centry, &sid);
3577 centry_free(centry);
3579 if (!(state->success)) {
3582 DEBUG(10,("validate_u: %s ok\n", keystr));
3586 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3587 struct tdb_validation_status *state)
3589 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3595 (void)centry_nttime(centry);
3596 (void)centry_nttime(centry);
3597 (void)centry_uint16(centry);
3599 centry_free(centry);
3601 if (!(state->success)) {
3604 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3608 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3609 struct tdb_validation_status *state)
3611 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3617 (void)centry_uint16(centry);
3618 (void)centry_uint16(centry);
3619 (void)centry_uint32(centry);
3620 (void)centry_nttime(centry);
3621 (void)centry_nttime(centry);
3623 centry_free(centry);
3625 if (!(state->success)) {
3628 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3632 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3633 struct tdb_validation_status *state)
3635 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3641 (void)centry_time(centry);
3642 (void)centry_hash16(centry, mem_ctx);
3644 /* We only have 17 bytes more data in the salted cred case. */
3645 if (centry->len - centry->ofs == 17) {
3646 (void)centry_hash16(centry, mem_ctx);
3649 centry_free(centry);
3651 if (!(state->success)) {
3654 DEBUG(10,("validate_cred: %s ok\n", keystr));
3658 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3659 struct tdb_validation_status *state)
3661 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3662 int32 num_entries, i;
3668 num_entries = (int32)centry_uint32(centry);
3670 for (i=0; i< num_entries; i++) {
3672 (void)centry_string(centry, mem_ctx);
3673 (void)centry_string(centry, mem_ctx);
3674 (void)centry_string(centry, mem_ctx);
3675 (void)centry_string(centry, mem_ctx);
3676 (void)centry_sid(centry, &sid);
3677 (void)centry_sid(centry, &sid);
3680 centry_free(centry);
3682 if (!(state->success)) {
3685 DEBUG(10,("validate_ul: %s ok\n", keystr));
3689 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3690 struct tdb_validation_status *state)
3692 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3693 int32 num_entries, i;
3699 num_entries = centry_uint32(centry);
3701 for (i=0; i< num_entries; i++) {
3702 (void)centry_string(centry, mem_ctx);
3703 (void)centry_string(centry, mem_ctx);
3704 (void)centry_uint32(centry);
3707 centry_free(centry);
3709 if (!(state->success)) {
3712 DEBUG(10,("validate_gl: %s ok\n", keystr));
3716 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3717 struct tdb_validation_status *state)
3719 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3720 int32 num_groups, i;
3726 num_groups = centry_uint32(centry);
3728 for (i=0; i< num_groups; i++) {
3730 centry_sid(centry, &sid);
3733 centry_free(centry);
3735 if (!(state->success)) {
3738 DEBUG(10,("validate_ug: %s ok\n", keystr));
3742 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3743 struct tdb_validation_status *state)
3745 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3746 int32 num_aliases, i;
3752 num_aliases = centry_uint32(centry);
3754 for (i=0; i < num_aliases; i++) {
3755 (void)centry_uint32(centry);
3758 centry_free(centry);
3760 if (!(state->success)) {
3763 DEBUG(10,("validate_ua: %s ok\n", keystr));
3767 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3768 struct tdb_validation_status *state)
3770 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3777 num_names = centry_uint32(centry);
3779 for (i=0; i< num_names; i++) {
3781 centry_sid(centry, &sid);
3782 (void)centry_string(centry, mem_ctx);
3783 (void)centry_uint32(centry);
3786 centry_free(centry);
3788 if (!(state->success)) {
3791 DEBUG(10,("validate_gm: %s ok\n", keystr));
3795 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3796 struct tdb_validation_status *state)
3798 /* Can't say anything about this other than must be nonzero. */
3799 if (dbuf.dsize == 0) {
3800 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3802 state->bad_entry = true;
3803 state->success = false;
3807 DEBUG(10,("validate_dr: %s ok\n", keystr));
3811 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3812 struct tdb_validation_status *state)
3814 /* Can't say anything about this other than must be nonzero. */
3815 if (dbuf.dsize == 0) {
3816 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3818 state->bad_entry = true;
3819 state->success = false;
3823 DEBUG(10,("validate_de: %s ok\n", keystr));
3827 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3828 TDB_DATA dbuf, struct tdb_validation_status *state)
3830 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3836 (void)centry_string(centry, mem_ctx);
3837 (void)centry_string(centry, mem_ctx);
3838 (void)centry_string(centry, mem_ctx);
3839 (void)centry_uint32(centry);
3841 centry_free(centry);
3843 if (!(state->success)) {
3846 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3850 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3852 struct tdb_validation_status *state)
3854 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3860 (void)centry_string( centry, mem_ctx );
3862 centry_free(centry);
3864 if (!(state->success)) {
3867 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3871 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3873 struct tdb_validation_status *state)
3875 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3881 (void)centry_string( centry, mem_ctx );
3883 centry_free(centry);
3885 if (!(state->success)) {
3888 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3892 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3894 struct tdb_validation_status *state)
3896 if (dbuf.dsize == 0) {
3897 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3898 "key %s (len ==0) ?\n", keystr));
3899 state->bad_entry = true;
3900 state->success = false;
3904 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3905 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3909 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3910 struct tdb_validation_status *state)
3912 if (dbuf.dsize != 4) {
3913 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3914 keystr, (unsigned int)dbuf.dsize ));
3915 state->bad_entry = true;
3916 state->success = false;
3919 DEBUG(10,("validate_offline: %s ok\n", keystr));
3923 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3924 struct tdb_validation_status *state)
3927 * Ignore validation for now. The proper way to do this is with a
3928 * checksum. Just pure parsing does not really catch much.
3933 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3934 struct tdb_validation_status *state)
3936 if (dbuf.dsize != 4) {
3937 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3938 "key %s (len %u != 4) ?\n",
3939 keystr, (unsigned int)dbuf.dsize));
3940 state->bad_entry = true;
3941 state->success = false;
3945 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3949 /***********************************************************************
3950 A list of all possible cache tdb keys with associated validation
3952 ***********************************************************************/
3954 struct key_val_struct {
3955 const char *keyname;
3956 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3958 {"SEQNUM/", validate_seqnum},
3959 {"NS/", validate_ns},
3960 {"SN/", validate_sn},
3962 {"LOC_POL/", validate_loc_pol},
3963 {"PWD_POL/", validate_pwd_pol},
3964 {"CRED/", validate_cred},
3965 {"UL/", validate_ul},
3966 {"GL/", validate_gl},
3967 {"UG/", validate_ug},
3968 {"UA", validate_ua},
3969 {"GM/", validate_gm},
3970 {"DR/", validate_dr},
3971 {"DE/", validate_de},
3972 {"NSS/PWINFO/", validate_pwinfo},
3973 {"TRUSTDOMCACHE/", validate_trustdomcache},
3974 {"NSS/NA/", validate_nss_na},
3975 {"NSS/AN/", validate_nss_an},
3976 {"WINBINDD_OFFLINE", validate_offline},
3977 {"NDR/", validate_ndr},
3978 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3982 /***********************************************************************
3983 Function to look at every entry in the tdb and validate it as far as
3985 ***********************************************************************/
3987 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3990 unsigned int max_key_len = 1024;
3991 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3993 /* Paranoia check. */
3994 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3995 max_key_len = 1024 * 1024;
3997 if (kbuf.dsize > max_key_len) {
3998 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4000 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4004 for (i = 0; key_val[i].keyname; i++) {
4005 size_t namelen = strlen(key_val[i].keyname);
4006 if (kbuf.dsize >= namelen && (
4007 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4008 TALLOC_CTX *mem_ctx;
4012 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4016 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4017 keystr[kbuf.dsize] = '\0';
4019 mem_ctx = talloc_init("validate_ctx");
4025 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4029 talloc_destroy(mem_ctx);
4034 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4035 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4036 DEBUG(0,("data :\n"));
4037 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4038 v_state->unknown_key = true;
4039 v_state->success = false;
4040 return 1; /* terminate. */
4043 static void validate_panic(const char *const why)
4045 DEBUG(0,("validating cache: would panic %s\n", why ));
4046 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4050 static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4058 if (is_non_centry_key(key)) {
4062 if (data.dptr == NULL || data.dsize == 0) {
4063 if (tdb_delete(tdb, key) < 0) {
4064 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4070 /* add timeout to blob (uint64_t) */
4071 blob.dsize = data.dsize + 8;
4073 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4074 if (blob.dptr == NULL) {
4077 memset(blob.dptr, 0, blob.dsize);
4079 /* copy status and seqnum */
4080 memcpy(blob.dptr, data.dptr, 8);
4083 ctimeout = lp_winbind_cache_time() + time(NULL);
4084 SBVAL(blob.dptr, 8, ctimeout);
4087 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4089 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4090 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4092 SAFE_FREE(blob.dptr);
4096 SAFE_FREE(blob.dptr);
4100 static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4104 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4106 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4114 /***********************************************************************
4115 Try and validate every entry in the winbindd cache. If we fail here,
4116 delete the cache tdb and return non-zero.
4117 ***********************************************************************/
4119 int winbindd_validate_cache(void)
4122 const char *tdb_path = cache_path("winbindd_cache.tdb");
4123 TDB_CONTEXT *tdb = NULL;
4127 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4128 smb_panic_fn = validate_panic;
4130 tdb = tdb_open_log(tdb_path,
4131 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4132 ( lp_winbind_offline_logon()
4134 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4138 DEBUG(0, ("winbindd_validate_cache: "
4139 "error opening/initializing tdb\n"));
4143 /* Version check and upgrade code. */
4144 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4145 DEBUG(10, ("Fresh database\n"));
4146 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4147 vers_id = WINBINDD_CACHE_VERSION;
4150 if (vers_id != WINBINDD_CACHE_VERSION) {
4151 if (vers_id == WINBINDD_CACHE_VER1) {
4152 ok = wbcache_upgrade_v1_to_v2(tdb);
4154 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4159 tdb_store_uint32(tdb,
4160 WINBINDD_CACHE_VERSION_KEYSTR,
4161 WINBINDD_CACHE_VERSION);
4162 vers_id = WINBINDD_CACHE_VER2;
4168 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4171 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4172 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4177 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4178 smb_panic_fn = smb_panic;
4182 /***********************************************************************
4183 Try and validate every entry in the winbindd cache.
4184 ***********************************************************************/
4186 int winbindd_validate_cache_nobackup(void)
4189 const char *tdb_path = cache_path("winbindd_cache.tdb");
4191 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4192 smb_panic_fn = validate_panic;
4195 if (wcache == NULL || wcache->tdb == NULL) {
4196 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4198 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4202 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4206 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4208 smb_panic_fn = smb_panic;
4212 bool winbindd_cache_validate_and_initialize(void)
4214 close_winbindd_cache();
4216 if (lp_winbind_offline_logon()) {
4217 if (winbindd_validate_cache() < 0) {
4218 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4219 "could be restored.\n"));
4223 return initialize_winbindd_cache();
4226 /*********************************************************************
4227 ********************************************************************/
4229 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4230 struct winbindd_tdc_domain **domains,
4231 size_t *num_domains )
4233 struct winbindd_tdc_domain *list = NULL;
4236 bool set_only = false;
4238 /* don't allow duplicates */
4243 for ( i=0; i< (*num_domains); i++ ) {
4244 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4245 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4256 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4259 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4260 struct winbindd_tdc_domain,
4265 ZERO_STRUCT( list[idx] );
4271 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4272 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4274 if ( !is_null_sid( &new_dom->sid ) ) {
4275 sid_copy( &list[idx].sid, &new_dom->sid );
4277 sid_copy(&list[idx].sid, &global_sid_NULL);
4280 if ( new_dom->domain_flags != 0x0 )
4281 list[idx].trust_flags = new_dom->domain_flags;
4283 if ( new_dom->domain_type != 0x0 )
4284 list[idx].trust_type = new_dom->domain_type;
4286 if ( new_dom->domain_trust_attribs != 0x0 )
4287 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4291 *num_domains = idx + 1;
4297 /*********************************************************************
4298 ********************************************************************/
4300 static TDB_DATA make_tdc_key( const char *domain_name )
4302 char *keystr = NULL;
4303 TDB_DATA key = { NULL, 0 };
4305 if ( !domain_name ) {
4306 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4310 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4313 key = string_term_tdb_data(keystr);
4318 /*********************************************************************
4319 ********************************************************************/
4321 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4323 unsigned char **buf )
4325 unsigned char *buffer = NULL;
4330 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4338 /* Store the number of array items first */
4339 len += tdb_pack( buffer+len, buflen-len, "d",
4342 /* now pack each domain trust record */
4343 for ( i=0; i<num_domains; i++ ) {
4348 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4349 domains[i].domain_name,
4350 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4353 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4354 domains[i].domain_name,
4355 domains[i].dns_name,
4356 sid_to_fstring(tmp, &domains[i].sid),
4357 domains[i].trust_flags,
4358 domains[i].trust_attribs,
4359 domains[i].trust_type );
4362 if ( buflen < len ) {
4364 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4365 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4379 /*********************************************************************
4380 ********************************************************************/
4382 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4383 struct winbindd_tdc_domain **domains )
4385 fstring domain_name, dns_name, sid_string;
4386 uint32 type, attribs, flags;
4390 struct winbindd_tdc_domain *list = NULL;
4392 /* get the number of domains */
4393 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4395 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4399 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4401 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4405 for ( i=0; i<num_domains; i++ ) {
4406 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4415 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4416 TALLOC_FREE( list );
4420 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4421 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4422 domain_name, dns_name, sid_string,
4423 flags, attribs, type));
4425 list[i].domain_name = talloc_strdup( list, domain_name );
4426 list[i].dns_name = talloc_strdup( list, dns_name );
4427 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4428 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4431 list[i].trust_flags = flags;
4432 list[i].trust_attribs = attribs;
4433 list[i].trust_type = type;
4441 /*********************************************************************
4442 ********************************************************************/
4444 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4446 TDB_DATA key = make_tdc_key( lp_workgroup() );
4447 TDB_DATA data = { NULL, 0 };
4453 /* See if we were asked to delete the cache entry */
4456 ret = tdb_delete( wcache->tdb, key );
4460 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4467 ret = tdb_store( wcache->tdb, key, data, 0 );
4470 SAFE_FREE( data.dptr );
4471 SAFE_FREE( key.dptr );
4473 return ( ret != -1 );
4476 /*********************************************************************
4477 ********************************************************************/
4479 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4481 TDB_DATA key = make_tdc_key( lp_workgroup() );
4482 TDB_DATA data = { NULL, 0 };
4490 data = tdb_fetch( wcache->tdb, key );
4492 SAFE_FREE( key.dptr );
4497 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4499 SAFE_FREE( data.dptr );
4507 /*********************************************************************
4508 ********************************************************************/
4510 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4512 struct winbindd_tdc_domain *dom_list = NULL;
4513 size_t num_domains = 0;
4516 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4517 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4518 domain->name, domain->alt_name,
4519 sid_string_dbg(&domain->sid),
4520 domain->domain_flags,
4521 domain->domain_trust_attribs,
4522 domain->domain_type));
4524 if ( !init_wcache() ) {
4528 /* fetch the list */
4530 wcache_tdc_fetch_list( &dom_list, &num_domains );
4532 /* add the new domain */
4534 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4538 /* pack the domain */
4540 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4548 TALLOC_FREE( dom_list );
4553 /*********************************************************************
4554 ********************************************************************/
4556 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4558 struct winbindd_tdc_domain *dom_list = NULL;
4559 size_t num_domains = 0;
4561 struct winbindd_tdc_domain *d = NULL;
4563 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4565 if ( !init_wcache() ) {
4569 /* fetch the list */
4571 wcache_tdc_fetch_list( &dom_list, &num_domains );
4573 for ( i=0; i<num_domains; i++ ) {
4574 if ( strequal(name, dom_list[i].domain_name) ||
4575 strequal(name, dom_list[i].dns_name) )
4577 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4580 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4584 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4585 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4586 sid_copy( &d->sid, &dom_list[i].sid );
4587 d->trust_flags = dom_list[i].trust_flags;
4588 d->trust_type = dom_list[i].trust_type;
4589 d->trust_attribs = dom_list[i].trust_attribs;
4595 TALLOC_FREE( dom_list );
4601 /*********************************************************************
4602 ********************************************************************/
4604 void wcache_tdc_clear( void )
4606 if ( !init_wcache() )
4609 wcache_tdc_store_list( NULL, 0 );
4615 /*********************************************************************
4616 ********************************************************************/
4618 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4620 const DOM_SID *user_sid,
4621 const char *homedir,
4626 struct cache_entry *centry;
4629 if ( (centry = centry_start(domain, status)) == NULL )
4632 centry_put_string( centry, homedir );
4633 centry_put_string( centry, shell );
4634 centry_put_string( centry, gecos );
4635 centry_put_uint32( centry, gid );
4637 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4639 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4641 centry_free(centry);
4644 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4645 const DOM_SID *user_sid,
4647 ADS_STRUCT *ads, LDAPMessage *msg,
4648 const char **homedir, const char **shell,
4649 const char **gecos, gid_t *p_gid)
4651 struct winbind_cache *cache = get_cache(domain);
4652 struct cache_entry *centry = NULL;
4659 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4660 sid_to_fstring(tmp, user_sid));
4665 *homedir = centry_string( centry, ctx );
4666 *shell = centry_string( centry, ctx );
4667 *gecos = centry_string( centry, ctx );
4668 *p_gid = centry_uint32( centry );
4670 centry_free(centry);
4672 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4673 sid_string_dbg(user_sid)));
4675 return NT_STATUS_OK;
4679 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4680 homedir, shell, gecos, p_gid );
4682 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4684 if ( NT_STATUS_IS_OK(nt_status) ) {
4685 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4686 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4687 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4688 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4690 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4691 *homedir, *shell, *gecos, *p_gid );
4694 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4695 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4697 set_domain_offline( domain );
4704 /* the cache backend methods are exposed via this structure */
4705 struct winbindd_methods cache_methods = {
4723 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4724 uint32_t opnum, const DATA_BLOB *req,
4730 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4734 keylen = talloc_get_size(key) - 1;
4736 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4740 memcpy(key + keylen, req->data, req->length);
4742 pkey->dptr = (uint8_t *)key;
4743 pkey->dsize = talloc_get_size(key);
4747 static bool wcache_opnum_cacheable(uint32_t opnum)
4750 case NDR_WBINT_PING:
4751 case NDR_WBINT_QUERYSEQUENCENUMBER:
4752 case NDR_WBINT_ALLOCATEUID:
4753 case NDR_WBINT_ALLOCATEGID:
4754 case NDR_WBINT_CHECKMACHINEACCOUNT:
4755 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4756 case NDR_WBINT_PINGDC:
4762 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4763 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4768 if (!wcache_opnum_cacheable(opnum)) {
4772 if (wcache->tdb == NULL) {
4776 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4779 data = tdb_fetch(wcache->tdb, key);
4780 TALLOC_FREE(key.dptr);
4782 if (data.dptr == NULL) {
4785 if (data.dsize < 12) {
4789 if (!is_domain_offline(domain)) {
4790 uint32_t entry_seqnum, dom_seqnum, last_check;
4791 uint64_t entry_timeout;
4793 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4797 entry_seqnum = IVAL(data.dptr, 0);
4798 if (entry_seqnum != dom_seqnum) {
4799 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4800 (int)entry_seqnum));
4803 entry_timeout = BVAL(data.dptr, 4);
4804 if (time(NULL) > entry_timeout) {
4805 DEBUG(10, ("Entry has timed out\n"));
4810 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4812 if (resp->data == NULL) {
4813 DEBUG(10, ("talloc failed\n"));
4816 resp->length = data.dsize - 12;
4820 SAFE_FREE(data.dptr);
4824 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4825 const DATA_BLOB *req, const DATA_BLOB *resp)
4828 uint32_t dom_seqnum, last_check;
4831 if (!wcache_opnum_cacheable(opnum)) {
4835 if (wcache->tdb == NULL) {
4839 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4840 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4845 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4849 timeout = time(NULL) + lp_winbind_cache_time();
4851 data.dsize = resp->length + 12;
4852 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4853 if (data.dptr == NULL) {
4857 SIVAL(data.dptr, 0, dom_seqnum);
4858 SBVAL(data.dptr, 4, timeout);
4859 memcpy(data.dptr + 12, resp->data, resp->length);
4861 tdb_store(wcache->tdb, key, data, 0);
4864 TALLOC_FREE(key.dptr);