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_VERSION 1
36 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
38 extern struct winbindd_methods reconnect_methods;
40 extern struct winbindd_methods ads_methods;
42 extern struct winbindd_methods builtin_passdb_methods;
45 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
46 * Here are the list of entry types that are *not* stored
47 * as form struct cache_entry in the cache.
50 static const char *non_centry_keys[] = {
55 WINBINDD_CACHE_VERSION_KEYSTR,
59 /************************************************************************
60 Is this key a non-centry type ?
61 ************************************************************************/
63 static bool is_non_centry_key(TDB_DATA kbuf)
67 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
70 for (i = 0; non_centry_keys[i] != NULL; i++) {
71 size_t namelen = strlen(non_centry_keys[i]);
72 if (kbuf.dsize < namelen) {
75 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
82 /* Global online/offline state - False when online. winbindd starts up online
83 and sets this to true if the first query fails and there's an entry in
84 the cache tdb telling us to stay offline. */
86 static bool global_winbindd_offline_state;
88 struct winbind_cache {
94 uint32 sequence_number;
99 void (*smb_panic_fn)(const char *const why) = smb_panic;
101 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
103 static struct winbind_cache *wcache;
105 void winbindd_check_cache_size(time_t t)
107 static time_t last_check_time;
110 if (last_check_time == (time_t)0)
113 if (t - last_check_time < 60 && t - last_check_time > 0)
116 if (wcache == NULL || wcache->tdb == NULL) {
117 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
121 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
122 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
126 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
127 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
128 (unsigned long)st.st_size,
129 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
130 wcache_flush_cache();
134 /* get the winbind_cache structure */
135 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
137 struct winbind_cache *ret = wcache;
139 /* We have to know what type of domain we are dealing with first. */
141 if (domain->internal) {
142 domain->backend = &builtin_passdb_methods;
143 domain->initialized = True;
145 if ( !domain->initialized ) {
146 init_dc_connection( domain );
150 OK. listen up becasue I'm only going to say this once.
151 We have the following scenarios to consider
152 (a) trusted AD domains on a Samba DC,
153 (b) trusted AD domains and we are joined to a non-kerberos domain
154 (c) trusted AD domains and we are joined to a kerberos (AD) domain
156 For (a) we can always contact the trusted domain using krb5
157 since we have the domain trust account password
159 For (b) we can only use RPC since we have no way of
160 getting a krb5 ticket in our own domain
162 For (c) we can always use krb5 since we have a kerberos trust
167 if (!domain->backend) {
169 struct winbindd_domain *our_domain = domain;
171 /* find our domain first so we can figure out if we
172 are joined to a kerberized domain */
174 if ( !domain->primary )
175 our_domain = find_our_domain();
177 if ((our_domain->active_directory || IS_DC)
178 && domain->active_directory
179 && !lp_winbind_rpc_only()) {
180 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
181 domain->backend = &ads_methods;
183 #endif /* HAVE_ADS */
184 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
185 domain->backend = &reconnect_methods;
188 #endif /* HAVE_ADS */
194 ret = SMB_XMALLOC_P(struct winbind_cache);
198 wcache_flush_cache();
204 free a centry structure
206 static void centry_free(struct cache_entry *centry)
210 SAFE_FREE(centry->data);
214 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
216 if (centry->len - centry->ofs < nbytes) {
217 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
218 (unsigned int)nbytes,
219 centry->len - centry->ofs));
226 pull a uint32 from a cache entry
228 static uint32 centry_uint32(struct cache_entry *centry)
232 if (!centry_check_bytes(centry, 4)) {
233 smb_panic_fn("centry_uint32");
235 ret = IVAL(centry->data, centry->ofs);
241 pull a uint16 from a cache entry
243 static uint16 centry_uint16(struct cache_entry *centry)
246 if (!centry_check_bytes(centry, 2)) {
247 smb_panic_fn("centry_uint16");
249 ret = CVAL(centry->data, centry->ofs);
255 pull a uint8 from a cache entry
257 static uint8 centry_uint8(struct cache_entry *centry)
260 if (!centry_check_bytes(centry, 1)) {
261 smb_panic_fn("centry_uint8");
263 ret = CVAL(centry->data, centry->ofs);
269 pull a NTTIME from a cache entry
271 static NTTIME centry_nttime(struct cache_entry *centry)
274 if (!centry_check_bytes(centry, 8)) {
275 smb_panic_fn("centry_nttime");
277 ret = IVAL(centry->data, centry->ofs);
279 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
285 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
287 static time_t centry_time(struct cache_entry *centry)
289 return (time_t)centry_nttime(centry);
292 /* pull a string from a cache entry, using the supplied
295 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
300 len = centry_uint8(centry);
303 /* a deliberate NULL string */
307 if (!centry_check_bytes(centry, (size_t)len)) {
308 smb_panic_fn("centry_string");
311 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
313 smb_panic_fn("centry_string out of memory\n");
315 memcpy(ret,centry->data + centry->ofs, len);
321 /* pull a hash16 from a cache entry, using the supplied
324 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
329 len = centry_uint8(centry);
332 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
337 if (!centry_check_bytes(centry, 16)) {
341 ret = TALLOC_ARRAY(mem_ctx, char, 16);
343 smb_panic_fn("centry_hash out of memory\n");
345 memcpy(ret,centry->data + centry->ofs, 16);
350 /* pull a sid from a cache entry, using the supplied
353 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
358 sid_string = centry_string(centry, talloc_tos());
359 if (sid_string == NULL) {
362 ret = string_to_sid(sid, sid_string);
363 TALLOC_FREE(sid_string);
369 pull a NTSTATUS from a cache entry
371 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
375 status = NT_STATUS(centry_uint32(centry));
380 /* the server is considered down if it can't give us a sequence number */
381 static bool wcache_server_down(struct winbindd_domain *domain)
388 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
391 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
396 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
397 uint32_t *last_seq_check)
402 if (wcache->tdb == NULL) {
403 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
407 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
409 DEBUG(10, ("talloc failed\n"));
413 data = tdb_fetch_bystring(wcache->tdb, key);
416 if (data.dptr == NULL) {
417 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
421 if (data.dsize != 8) {
422 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
424 SAFE_FREE(data.dptr);
428 *seqnum = IVAL(data.dptr, 0);
429 *last_seq_check = IVAL(data.dptr, 4);
430 SAFE_FREE(data.dptr);
435 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
437 uint32 last_check, time_diff;
439 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
441 return NT_STATUS_UNSUCCESSFUL;
443 domain->last_seq_check = last_check;
445 /* have we expired? */
447 time_diff = now - domain->last_seq_check;
448 if ( time_diff > lp_winbind_cache_time() ) {
449 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
450 domain->name, domain->sequence_number,
451 (uint32)domain->last_seq_check));
452 return NT_STATUS_UNSUCCESSFUL;
455 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
456 domain->name, domain->sequence_number,
457 (uint32)domain->last_seq_check));
462 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
463 time_t last_seq_check)
469 if (wcache->tdb == NULL) {
470 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
474 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
475 if (key_str == NULL) {
476 DEBUG(10, ("talloc_asprintf failed\n"));
480 SIVAL(buf, 0, seqnum);
481 SIVAL(buf, 4, last_seq_check);
483 ret = tdb_store_bystring(wcache->tdb, key_str,
484 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
485 TALLOC_FREE(key_str);
487 DEBUG(10, ("tdb_store_bystring failed: %s\n",
488 tdb_errorstr(wcache->tdb)));
489 TALLOC_FREE(key_str);
493 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
494 domain_name, seqnum, (unsigned)last_seq_check));
499 static bool store_cache_seqnum( struct winbindd_domain *domain )
501 return wcache_store_seqnum(domain->name, domain->sequence_number,
502 domain->last_seq_check);
506 refresh the domain sequence number. If force is true
507 then always refresh it, no matter how recently we fetched it
510 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
514 time_t t = time(NULL);
515 unsigned cache_time = lp_winbind_cache_time();
517 if ( IS_DOMAIN_OFFLINE(domain) ) {
523 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
524 /* trying to reconnect is expensive, don't do it too often */
525 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
530 time_diff = t - domain->last_seq_check;
532 /* see if we have to refetch the domain sequence number */
533 if (!force && (time_diff < cache_time) &&
534 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
535 NT_STATUS_IS_OK(domain->last_status)) {
536 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
540 /* try to get the sequence number from the tdb cache first */
541 /* this will update the timestamp as well */
543 status = fetch_cache_seqnum( domain, t );
544 if (NT_STATUS_IS_OK(status) &&
545 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
546 NT_STATUS_IS_OK(domain->last_status)) {
550 /* important! make sure that we know if this is a native
551 mode domain or not. And that we can contact it. */
553 if ( winbindd_can_contact_domain( domain ) ) {
554 status = domain->backend->sequence_number(domain,
555 &domain->sequence_number);
557 /* just use the current time */
558 status = NT_STATUS_OK;
559 domain->sequence_number = time(NULL);
563 /* the above call could have set our domain->backend to NULL when
564 * coming from offline to online mode, make sure to reinitialize the
565 * backend - Guenther */
568 if (!NT_STATUS_IS_OK(status)) {
569 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
570 domain->sequence_number = DOM_SEQUENCE_NONE;
573 domain->last_status = status;
574 domain->last_seq_check = time(NULL);
576 /* save the new sequence number in the cache */
577 store_cache_seqnum( domain );
580 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
581 domain->name, domain->sequence_number));
587 decide if a cache entry has expired
589 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
591 /* If we've been told to be offline - stay in that state... */
592 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
593 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
594 keystr, domain->name ));
598 /* when the domain is offline return the cached entry.
599 * This deals with transient offline states... */
601 if (!domain->online) {
602 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
603 keystr, domain->name ));
607 /* if the server is OK and our cache entry came from when it was down then
608 the entry is invalid */
609 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
610 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
611 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
612 keystr, domain->name ));
616 /* if the server is down or the cache entry is not older than the
617 current sequence number then it is OK */
618 if (wcache_server_down(domain) ||
619 centry->sequence_number == domain->sequence_number) {
620 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
621 keystr, domain->name ));
625 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
626 keystr, domain->name ));
632 static struct cache_entry *wcache_fetch_raw(char *kstr)
635 struct cache_entry *centry;
638 key = string_tdb_data(kstr);
639 data = tdb_fetch(wcache->tdb, key);
645 centry = SMB_XMALLOC_P(struct cache_entry);
646 centry->data = (unsigned char *)data.dptr;
647 centry->len = data.dsize;
650 if (centry->len < 8) {
651 /* huh? corrupt cache? */
652 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
657 centry->status = centry_ntstatus(centry);
658 centry->sequence_number = centry_uint32(centry);
664 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
665 number and return status
667 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
668 struct winbindd_domain *domain,
669 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
670 static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
671 struct winbindd_domain *domain,
672 const char *format, ...)
676 struct cache_entry *centry;
678 if (!winbindd_use_cache()) {
682 refresh_sequence_number(domain, false);
684 va_start(ap, format);
685 smb_xvasprintf(&kstr, format, ap);
688 centry = wcache_fetch_raw(kstr);
689 if (centry == NULL) {
694 if (centry_expired(domain, kstr, centry)) {
696 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
697 kstr, domain->name ));
704 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
705 kstr, domain->name ));
711 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
712 static void wcache_delete(const char *format, ...)
718 va_start(ap, format);
719 smb_xvasprintf(&kstr, format, ap);
722 key = string_tdb_data(kstr);
724 tdb_delete(wcache->tdb, key);
729 make sure we have at least len bytes available in a centry
731 static void centry_expand(struct cache_entry *centry, uint32 len)
733 if (centry->len - centry->ofs >= len)
736 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
739 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
740 smb_panic_fn("out of memory in centry_expand");
745 push a uint32 into a centry
747 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
749 centry_expand(centry, 4);
750 SIVAL(centry->data, centry->ofs, v);
755 push a uint16 into a centry
757 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
759 centry_expand(centry, 2);
760 SIVAL(centry->data, centry->ofs, v);
765 push a uint8 into a centry
767 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
769 centry_expand(centry, 1);
770 SCVAL(centry->data, centry->ofs, v);
775 push a string into a centry
777 static void centry_put_string(struct cache_entry *centry, const char *s)
782 /* null strings are marked as len 0xFFFF */
783 centry_put_uint8(centry, 0xFF);
788 /* can't handle more than 254 char strings. Truncating is probably best */
790 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
793 centry_put_uint8(centry, len);
794 centry_expand(centry, len);
795 memcpy(centry->data + centry->ofs, s, len);
800 push a 16 byte hash into a centry - treat as 16 byte string.
802 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
804 centry_put_uint8(centry, 16);
805 centry_expand(centry, 16);
806 memcpy(centry->data + centry->ofs, val, 16);
810 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
813 centry_put_string(centry, sid_to_fstring(sid_string, sid));
818 put NTSTATUS into a centry
820 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
822 uint32 status_value = NT_STATUS_V(status);
823 centry_put_uint32(centry, status_value);
828 push a NTTIME into a centry
830 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
832 centry_expand(centry, 8);
833 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
835 SIVAL(centry->data, centry->ofs, nt >> 32);
840 push a time_t into a centry - use a 64 bit size.
841 NTTIME here is being used as a convenient 64-bit size.
843 static void centry_put_time(struct cache_entry *centry, time_t t)
845 NTTIME nt = (NTTIME)t;
846 centry_put_nttime(centry, nt);
850 start a centry for output. When finished, call centry_end()
852 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
854 struct cache_entry *centry;
859 centry = SMB_XMALLOC_P(struct cache_entry);
861 centry->len = 8192; /* reasonable default */
862 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
864 centry->sequence_number = domain->sequence_number;
865 centry_put_ntstatus(centry, status);
866 centry_put_uint32(centry, centry->sequence_number);
871 finish a centry and write it to the tdb
873 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
874 static void centry_end(struct cache_entry *centry, const char *format, ...)
880 if (!winbindd_use_cache()) {
884 va_start(ap, format);
885 smb_xvasprintf(&kstr, format, ap);
888 key = string_tdb_data(kstr);
889 data.dptr = centry->data;
890 data.dsize = centry->ofs;
892 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
896 static void wcache_save_name_to_sid(struct winbindd_domain *domain,
897 NTSTATUS status, const char *domain_name,
898 const char *name, const DOM_SID *sid,
899 enum lsa_SidType type)
901 struct cache_entry *centry;
904 centry = centry_start(domain, status);
907 centry_put_uint32(centry, type);
908 centry_put_sid(centry, sid);
909 fstrcpy(uname, name);
911 centry_end(centry, "NS/%s/%s", domain_name, uname);
912 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
913 uname, sid_string_dbg(sid), nt_errstr(status)));
917 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
918 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
920 struct cache_entry *centry;
923 centry = centry_start(domain, status);
927 if (NT_STATUS_IS_OK(status)) {
928 centry_put_uint32(centry, type);
929 centry_put_string(centry, domain_name);
930 centry_put_string(centry, name);
933 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
934 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
935 name, nt_errstr(status)));
940 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
941 struct wbint_userinfo *info)
943 struct cache_entry *centry;
946 if (is_null_sid(&info->user_sid)) {
950 centry = centry_start(domain, status);
953 centry_put_string(centry, info->acct_name);
954 centry_put_string(centry, info->full_name);
955 centry_put_string(centry, info->homedir);
956 centry_put_string(centry, info->shell);
957 centry_put_uint32(centry, info->primary_gid);
958 centry_put_sid(centry, &info->user_sid);
959 centry_put_sid(centry, &info->group_sid);
960 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
962 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
966 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
968 struct samr_DomInfo12 *lockout_policy)
970 struct cache_entry *centry;
972 centry = centry_start(domain, status);
976 centry_put_nttime(centry, lockout_policy->lockout_duration);
977 centry_put_nttime(centry, lockout_policy->lockout_window);
978 centry_put_uint16(centry, lockout_policy->lockout_threshold);
980 centry_end(centry, "LOC_POL/%s", domain->name);
982 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
989 static void wcache_save_password_policy(struct winbindd_domain *domain,
991 struct samr_DomInfo1 *policy)
993 struct cache_entry *centry;
995 centry = centry_start(domain, status);
999 centry_put_uint16(centry, policy->min_password_length);
1000 centry_put_uint16(centry, policy->password_history_length);
1001 centry_put_uint32(centry, policy->password_properties);
1002 centry_put_nttime(centry, policy->max_password_age);
1003 centry_put_nttime(centry, policy->min_password_age);
1005 centry_end(centry, "PWD_POL/%s", domain->name);
1007 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1009 centry_free(centry);
1012 /***************************************************************************
1013 ***************************************************************************/
1015 static void wcache_save_username_alias(struct winbindd_domain *domain,
1017 const char *name, const char *alias)
1019 struct cache_entry *centry;
1022 if ( (centry = centry_start(domain, status)) == NULL )
1025 centry_put_string( centry, alias );
1027 fstrcpy(uname, name);
1029 centry_end(centry, "NSS/NA/%s", uname);
1031 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1033 centry_free(centry);
1036 static void wcache_save_alias_username(struct winbindd_domain *domain,
1038 const char *alias, const char *name)
1040 struct cache_entry *centry;
1043 if ( (centry = centry_start(domain, status)) == NULL )
1046 centry_put_string( centry, name );
1048 fstrcpy(uname, alias);
1050 centry_end(centry, "NSS/AN/%s", uname);
1052 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1054 centry_free(centry);
1057 /***************************************************************************
1058 ***************************************************************************/
1060 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1061 struct winbindd_domain *domain,
1062 const char *name, char **alias )
1064 struct winbind_cache *cache = get_cache(domain);
1065 struct cache_entry *centry = NULL;
1069 if ( domain->internal )
1070 return NT_STATUS_NOT_SUPPORTED;
1075 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1076 return NT_STATUS_NO_MEMORY;
1077 strupper_m(upper_name);
1079 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1081 SAFE_FREE( upper_name );
1086 status = centry->status;
1088 if (!NT_STATUS_IS_OK(status)) {
1089 centry_free(centry);
1093 *alias = centry_string( centry, mem_ctx );
1095 centry_free(centry);
1097 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1098 name, *alias ? *alias : "(none)"));
1100 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1104 /* If its not in cache and we are offline, then fail */
1106 if ( get_global_winbindd_state_offline() || !domain->online ) {
1107 DEBUG(8,("resolve_username_to_alias: rejecting query "
1108 "in offline mode\n"));
1109 return NT_STATUS_NOT_FOUND;
1112 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1114 if ( NT_STATUS_IS_OK( status ) ) {
1115 wcache_save_username_alias(domain, status, name, *alias);
1118 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1119 wcache_save_username_alias(domain, status, name, "(NULL)");
1122 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1123 nt_errstr(status)));
1125 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1126 set_domain_offline( domain );
1132 /***************************************************************************
1133 ***************************************************************************/
1135 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1136 struct winbindd_domain *domain,
1137 const char *alias, char **name )
1139 struct winbind_cache *cache = get_cache(domain);
1140 struct cache_entry *centry = NULL;
1144 if ( domain->internal )
1145 return NT_STATUS_NOT_SUPPORTED;
1150 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1151 return NT_STATUS_NO_MEMORY;
1152 strupper_m(upper_name);
1154 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1156 SAFE_FREE( upper_name );
1161 status = centry->status;
1163 if (!NT_STATUS_IS_OK(status)) {
1164 centry_free(centry);
1168 *name = centry_string( centry, mem_ctx );
1170 centry_free(centry);
1172 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1173 alias, *name ? *name : "(none)"));
1175 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1179 /* If its not in cache and we are offline, then fail */
1181 if ( get_global_winbindd_state_offline() || !domain->online ) {
1182 DEBUG(8,("resolve_alias_to_username: rejecting query "
1183 "in offline mode\n"));
1184 return NT_STATUS_NOT_FOUND;
1187 /* an alias cannot contain a domain prefix or '@' */
1189 if (strchr(alias, '\\') || strchr(alias, '@')) {
1190 DEBUG(10,("resolve_alias_to_username: skipping fully "
1191 "qualified name %s\n", alias));
1192 return NT_STATUS_OBJECT_NAME_INVALID;
1195 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1197 if ( NT_STATUS_IS_OK( status ) ) {
1198 wcache_save_alias_username( domain, status, alias, *name );
1201 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1202 wcache_save_alias_username(domain, status, alias, "(NULL)");
1205 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1206 nt_errstr(status)));
1208 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1209 set_domain_offline( domain );
1215 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1217 struct winbind_cache *cache = get_cache(domain);
1219 fstring key_str, tmp;
1223 return NT_STATUS_INTERNAL_DB_ERROR;
1226 if (is_null_sid(sid)) {
1227 return NT_STATUS_INVALID_SID;
1230 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1231 return NT_STATUS_INVALID_SID;
1234 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1236 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1238 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1241 SAFE_FREE(data.dptr);
1242 return NT_STATUS_OK;
1245 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1246 as new salted ones. */
1248 NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1249 TALLOC_CTX *mem_ctx,
1251 const uint8 **cached_nt_pass,
1252 const uint8 **cached_salt)
1254 struct winbind_cache *cache = get_cache(domain);
1255 struct cache_entry *centry = NULL;
1262 return NT_STATUS_INTERNAL_DB_ERROR;
1265 if (is_null_sid(sid)) {
1266 return NT_STATUS_INVALID_SID;
1269 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1270 return NT_STATUS_INVALID_SID;
1273 /* Try and get a salted cred first. If we can't
1274 fall back to an unsalted cred. */
1276 centry = wcache_fetch(cache, domain, "CRED/%s",
1277 sid_to_fstring(tmp, sid));
1279 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1280 sid_string_dbg(sid)));
1281 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1284 t = centry_time(centry);
1286 /* In the salted case this isn't actually the nt_hash itself,
1287 but the MD5 of the salt + nt_hash. Let the caller
1288 sort this out. It can tell as we only return the cached_salt
1289 if we are returning a salted cred. */
1291 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1292 if (*cached_nt_pass == NULL) {
1295 sid_to_fstring(sidstr, sid);
1297 /* Bad (old) cred cache. Delete and pretend we
1299 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1301 wcache_delete("CRED/%s", sidstr);
1302 centry_free(centry);
1303 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1306 /* We only have 17 bytes more data in the salted cred case. */
1307 if (centry->len - centry->ofs == 17) {
1308 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1310 *cached_salt = NULL;
1313 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1315 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1318 status = centry->status;
1320 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1321 sid_string_dbg(sid), nt_errstr(status) ));
1323 centry_free(centry);
1327 /* Store creds for a SID - only writes out new salted ones. */
1329 NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1330 TALLOC_CTX *mem_ctx,
1332 const uint8 nt_pass[NT_HASH_LEN])
1334 struct cache_entry *centry;
1337 uint8 cred_salt[NT_HASH_LEN];
1338 uint8 salted_hash[NT_HASH_LEN];
1340 if (is_null_sid(sid)) {
1341 return NT_STATUS_INVALID_SID;
1344 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1345 return NT_STATUS_INVALID_SID;
1348 centry = centry_start(domain, NT_STATUS_OK);
1350 return NT_STATUS_INTERNAL_DB_ERROR;
1353 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1355 centry_put_time(centry, time(NULL));
1357 /* Create a salt and then salt the hash. */
1358 generate_random_buffer(cred_salt, NT_HASH_LEN);
1359 E_md5hash(cred_salt, nt_pass, salted_hash);
1361 centry_put_hash16(centry, salted_hash);
1362 centry_put_hash16(centry, cred_salt);
1363 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1365 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1367 centry_free(centry);
1369 return NT_STATUS_OK;
1373 /* Query display info. This is the basic user list fn */
1374 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1375 TALLOC_CTX *mem_ctx,
1376 uint32 *num_entries,
1377 struct wbint_userinfo **info)
1379 struct winbind_cache *cache = get_cache(domain);
1380 struct cache_entry *centry = NULL;
1382 unsigned int i, retry;
1387 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1391 *num_entries = centry_uint32(centry);
1393 if (*num_entries == 0)
1396 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1398 smb_panic_fn("query_user_list out of memory");
1400 for (i=0; i<(*num_entries); i++) {
1401 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1402 (*info)[i].full_name = centry_string(centry, mem_ctx);
1403 (*info)[i].homedir = centry_string(centry, mem_ctx);
1404 (*info)[i].shell = centry_string(centry, mem_ctx);
1405 centry_sid(centry, &(*info)[i].user_sid);
1406 centry_sid(centry, &(*info)[i].group_sid);
1410 status = centry->status;
1412 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1413 domain->name, nt_errstr(status) ));
1415 centry_free(centry);
1422 /* Return status value returned by seq number check */
1424 if (!NT_STATUS_IS_OK(domain->last_status))
1425 return domain->last_status;
1427 /* Put the query_user_list() in a retry loop. There appears to be
1428 * some bug either with Windows 2000 or Samba's handling of large
1429 * rpc replies. This manifests itself as sudden disconnection
1430 * at a random point in the enumeration of a large (60k) user list.
1431 * The retry loop simply tries the operation again. )-: It's not
1432 * pretty but an acceptable workaround until we work out what the
1433 * real problem is. */
1438 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1441 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1442 if (!NT_STATUS_IS_OK(status)) {
1443 DEBUG(3, ("query_user_list: returned 0x%08x, "
1444 "retrying\n", NT_STATUS_V(status)));
1446 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1447 DEBUG(3, ("query_user_list: flushing "
1448 "connection cache\n"));
1449 invalidate_cm_connection(&domain->conn);
1452 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1456 refresh_sequence_number(domain, false);
1457 centry = centry_start(domain, status);
1460 centry_put_uint32(centry, *num_entries);
1461 for (i=0; i<(*num_entries); i++) {
1462 centry_put_string(centry, (*info)[i].acct_name);
1463 centry_put_string(centry, (*info)[i].full_name);
1464 centry_put_string(centry, (*info)[i].homedir);
1465 centry_put_string(centry, (*info)[i].shell);
1466 centry_put_sid(centry, &(*info)[i].user_sid);
1467 centry_put_sid(centry, &(*info)[i].group_sid);
1468 if (domain->backend && domain->backend->consistent) {
1469 /* when the backend is consistent we can pre-prime some mappings */
1470 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1472 (*info)[i].acct_name,
1473 &(*info)[i].user_sid,
1475 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1476 &(*info)[i].user_sid,
1478 (*info)[i].acct_name,
1480 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1483 centry_end(centry, "UL/%s", domain->name);
1484 centry_free(centry);
1490 /* list all domain groups */
1491 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1492 TALLOC_CTX *mem_ctx,
1493 uint32 *num_entries,
1494 struct acct_info **info)
1496 struct winbind_cache *cache = get_cache(domain);
1497 struct cache_entry *centry = NULL;
1504 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1508 *num_entries = centry_uint32(centry);
1510 if (*num_entries == 0)
1513 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1515 smb_panic_fn("enum_dom_groups out of memory");
1517 for (i=0; i<(*num_entries); i++) {
1518 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1519 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1520 (*info)[i].rid = centry_uint32(centry);
1524 status = centry->status;
1526 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1527 domain->name, nt_errstr(status) ));
1529 centry_free(centry);
1536 /* Return status value returned by seq number check */
1538 if (!NT_STATUS_IS_OK(domain->last_status))
1539 return domain->last_status;
1541 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1544 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1547 refresh_sequence_number(domain, false);
1548 centry = centry_start(domain, status);
1551 centry_put_uint32(centry, *num_entries);
1552 for (i=0; i<(*num_entries); i++) {
1553 centry_put_string(centry, (*info)[i].acct_name);
1554 centry_put_string(centry, (*info)[i].acct_desc);
1555 centry_put_uint32(centry, (*info)[i].rid);
1557 centry_end(centry, "GL/%s/domain", domain->name);
1558 centry_free(centry);
1564 /* list all domain groups */
1565 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1566 TALLOC_CTX *mem_ctx,
1567 uint32 *num_entries,
1568 struct acct_info **info)
1570 struct winbind_cache *cache = get_cache(domain);
1571 struct cache_entry *centry = NULL;
1578 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1582 *num_entries = centry_uint32(centry);
1584 if (*num_entries == 0)
1587 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1589 smb_panic_fn("enum_dom_groups out of memory");
1591 for (i=0; i<(*num_entries); i++) {
1592 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1593 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1594 (*info)[i].rid = centry_uint32(centry);
1599 /* If we are returning cached data and the domain controller
1600 is down then we don't know whether the data is up to date
1601 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1604 if (wcache_server_down(domain)) {
1605 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1606 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1608 status = centry->status;
1610 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1611 domain->name, nt_errstr(status) ));
1613 centry_free(centry);
1620 /* Return status value returned by seq number check */
1622 if (!NT_STATUS_IS_OK(domain->last_status))
1623 return domain->last_status;
1625 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1628 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1631 refresh_sequence_number(domain, false);
1632 centry = centry_start(domain, status);
1635 centry_put_uint32(centry, *num_entries);
1636 for (i=0; i<(*num_entries); i++) {
1637 centry_put_string(centry, (*info)[i].acct_name);
1638 centry_put_string(centry, (*info)[i].acct_desc);
1639 centry_put_uint32(centry, (*info)[i].rid);
1641 centry_end(centry, "GL/%s/local", domain->name);
1642 centry_free(centry);
1648 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1649 const char *domain_name,
1651 struct dom_sid *sid,
1652 enum lsa_SidType *type)
1654 struct winbind_cache *cache = get_cache(domain);
1655 struct cache_entry *centry;
1659 if (cache->tdb == NULL) {
1660 return NT_STATUS_NOT_FOUND;
1663 uname = talloc_strdup_upper(talloc_tos(), name);
1664 if (uname == NULL) {
1665 return NT_STATUS_NO_MEMORY;
1668 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1670 if (centry == NULL) {
1671 return NT_STATUS_NOT_FOUND;
1674 status = centry->status;
1675 if (NT_STATUS_IS_OK(status)) {
1676 *type = (enum lsa_SidType)centry_uint32(centry);
1677 centry_sid(centry, sid);
1680 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1681 "%s\n", domain->name, nt_errstr(status) ));
1683 centry_free(centry);
1687 /* convert a single name to a sid in a domain */
1688 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1689 TALLOC_CTX *mem_ctx,
1690 const char *domain_name,
1694 enum lsa_SidType *type)
1698 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1699 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1705 /* If the seq number check indicated that there is a problem
1706 * with this DC, then return that status... except for
1707 * access_denied. This is special because the dc may be in
1708 * "restrict anonymous = 1" mode, in which case it will deny
1709 * most unauthenticated operations, but *will* allow the LSA
1710 * name-to-sid that we try as a fallback. */
1712 if (!(NT_STATUS_IS_OK(domain->last_status)
1713 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1714 return domain->last_status;
1716 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1719 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1720 name, flags, sid, type);
1723 refresh_sequence_number(domain, false);
1725 if (domain->online &&
1726 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1727 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1729 /* Only save the reverse mapping if this was not a UPN */
1730 if (!strchr(name, '@')) {
1731 strupper_m(CONST_DISCARD(char *,domain_name));
1732 strlower_m(CONST_DISCARD(char *,name));
1733 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1740 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1741 const struct dom_sid *sid,
1742 TALLOC_CTX *mem_ctx,
1745 enum lsa_SidType *type)
1747 struct winbind_cache *cache = get_cache(domain);
1748 struct cache_entry *centry;
1752 if (cache->tdb == NULL) {
1753 return NT_STATUS_NOT_FOUND;
1756 sid_string = sid_string_tos(sid);
1757 if (sid_string == NULL) {
1758 return NT_STATUS_NO_MEMORY;
1761 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1762 TALLOC_FREE(sid_string);
1763 if (centry == NULL) {
1764 return NT_STATUS_NOT_FOUND;
1767 if (NT_STATUS_IS_OK(centry->status)) {
1768 *type = (enum lsa_SidType)centry_uint32(centry);
1769 *domain_name = centry_string(centry, mem_ctx);
1770 *name = centry_string(centry, mem_ctx);
1773 status = centry->status;
1774 centry_free(centry);
1776 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1777 "%s\n", domain->name, nt_errstr(status) ));
1782 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1784 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1785 TALLOC_CTX *mem_ctx,
1789 enum lsa_SidType *type)
1793 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1795 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1800 *domain_name = NULL;
1802 /* If the seq number check indicated that there is a problem
1803 * with this DC, then return that status... except for
1804 * access_denied. This is special because the dc may be in
1805 * "restrict anonymous = 1" mode, in which case it will deny
1806 * most unauthenticated operations, but *will* allow the LSA
1807 * sid-to-name that we try as a fallback. */
1809 if (!(NT_STATUS_IS_OK(domain->last_status)
1810 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1811 return domain->last_status;
1813 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1816 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1819 refresh_sequence_number(domain, false);
1820 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1822 /* We can't save the name to sid mapping here, as with sid history a
1823 * later name2sid would give the wrong sid. */
1828 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1829 TALLOC_CTX *mem_ctx,
1830 const DOM_SID *domain_sid,
1835 enum lsa_SidType **types)
1837 struct winbind_cache *cache = get_cache(domain);
1839 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1843 *domain_name = NULL;
1851 if (num_rids == 0) {
1852 return NT_STATUS_OK;
1855 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1856 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1858 if ((*names == NULL) || (*types == NULL)) {
1859 result = NT_STATUS_NO_MEMORY;
1863 have_mapped = have_unmapped = false;
1865 for (i=0; i<num_rids; i++) {
1867 struct cache_entry *centry;
1870 if (!sid_compose(&sid, domain_sid, rids[i])) {
1871 result = NT_STATUS_INTERNAL_ERROR;
1875 centry = wcache_fetch(cache, domain, "SN/%s",
1876 sid_to_fstring(tmp, &sid));
1881 (*types)[i] = SID_NAME_UNKNOWN;
1882 (*names)[i] = talloc_strdup(*names, "");
1884 if (NT_STATUS_IS_OK(centry->status)) {
1887 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1889 dom = centry_string(centry, mem_ctx);
1890 if (*domain_name == NULL) {
1896 (*names)[i] = centry_string(centry, *names);
1898 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1899 have_unmapped = true;
1902 /* something's definitely wrong */
1903 result = centry->status;
1907 centry_free(centry);
1911 return NT_STATUS_NONE_MAPPED;
1913 if (!have_unmapped) {
1914 return NT_STATUS_OK;
1916 return STATUS_SOME_UNMAPPED;
1920 TALLOC_FREE(*names);
1921 TALLOC_FREE(*types);
1923 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1924 rids, num_rids, domain_name,
1928 None of the queried rids has been found so save all negative entries
1930 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1931 for (i = 0; i < num_rids; i++) {
1933 const char *name = "";
1934 const enum lsa_SidType type = SID_NAME_UNKNOWN;
1935 NTSTATUS status = NT_STATUS_NONE_MAPPED;
1937 if (!sid_compose(&sid, domain_sid, rids[i])) {
1938 return NT_STATUS_INTERNAL_ERROR;
1941 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1949 Some or all of the queried rids have been found.
1951 if (!NT_STATUS_IS_OK(result) &&
1952 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1956 refresh_sequence_number(domain, false);
1958 for (i=0; i<num_rids; i++) {
1962 if (!sid_compose(&sid, domain_sid, rids[i])) {
1963 result = NT_STATUS_INTERNAL_ERROR;
1967 status = (*types)[i] == SID_NAME_UNKNOWN ?
1968 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1970 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1971 (*names)[i], (*types)[i]);
1977 TALLOC_FREE(*names);
1978 TALLOC_FREE(*types);
1982 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
1983 TALLOC_CTX *mem_ctx,
1984 const struct dom_sid *user_sid,
1985 struct wbint_userinfo *info)
1987 struct winbind_cache *cache = get_cache(domain);
1988 struct cache_entry *centry = NULL;
1992 if (cache->tdb == NULL) {
1993 return NT_STATUS_NOT_FOUND;
1996 sid_string = sid_string_tos(user_sid);
1997 if (sid_string == NULL) {
1998 return NT_STATUS_NO_MEMORY;
2001 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2002 TALLOC_FREE(sid_string);
2003 if (centry == NULL) {
2004 return NT_STATUS_NOT_FOUND;
2008 * If we have an access denied cache entry and a cached info3
2009 * in the samlogon cache then do a query. This will force the
2010 * rpc back end to return the info3 data.
2013 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2014 netsamlogon_cache_have(user_sid)) {
2015 DEBUG(10, ("query_user: cached access denied and have cached "
2017 domain->last_status = NT_STATUS_OK;
2018 centry_free(centry);
2019 return NT_STATUS_NOT_FOUND;
2022 /* if status is not ok then this is a negative hit
2023 and the rest of the data doesn't matter */
2024 status = centry->status;
2025 if (NT_STATUS_IS_OK(status)) {
2026 info->acct_name = centry_string(centry, mem_ctx);
2027 info->full_name = centry_string(centry, mem_ctx);
2028 info->homedir = centry_string(centry, mem_ctx);
2029 info->shell = centry_string(centry, mem_ctx);
2030 info->primary_gid = centry_uint32(centry);
2031 centry_sid(centry, &info->user_sid);
2032 centry_sid(centry, &info->group_sid);
2035 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2036 "%s\n", domain->name, nt_errstr(status) ));
2038 centry_free(centry);
2042 /* Lookup user information from a rid */
2043 static NTSTATUS query_user(struct winbindd_domain *domain,
2044 TALLOC_CTX *mem_ctx,
2045 const DOM_SID *user_sid,
2046 struct wbint_userinfo *info)
2050 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2051 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2057 /* Return status value returned by seq number check */
2059 if (!NT_STATUS_IS_OK(domain->last_status))
2060 return domain->last_status;
2062 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2065 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2068 refresh_sequence_number(domain, false);
2069 wcache_save_user(domain, status, info);
2074 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2075 TALLOC_CTX *mem_ctx,
2076 const struct dom_sid *user_sid,
2077 uint32_t *pnum_sids,
2078 struct dom_sid **psids)
2080 struct winbind_cache *cache = get_cache(domain);
2081 struct cache_entry *centry = NULL;
2083 uint32_t i, num_sids;
2084 struct dom_sid *sids;
2087 if (cache->tdb == NULL) {
2088 return NT_STATUS_NOT_FOUND;
2091 centry = wcache_fetch(cache, domain, "UG/%s",
2092 sid_to_fstring(sid_string, user_sid));
2093 if (centry == NULL) {
2094 return NT_STATUS_NOT_FOUND;
2097 /* If we have an access denied cache entry and a cached info3 in the
2098 samlogon cache then do a query. This will force the rpc back end
2099 to return the info3 data. */
2101 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2102 && netsamlogon_cache_have(user_sid)) {
2103 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2105 domain->last_status = NT_STATUS_OK;
2106 centry_free(centry);
2107 return NT_STATUS_NOT_FOUND;
2110 num_sids = centry_uint32(centry);
2111 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2113 return NT_STATUS_NO_MEMORY;
2116 for (i=0; i<num_sids; i++) {
2117 centry_sid(centry, &sids[i]);
2120 status = centry->status;
2122 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2123 "status: %s\n", domain->name, nt_errstr(status)));
2125 centry_free(centry);
2127 *pnum_sids = num_sids;
2132 /* Lookup groups a user is a member of. */
2133 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2134 TALLOC_CTX *mem_ctx,
2135 const DOM_SID *user_sid,
2136 uint32 *num_groups, DOM_SID **user_gids)
2138 struct cache_entry *centry = NULL;
2143 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2144 num_groups, user_gids);
2145 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2150 (*user_gids) = NULL;
2152 /* Return status value returned by seq number check */
2154 if (!NT_STATUS_IS_OK(domain->last_status))
2155 return domain->last_status;
2157 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2160 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2162 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2166 refresh_sequence_number(domain, false);
2167 centry = centry_start(domain, status);
2171 centry_put_uint32(centry, *num_groups);
2172 for (i=0; i<(*num_groups); i++) {
2173 centry_put_sid(centry, &(*user_gids)[i]);
2176 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2177 centry_free(centry);
2183 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2184 const struct dom_sid *sids)
2189 sidlist = talloc_strdup(mem_ctx, "");
2190 if (sidlist == NULL) {
2193 for (i=0; i<num_sids; i++) {
2195 sidlist = talloc_asprintf_append_buffer(
2196 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2197 if (sidlist == NULL) {
2204 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2205 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2206 const struct dom_sid *sids,
2207 uint32_t *pnum_aliases, uint32_t **paliases)
2209 struct winbind_cache *cache = get_cache(domain);
2210 struct cache_entry *centry = NULL;
2211 uint32_t num_aliases;
2217 if (cache->tdb == NULL) {
2218 return NT_STATUS_NOT_FOUND;
2221 if (num_sids == 0) {
2224 return NT_STATUS_OK;
2227 /* We need to cache indexed by the whole list of SIDs, the aliases
2228 * resulting might come from any of the SIDs. */
2230 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2231 if (sidlist == NULL) {
2232 return NT_STATUS_NO_MEMORY;
2235 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2236 TALLOC_FREE(sidlist);
2237 if (centry == NULL) {
2238 return NT_STATUS_NOT_FOUND;
2241 num_aliases = centry_uint32(centry);
2242 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2243 if (aliases == NULL) {
2244 centry_free(centry);
2245 return NT_STATUS_NO_MEMORY;
2248 for (i=0; i<num_aliases; i++) {
2249 aliases[i] = centry_uint32(centry);
2252 status = centry->status;
2254 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2255 "status %s\n", domain->name, nt_errstr(status)));
2257 centry_free(centry);
2259 *pnum_aliases = num_aliases;
2260 *paliases = aliases;
2265 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2266 TALLOC_CTX *mem_ctx,
2267 uint32 num_sids, const DOM_SID *sids,
2268 uint32 *num_aliases, uint32 **alias_rids)
2270 struct cache_entry *centry = NULL;
2275 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2276 num_aliases, alias_rids);
2277 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2282 (*alias_rids) = NULL;
2284 if (!NT_STATUS_IS_OK(domain->last_status))
2285 return domain->last_status;
2287 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2288 "for domain %s\n", domain->name ));
2290 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2291 if (sidlist == NULL) {
2292 return NT_STATUS_NO_MEMORY;
2295 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2297 num_aliases, alias_rids);
2300 refresh_sequence_number(domain, false);
2301 centry = centry_start(domain, status);
2304 centry_put_uint32(centry, *num_aliases);
2305 for (i=0; i<(*num_aliases); i++)
2306 centry_put_uint32(centry, (*alias_rids)[i]);
2307 centry_end(centry, "UA%s", sidlist);
2308 centry_free(centry);
2314 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2315 TALLOC_CTX *mem_ctx,
2316 const struct dom_sid *group_sid,
2317 uint32_t *num_names,
2318 struct dom_sid **sid_mem, char ***names,
2319 uint32_t **name_types)
2321 struct winbind_cache *cache = get_cache(domain);
2322 struct cache_entry *centry = NULL;
2327 if (cache->tdb == NULL) {
2328 return NT_STATUS_NOT_FOUND;
2331 sid_string = sid_string_tos(group_sid);
2332 if (sid_string == NULL) {
2333 return NT_STATUS_NO_MEMORY;
2336 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2337 TALLOC_FREE(sid_string);
2338 if (centry == NULL) {
2339 return NT_STATUS_NOT_FOUND;
2346 *num_names = centry_uint32(centry);
2347 if (*num_names == 0) {
2348 centry_free(centry);
2349 return NT_STATUS_OK;
2352 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2353 *names = talloc_array(mem_ctx, char *, *num_names);
2354 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2356 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2357 TALLOC_FREE(*sid_mem);
2358 TALLOC_FREE(*names);
2359 TALLOC_FREE(*name_types);
2360 centry_free(centry);
2361 return NT_STATUS_NO_MEMORY;
2364 for (i=0; i<(*num_names); i++) {
2365 centry_sid(centry, &(*sid_mem)[i]);
2366 (*names)[i] = centry_string(centry, mem_ctx);
2367 (*name_types)[i] = centry_uint32(centry);
2370 status = centry->status;
2372 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2373 "status: %s\n", domain->name, nt_errstr(status)));
2375 centry_free(centry);
2379 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2380 TALLOC_CTX *mem_ctx,
2381 const DOM_SID *group_sid, uint32 *num_names,
2382 DOM_SID **sid_mem, char ***names,
2383 uint32 **name_types)
2385 struct cache_entry *centry = NULL;
2390 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2391 sid_mem, names, name_types);
2392 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2399 (*name_types) = NULL;
2401 /* Return status value returned by seq number check */
2403 if (!NT_STATUS_IS_OK(domain->last_status))
2404 return domain->last_status;
2406 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2409 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2410 sid_mem, names, name_types);
2413 refresh_sequence_number(domain, false);
2414 centry = centry_start(domain, status);
2417 centry_put_uint32(centry, *num_names);
2418 for (i=0; i<(*num_names); i++) {
2419 centry_put_sid(centry, &(*sid_mem)[i]);
2420 centry_put_string(centry, (*names)[i]);
2421 centry_put_uint32(centry, (*name_types)[i]);
2423 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2424 centry_free(centry);
2430 /* find the sequence number for a domain */
2431 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2433 refresh_sequence_number(domain, false);
2435 *seq = domain->sequence_number;
2437 return NT_STATUS_OK;
2440 /* enumerate trusted domains
2441 * (we need to have the list of trustdoms in the cache when we go offline) -
2443 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2444 TALLOC_CTX *mem_ctx,
2445 uint32 *num_domains,
2450 struct winbind_cache *cache = get_cache(domain);
2451 struct cache_entry *centry = NULL;
2458 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2464 *num_domains = centry_uint32(centry);
2467 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2468 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2469 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2471 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2472 smb_panic_fn("trusted_domains out of memory");
2476 (*alt_names) = NULL;
2480 for (i=0; i<(*num_domains); i++) {
2481 (*names)[i] = centry_string(centry, mem_ctx);
2482 (*alt_names)[i] = centry_string(centry, mem_ctx);
2483 if (!centry_sid(centry, &(*dom_sids)[i])) {
2484 sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2488 status = centry->status;
2490 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2491 domain->name, *num_domains, nt_errstr(status) ));
2493 centry_free(centry);
2500 (*alt_names) = NULL;
2502 /* Return status value returned by seq number check */
2504 if (!NT_STATUS_IS_OK(domain->last_status))
2505 return domain->last_status;
2507 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2510 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2511 names, alt_names, dom_sids);
2513 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2514 * so that the generic centry handling still applies correctly -
2517 if (!NT_STATUS_IS_ERR(status)) {
2518 status = NT_STATUS_OK;
2522 #if 0 /* Disabled as we want the trust dom list to be managed by
2523 the main parent and always to make the query. --jerry */
2526 refresh_sequence_number(domain, false);
2528 centry = centry_start(domain, status);
2532 centry_put_uint32(centry, *num_domains);
2534 for (i=0; i<(*num_domains); i++) {
2535 centry_put_string(centry, (*names)[i]);
2536 centry_put_string(centry, (*alt_names)[i]);
2537 centry_put_sid(centry, &(*dom_sids)[i]);
2540 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2542 centry_free(centry);
2550 /* get lockout policy */
2551 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2552 TALLOC_CTX *mem_ctx,
2553 struct samr_DomInfo12 *policy)
2555 struct winbind_cache *cache = get_cache(domain);
2556 struct cache_entry *centry = NULL;
2562 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2567 policy->lockout_duration = centry_nttime(centry);
2568 policy->lockout_window = centry_nttime(centry);
2569 policy->lockout_threshold = centry_uint16(centry);
2571 status = centry->status;
2573 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2574 domain->name, nt_errstr(status) ));
2576 centry_free(centry);
2580 ZERO_STRUCTP(policy);
2582 /* Return status value returned by seq number check */
2584 if (!NT_STATUS_IS_OK(domain->last_status))
2585 return domain->last_status;
2587 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2590 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2593 refresh_sequence_number(domain, false);
2594 wcache_save_lockout_policy(domain, status, policy);
2599 /* get password policy */
2600 static NTSTATUS password_policy(struct winbindd_domain *domain,
2601 TALLOC_CTX *mem_ctx,
2602 struct samr_DomInfo1 *policy)
2604 struct winbind_cache *cache = get_cache(domain);
2605 struct cache_entry *centry = NULL;
2611 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2616 policy->min_password_length = centry_uint16(centry);
2617 policy->password_history_length = centry_uint16(centry);
2618 policy->password_properties = centry_uint32(centry);
2619 policy->max_password_age = centry_nttime(centry);
2620 policy->min_password_age = centry_nttime(centry);
2622 status = centry->status;
2624 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2625 domain->name, nt_errstr(status) ));
2627 centry_free(centry);
2631 ZERO_STRUCTP(policy);
2633 /* Return status value returned by seq number check */
2635 if (!NT_STATUS_IS_OK(domain->last_status))
2636 return domain->last_status;
2638 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2641 status = domain->backend->password_policy(domain, mem_ctx, policy);
2644 refresh_sequence_number(domain, false);
2645 if (NT_STATUS_IS_OK(status)) {
2646 wcache_save_password_policy(domain, status, policy);
2653 /* Invalidate cached user and group lists coherently */
2655 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2658 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2659 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2660 tdb_delete(the_tdb, kbuf);
2665 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2667 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2668 struct netr_SamInfo3 *info3)
2671 fstring key_str, sid_string;
2672 struct winbind_cache *cache;
2674 /* dont clear cached U/SID and UG/SID entries when we want to logon
2677 if (lp_winbind_offline_logon()) {
2684 cache = get_cache(domain);
2690 sid_copy(&sid, info3->base.domain_sid);
2691 sid_append_rid(&sid, info3->base.rid);
2693 /* Clear U/SID cache entry */
2694 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2695 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2696 tdb_delete(cache->tdb, string_tdb_data(key_str));
2698 /* Clear UG/SID cache entry */
2699 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2700 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2701 tdb_delete(cache->tdb, string_tdb_data(key_str));
2703 /* Samba/winbindd never needs this. */
2704 netsamlogon_clear_cached_user(info3);
2707 bool wcache_invalidate_cache(void)
2709 struct winbindd_domain *domain;
2711 for (domain = domain_list(); domain; domain = domain->next) {
2712 struct winbind_cache *cache = get_cache(domain);
2714 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2715 "entries for %s\n", domain->name));
2718 tdb_traverse(cache->tdb, traverse_fn, NULL);
2727 bool init_wcache(void)
2729 if (wcache == NULL) {
2730 wcache = SMB_XMALLOC_P(struct winbind_cache);
2731 ZERO_STRUCTP(wcache);
2734 if (wcache->tdb != NULL)
2737 /* when working offline we must not clear the cache on restart */
2738 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2739 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2740 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2741 O_RDWR|O_CREAT, 0600);
2743 if (wcache->tdb == NULL) {
2744 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2751 /************************************************************************
2752 This is called by the parent to initialize the cache file.
2753 We don't need sophisticated locking here as we know we're the
2755 ************************************************************************/
2757 bool initialize_winbindd_cache(void)
2759 bool cache_bad = true;
2762 if (!init_wcache()) {
2763 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2767 /* Check version number. */
2768 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2769 vers == WINBINDD_CACHE_VERSION) {
2774 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2775 "and re-creating with version number %d\n",
2776 WINBINDD_CACHE_VERSION ));
2778 tdb_close(wcache->tdb);
2781 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2782 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2783 cache_path("winbindd_cache.tdb"),
2787 if (!init_wcache()) {
2788 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2789 "init_wcache failed.\n"));
2793 /* Write the version. */
2794 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2795 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2796 tdb_errorstr(wcache->tdb) ));
2801 tdb_close(wcache->tdb);
2806 void close_winbindd_cache(void)
2812 tdb_close(wcache->tdb);
2817 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2818 char **domain_name, char **name,
2819 enum lsa_SidType *type)
2821 struct winbindd_domain *domain;
2824 domain = find_lookup_domain_from_sid(sid);
2825 if (domain == NULL) {
2828 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
2830 return NT_STATUS_IS_OK(status);
2833 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2834 const char *domain_name,
2837 enum lsa_SidType *type)
2839 struct winbindd_domain *domain;
2841 bool original_online_state;
2843 domain = find_lookup_domain_from_name(domain_name);
2844 if (domain == NULL) {
2848 /* If we are doing a cached logon, temporarily set the domain
2849 offline so the cache won't expire the entry */
2851 original_online_state = domain->online;
2852 domain->online = false;
2853 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
2854 domain->online = original_online_state;
2856 return NT_STATUS_IS_OK(status);
2859 void cache_name2sid(struct winbindd_domain *domain,
2860 const char *domain_name, const char *name,
2861 enum lsa_SidType type, const DOM_SID *sid)
2863 refresh_sequence_number(domain, false);
2864 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2869 * The original idea that this cache only contains centries has
2870 * been blurred - now other stuff gets put in here. Ensure we
2871 * ignore these things on cleanup.
2874 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2875 TDB_DATA dbuf, void *state)
2877 struct cache_entry *centry;
2879 if (is_non_centry_key(kbuf)) {
2883 centry = wcache_fetch_raw((char *)kbuf.dptr);
2888 if (!NT_STATUS_IS_OK(centry->status)) {
2889 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2890 tdb_delete(the_tdb, kbuf);
2893 centry_free(centry);
2897 /* flush the cache */
2898 void wcache_flush_cache(void)
2903 tdb_close(wcache->tdb);
2906 if (!winbindd_use_cache()) {
2910 /* when working offline we must not clear the cache on restart */
2911 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2912 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2913 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2914 O_RDWR|O_CREAT, 0600);
2917 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2921 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2923 DEBUG(10,("wcache_flush_cache success\n"));
2926 /* Count cached creds */
2928 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2931 int *cred_count = (int*)state;
2933 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2939 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2941 struct winbind_cache *cache = get_cache(domain);
2946 return NT_STATUS_INTERNAL_DB_ERROR;
2949 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2951 return NT_STATUS_OK;
2955 struct cred_list *prev, *next;
2960 static struct cred_list *wcache_cred_list;
2962 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2965 struct cred_list *cred;
2967 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2969 cred = SMB_MALLOC_P(struct cred_list);
2971 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2977 /* save a copy of the key */
2979 fstrcpy(cred->name, (const char *)kbuf.dptr);
2980 DLIST_ADD(wcache_cred_list, cred);
2986 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2988 struct winbind_cache *cache = get_cache(domain);
2991 struct cred_list *cred, *oldest = NULL;
2994 return NT_STATUS_INTERNAL_DB_ERROR;
2997 /* we possibly already have an entry */
2998 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3000 fstring key_str, tmp;
3002 DEBUG(11,("we already have an entry, deleting that\n"));
3004 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3006 tdb_delete(cache->tdb, string_tdb_data(key_str));
3008 return NT_STATUS_OK;
3011 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3013 return NT_STATUS_OK;
3014 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3015 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3018 ZERO_STRUCTP(oldest);
3020 for (cred = wcache_cred_list; cred; cred = cred->next) {
3025 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3027 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3029 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3033 t = IVAL(data.dptr, 0);
3034 SAFE_FREE(data.dptr);
3037 oldest = SMB_MALLOC_P(struct cred_list);
3038 if (oldest == NULL) {
3039 status = NT_STATUS_NO_MEMORY;
3043 fstrcpy(oldest->name, cred->name);
3044 oldest->created = t;
3048 if (t < oldest->created) {
3049 fstrcpy(oldest->name, cred->name);
3050 oldest->created = t;
3054 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3055 status = NT_STATUS_OK;
3057 status = NT_STATUS_UNSUCCESSFUL;
3060 SAFE_FREE(wcache_cred_list);
3066 /* Change the global online/offline state. */
3067 bool set_global_winbindd_state_offline(void)
3071 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3073 /* Only go offline if someone has created
3074 the key "WINBINDD_OFFLINE" in the cache tdb. */
3076 if (wcache == NULL || wcache->tdb == NULL) {
3077 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3081 if (!lp_winbind_offline_logon()) {
3082 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3086 if (global_winbindd_offline_state) {
3087 /* Already offline. */
3091 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3093 if (!data.dptr || data.dsize != 4) {
3094 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3095 SAFE_FREE(data.dptr);
3098 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3099 global_winbindd_offline_state = true;
3100 SAFE_FREE(data.dptr);
3105 void set_global_winbindd_state_online(void)
3107 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3109 if (!lp_winbind_offline_logon()) {
3110 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3114 if (!global_winbindd_offline_state) {
3115 /* Already online. */
3118 global_winbindd_offline_state = false;
3124 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3125 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3128 bool get_global_winbindd_state_offline(void)
3130 return global_winbindd_offline_state;
3133 /***********************************************************************
3134 Validate functions for all possible cache tdb keys.
3135 ***********************************************************************/
3137 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3138 struct tdb_validation_status *state)
3140 struct cache_entry *centry;
3142 centry = SMB_XMALLOC_P(struct cache_entry);
3143 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3144 if (!centry->data) {
3148 centry->len = data.dsize;
3151 if (centry->len < 8) {
3152 /* huh? corrupt cache? */
3153 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3154 centry_free(centry);
3155 state->bad_entry = true;
3156 state->success = false;
3160 centry->status = NT_STATUS(centry_uint32(centry));
3161 centry->sequence_number = centry_uint32(centry);
3165 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3166 struct tdb_validation_status *state)
3168 if (dbuf.dsize != 8) {
3169 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3170 keystr, (unsigned int)dbuf.dsize ));
3171 state->bad_entry = true;
3177 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3178 struct tdb_validation_status *state)
3180 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3185 (void)centry_uint32(centry);
3186 if (NT_STATUS_IS_OK(centry->status)) {
3188 (void)centry_sid(centry, &sid);
3191 centry_free(centry);
3193 if (!(state->success)) {
3196 DEBUG(10,("validate_ns: %s ok\n", keystr));
3200 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3201 struct tdb_validation_status *state)
3203 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3208 if (NT_STATUS_IS_OK(centry->status)) {
3209 (void)centry_uint32(centry);
3210 (void)centry_string(centry, mem_ctx);
3211 (void)centry_string(centry, mem_ctx);
3214 centry_free(centry);
3216 if (!(state->success)) {
3219 DEBUG(10,("validate_sn: %s ok\n", keystr));
3223 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3224 struct tdb_validation_status *state)
3226 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3233 (void)centry_string(centry, mem_ctx);
3234 (void)centry_string(centry, mem_ctx);
3235 (void)centry_string(centry, mem_ctx);
3236 (void)centry_string(centry, mem_ctx);
3237 (void)centry_uint32(centry);
3238 (void)centry_sid(centry, &sid);
3239 (void)centry_sid(centry, &sid);
3241 centry_free(centry);
3243 if (!(state->success)) {
3246 DEBUG(10,("validate_u: %s ok\n", keystr));
3250 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3251 struct tdb_validation_status *state)
3253 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3259 (void)centry_nttime(centry);
3260 (void)centry_nttime(centry);
3261 (void)centry_uint16(centry);
3263 centry_free(centry);
3265 if (!(state->success)) {
3268 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3272 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3273 struct tdb_validation_status *state)
3275 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3281 (void)centry_uint16(centry);
3282 (void)centry_uint16(centry);
3283 (void)centry_uint32(centry);
3284 (void)centry_nttime(centry);
3285 (void)centry_nttime(centry);
3287 centry_free(centry);
3289 if (!(state->success)) {
3292 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3296 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3297 struct tdb_validation_status *state)
3299 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3305 (void)centry_time(centry);
3306 (void)centry_hash16(centry, mem_ctx);
3308 /* We only have 17 bytes more data in the salted cred case. */
3309 if (centry->len - centry->ofs == 17) {
3310 (void)centry_hash16(centry, mem_ctx);
3313 centry_free(centry);
3315 if (!(state->success)) {
3318 DEBUG(10,("validate_cred: %s ok\n", keystr));
3322 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3323 struct tdb_validation_status *state)
3325 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3326 int32 num_entries, i;
3332 num_entries = (int32)centry_uint32(centry);
3334 for (i=0; i< num_entries; i++) {
3336 (void)centry_string(centry, mem_ctx);
3337 (void)centry_string(centry, mem_ctx);
3338 (void)centry_string(centry, mem_ctx);
3339 (void)centry_string(centry, mem_ctx);
3340 (void)centry_sid(centry, &sid);
3341 (void)centry_sid(centry, &sid);
3344 centry_free(centry);
3346 if (!(state->success)) {
3349 DEBUG(10,("validate_ul: %s ok\n", keystr));
3353 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3354 struct tdb_validation_status *state)
3356 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3357 int32 num_entries, i;
3363 num_entries = centry_uint32(centry);
3365 for (i=0; i< num_entries; i++) {
3366 (void)centry_string(centry, mem_ctx);
3367 (void)centry_string(centry, mem_ctx);
3368 (void)centry_uint32(centry);
3371 centry_free(centry);
3373 if (!(state->success)) {
3376 DEBUG(10,("validate_gl: %s ok\n", keystr));
3380 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3381 struct tdb_validation_status *state)
3383 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3384 int32 num_groups, i;
3390 num_groups = centry_uint32(centry);
3392 for (i=0; i< num_groups; i++) {
3394 centry_sid(centry, &sid);
3397 centry_free(centry);
3399 if (!(state->success)) {
3402 DEBUG(10,("validate_ug: %s ok\n", keystr));
3406 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3407 struct tdb_validation_status *state)
3409 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3410 int32 num_aliases, i;
3416 num_aliases = centry_uint32(centry);
3418 for (i=0; i < num_aliases; i++) {
3419 (void)centry_uint32(centry);
3422 centry_free(centry);
3424 if (!(state->success)) {
3427 DEBUG(10,("validate_ua: %s ok\n", keystr));
3431 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3432 struct tdb_validation_status *state)
3434 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3441 num_names = centry_uint32(centry);
3443 for (i=0; i< num_names; i++) {
3445 centry_sid(centry, &sid);
3446 (void)centry_string(centry, mem_ctx);
3447 (void)centry_uint32(centry);
3450 centry_free(centry);
3452 if (!(state->success)) {
3455 DEBUG(10,("validate_gm: %s ok\n", keystr));
3459 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3460 struct tdb_validation_status *state)
3462 /* Can't say anything about this other than must be nonzero. */
3463 if (dbuf.dsize == 0) {
3464 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3466 state->bad_entry = true;
3467 state->success = false;
3471 DEBUG(10,("validate_dr: %s ok\n", keystr));
3475 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3476 struct tdb_validation_status *state)
3478 /* Can't say anything about this other than must be nonzero. */
3479 if (dbuf.dsize == 0) {
3480 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3482 state->bad_entry = true;
3483 state->success = false;
3487 DEBUG(10,("validate_de: %s ok\n", keystr));
3491 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3492 TDB_DATA dbuf, struct tdb_validation_status *state)
3494 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3500 (void)centry_string(centry, mem_ctx);
3501 (void)centry_string(centry, mem_ctx);
3502 (void)centry_string(centry, mem_ctx);
3503 (void)centry_uint32(centry);
3505 centry_free(centry);
3507 if (!(state->success)) {
3510 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3514 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3516 struct tdb_validation_status *state)
3518 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3524 (void)centry_string( centry, mem_ctx );
3526 centry_free(centry);
3528 if (!(state->success)) {
3531 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3535 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3537 struct tdb_validation_status *state)
3539 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3545 (void)centry_string( centry, mem_ctx );
3547 centry_free(centry);
3549 if (!(state->success)) {
3552 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3556 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3557 struct tdb_validation_status *state)
3559 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3560 int32 num_domains, i;
3566 num_domains = centry_uint32(centry);
3568 for (i=0; i< num_domains; i++) {
3570 (void)centry_string(centry, mem_ctx);
3571 (void)centry_string(centry, mem_ctx);
3572 (void)centry_sid(centry, &sid);
3575 centry_free(centry);
3577 if (!(state->success)) {
3580 DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3584 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3586 struct tdb_validation_status *state)
3588 if (dbuf.dsize == 0) {
3589 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3590 "key %s (len ==0) ?\n", keystr));
3591 state->bad_entry = true;
3592 state->success = false;
3596 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3597 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3601 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3602 struct tdb_validation_status *state)
3604 if (dbuf.dsize != 4) {
3605 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3606 keystr, (unsigned int)dbuf.dsize ));
3607 state->bad_entry = true;
3608 state->success = false;
3611 DEBUG(10,("validate_offline: %s ok\n", keystr));
3615 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3616 struct tdb_validation_status *state)
3618 if (dbuf.dsize != 4) {
3619 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3620 "key %s (len %u != 4) ?\n",
3621 keystr, (unsigned int)dbuf.dsize));
3622 state->bad_entry = true;
3623 state->success = false;
3627 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3631 /***********************************************************************
3632 A list of all possible cache tdb keys with associated validation
3634 ***********************************************************************/
3636 struct key_val_struct {
3637 const char *keyname;
3638 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3640 {"SEQNUM/", validate_seqnum},
3641 {"NS/", validate_ns},
3642 {"SN/", validate_sn},
3644 {"LOC_POL/", validate_loc_pol},
3645 {"PWD_POL/", validate_pwd_pol},
3646 {"CRED/", validate_cred},
3647 {"UL/", validate_ul},
3648 {"GL/", validate_gl},
3649 {"UG/", validate_ug},
3650 {"UA", validate_ua},
3651 {"GM/", validate_gm},
3652 {"DR/", validate_dr},
3653 {"DE/", validate_de},
3654 {"NSS/PWINFO/", validate_pwinfo},
3655 {"TRUSTDOMS/", validate_trustdoms},
3656 {"TRUSTDOMCACHE/", validate_trustdomcache},
3657 {"NSS/NA/", validate_nss_na},
3658 {"NSS/AN/", validate_nss_an},
3659 {"WINBINDD_OFFLINE", validate_offline},
3660 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3664 /***********************************************************************
3665 Function to look at every entry in the tdb and validate it as far as
3667 ***********************************************************************/
3669 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3672 unsigned int max_key_len = 1024;
3673 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3675 /* Paranoia check. */
3676 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3677 max_key_len = 1024 * 1024;
3679 if (kbuf.dsize > max_key_len) {
3680 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3682 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3686 for (i = 0; key_val[i].keyname; i++) {
3687 size_t namelen = strlen(key_val[i].keyname);
3688 if (kbuf.dsize >= namelen && (
3689 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3690 TALLOC_CTX *mem_ctx;
3694 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3698 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3699 keystr[kbuf.dsize] = '\0';
3701 mem_ctx = talloc_init("validate_ctx");
3707 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3711 talloc_destroy(mem_ctx);
3716 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3717 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3718 DEBUG(0,("data :\n"));
3719 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3720 v_state->unknown_key = true;
3721 v_state->success = false;
3722 return 1; /* terminate. */
3725 static void validate_panic(const char *const why)
3727 DEBUG(0,("validating cache: would panic %s\n", why ));
3728 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3732 /***********************************************************************
3733 Try and validate every entry in the winbindd cache. If we fail here,
3734 delete the cache tdb and return non-zero.
3735 ***********************************************************************/
3737 int winbindd_validate_cache(void)
3740 const char *tdb_path = cache_path("winbindd_cache.tdb");
3741 TDB_CONTEXT *tdb = NULL;
3743 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3744 smb_panic_fn = validate_panic;
3747 tdb = tdb_open_log(tdb_path,
3748 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3749 ( lp_winbind_offline_logon()
3751 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3755 DEBUG(0, ("winbindd_validate_cache: "
3756 "error opening/initializing tdb\n"));
3761 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3764 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3765 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3770 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3771 smb_panic_fn = smb_panic;
3775 /***********************************************************************
3776 Try and validate every entry in the winbindd cache.
3777 ***********************************************************************/
3779 int winbindd_validate_cache_nobackup(void)
3782 const char *tdb_path = cache_path("winbindd_cache.tdb");
3784 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3785 smb_panic_fn = validate_panic;
3788 if (wcache == NULL || wcache->tdb == NULL) {
3789 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3791 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3795 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3799 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3801 smb_panic_fn = smb_panic;
3805 bool winbindd_cache_validate_and_initialize(void)
3807 close_winbindd_cache();
3809 if (lp_winbind_offline_logon()) {
3810 if (winbindd_validate_cache() < 0) {
3811 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3812 "could be restored.\n"));
3816 return initialize_winbindd_cache();
3819 /*********************************************************************
3820 ********************************************************************/
3822 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3823 struct winbindd_tdc_domain **domains,
3824 size_t *num_domains )
3826 struct winbindd_tdc_domain *list = NULL;
3829 bool set_only = false;
3831 /* don't allow duplicates */
3836 for ( i=0; i< (*num_domains); i++ ) {
3837 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3838 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3849 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3852 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
3853 struct winbindd_tdc_domain,
3858 ZERO_STRUCT( list[idx] );
3864 list[idx].domain_name = talloc_strdup( list, new_dom->name );
3865 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3867 if ( !is_null_sid( &new_dom->sid ) ) {
3868 sid_copy( &list[idx].sid, &new_dom->sid );
3870 sid_copy(&list[idx].sid, &global_sid_NULL);
3873 if ( new_dom->domain_flags != 0x0 )
3874 list[idx].trust_flags = new_dom->domain_flags;
3876 if ( new_dom->domain_type != 0x0 )
3877 list[idx].trust_type = new_dom->domain_type;
3879 if ( new_dom->domain_trust_attribs != 0x0 )
3880 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3884 *num_domains = idx + 1;
3890 /*********************************************************************
3891 ********************************************************************/
3893 static TDB_DATA make_tdc_key( const char *domain_name )
3895 char *keystr = NULL;
3896 TDB_DATA key = { NULL, 0 };
3898 if ( !domain_name ) {
3899 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3903 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3906 key = string_term_tdb_data(keystr);
3911 /*********************************************************************
3912 ********************************************************************/
3914 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
3916 unsigned char **buf )
3918 unsigned char *buffer = NULL;
3923 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3931 /* Store the number of array items first */
3932 len += tdb_pack( buffer+len, buflen-len, "d",
3935 /* now pack each domain trust record */
3936 for ( i=0; i<num_domains; i++ ) {
3941 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3942 domains[i].domain_name,
3943 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3946 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3947 domains[i].domain_name,
3948 domains[i].dns_name,
3949 sid_to_fstring(tmp, &domains[i].sid),
3950 domains[i].trust_flags,
3951 domains[i].trust_attribs,
3952 domains[i].trust_type );
3955 if ( buflen < len ) {
3957 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3958 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3972 /*********************************************************************
3973 ********************************************************************/
3975 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
3976 struct winbindd_tdc_domain **domains )
3978 fstring domain_name, dns_name, sid_string;
3979 uint32 type, attribs, flags;
3983 struct winbindd_tdc_domain *list = NULL;
3985 /* get the number of domains */
3986 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3988 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3992 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3994 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3998 for ( i=0; i<num_domains; i++ ) {
3999 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4008 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4009 TALLOC_FREE( list );
4013 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4014 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4015 domain_name, dns_name, sid_string,
4016 flags, attribs, type));
4018 list[i].domain_name = talloc_strdup( list, domain_name );
4019 list[i].dns_name = talloc_strdup( list, dns_name );
4020 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4021 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4024 list[i].trust_flags = flags;
4025 list[i].trust_attribs = attribs;
4026 list[i].trust_type = type;
4034 /*********************************************************************
4035 ********************************************************************/
4037 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4039 TDB_DATA key = make_tdc_key( lp_workgroup() );
4040 TDB_DATA data = { NULL, 0 };
4046 /* See if we were asked to delete the cache entry */
4049 ret = tdb_delete( wcache->tdb, key );
4053 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4060 ret = tdb_store( wcache->tdb, key, data, 0 );
4063 SAFE_FREE( data.dptr );
4064 SAFE_FREE( key.dptr );
4066 return ( ret != -1 );
4069 /*********************************************************************
4070 ********************************************************************/
4072 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4074 TDB_DATA key = make_tdc_key( lp_workgroup() );
4075 TDB_DATA data = { NULL, 0 };
4083 data = tdb_fetch( wcache->tdb, key );
4085 SAFE_FREE( key.dptr );
4090 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4092 SAFE_FREE( data.dptr );
4100 /*********************************************************************
4101 ********************************************************************/
4103 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4105 struct winbindd_tdc_domain *dom_list = NULL;
4106 size_t num_domains = 0;
4109 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4110 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4111 domain->name, domain->alt_name,
4112 sid_string_dbg(&domain->sid),
4113 domain->domain_flags,
4114 domain->domain_trust_attribs,
4115 domain->domain_type));
4117 if ( !init_wcache() ) {
4121 /* fetch the list */
4123 wcache_tdc_fetch_list( &dom_list, &num_domains );
4125 /* add the new domain */
4127 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4131 /* pack the domain */
4133 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4141 TALLOC_FREE( dom_list );
4146 /*********************************************************************
4147 ********************************************************************/
4149 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4151 struct winbindd_tdc_domain *dom_list = NULL;
4152 size_t num_domains = 0;
4154 struct winbindd_tdc_domain *d = NULL;
4156 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4158 if ( !init_wcache() ) {
4162 /* fetch the list */
4164 wcache_tdc_fetch_list( &dom_list, &num_domains );
4166 for ( i=0; i<num_domains; i++ ) {
4167 if ( strequal(name, dom_list[i].domain_name) ||
4168 strequal(name, dom_list[i].dns_name) )
4170 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4173 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4177 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4178 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4179 sid_copy( &d->sid, &dom_list[i].sid );
4180 d->trust_flags = dom_list[i].trust_flags;
4181 d->trust_type = dom_list[i].trust_type;
4182 d->trust_attribs = dom_list[i].trust_attribs;
4188 TALLOC_FREE( dom_list );
4194 /*********************************************************************
4195 ********************************************************************/
4197 void wcache_tdc_clear( void )
4199 if ( !init_wcache() )
4202 wcache_tdc_store_list( NULL, 0 );
4208 /*********************************************************************
4209 ********************************************************************/
4211 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4213 const DOM_SID *user_sid,
4214 const char *homedir,
4219 struct cache_entry *centry;
4222 if ( (centry = centry_start(domain, status)) == NULL )
4225 centry_put_string( centry, homedir );
4226 centry_put_string( centry, shell );
4227 centry_put_string( centry, gecos );
4228 centry_put_uint32( centry, gid );
4230 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4232 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4234 centry_free(centry);
4237 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4238 const DOM_SID *user_sid,
4240 ADS_STRUCT *ads, LDAPMessage *msg,
4241 const char **homedir, const char **shell,
4242 const char **gecos, gid_t *p_gid)
4244 struct winbind_cache *cache = get_cache(domain);
4245 struct cache_entry *centry = NULL;
4252 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4253 sid_to_fstring(tmp, user_sid));
4258 *homedir = centry_string( centry, ctx );
4259 *shell = centry_string( centry, ctx );
4260 *gecos = centry_string( centry, ctx );
4261 *p_gid = centry_uint32( centry );
4263 centry_free(centry);
4265 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4266 sid_string_dbg(user_sid)));
4268 return NT_STATUS_OK;
4272 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4273 homedir, shell, gecos, p_gid );
4275 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4277 if ( NT_STATUS_IS_OK(nt_status) ) {
4278 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4279 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4280 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4281 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4283 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4284 *homedir, *shell, *gecos, *p_gid );
4287 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4288 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4290 set_domain_offline( domain );
4297 /* the cache backend methods are exposed via this structure */
4298 struct winbindd_methods cache_methods = {
4316 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4317 uint32_t opnum, const DATA_BLOB *req,
4323 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4327 keylen = talloc_get_size(key) - 1;
4329 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4333 memcpy(key + keylen, req->data, req->length);
4335 pkey->dptr = (uint8_t *)key;
4336 pkey->dsize = talloc_get_size(key);
4340 static bool wcache_opnum_cacheable(uint32_t opnum)
4343 case NDR_WBINT_PING:
4344 case NDR_WBINT_QUERYSEQUENCENUMBER:
4350 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4351 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4356 if (!wcache_opnum_cacheable(opnum)) {
4360 if (wcache->tdb == NULL) {
4364 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4367 data = tdb_fetch(wcache->tdb, key);
4368 TALLOC_FREE(key.dptr);
4370 if (data.dptr == NULL) {
4373 if (data.dsize < 4) {
4377 if (IS_DOMAIN_ONLINE(domain)) {
4378 uint32_t entry_seqnum, dom_seqnum, last_check;
4380 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4384 entry_seqnum = IVAL(data.dptr, 0);
4385 if (entry_seqnum != dom_seqnum) {
4386 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4387 (int)entry_seqnum));
4392 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4394 if (resp->data == NULL) {
4395 DEBUG(10, ("talloc failed\n"));
4398 resp->length = data.dsize - 4;
4402 SAFE_FREE(data.dptr);
4406 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4407 const DATA_BLOB *req, const DATA_BLOB *resp)
4410 uint32_t dom_seqnum, last_check;
4412 if (!wcache_opnum_cacheable(opnum)) {
4416 if (wcache->tdb == NULL) {
4420 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4421 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4426 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4430 data.dsize = resp->length + 4;
4431 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4432 if (data.dptr == NULL) {
4436 SIVAL(data.dptr, 0, dom_seqnum);
4437 memcpy(data.dptr+4, resp->data, resp->length);
4439 tdb_store(wcache->tdb, key, data, 0);
4442 TALLOC_FREE(key.dptr);