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);
1392 *num_entries = centry_uint32(centry);
1394 if (*num_entries == 0)
1397 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1399 smb_panic_fn("query_user_list out of memory");
1401 for (i=0; i<(*num_entries); i++) {
1402 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1403 (*info)[i].full_name = centry_string(centry, mem_ctx);
1404 (*info)[i].homedir = centry_string(centry, mem_ctx);
1405 (*info)[i].shell = centry_string(centry, mem_ctx);
1406 centry_sid(centry, &(*info)[i].user_sid);
1407 centry_sid(centry, &(*info)[i].group_sid);
1411 status = centry->status;
1413 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1414 domain->name, nt_errstr(status) ));
1416 centry_free(centry);
1423 /* Return status value returned by seq number check */
1425 if (!NT_STATUS_IS_OK(domain->last_status))
1426 return domain->last_status;
1428 /* Put the query_user_list() in a retry loop. There appears to be
1429 * some bug either with Windows 2000 or Samba's handling of large
1430 * rpc replies. This manifests itself as sudden disconnection
1431 * at a random point in the enumeration of a large (60k) user list.
1432 * The retry loop simply tries the operation again. )-: It's not
1433 * pretty but an acceptable workaround until we work out what the
1434 * real problem is. */
1439 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1442 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1443 if (!NT_STATUS_IS_OK(status)) {
1444 DEBUG(3, ("query_user_list: returned 0x%08x, "
1445 "retrying\n", NT_STATUS_V(status)));
1447 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1448 DEBUG(3, ("query_user_list: flushing "
1449 "connection cache\n"));
1450 invalidate_cm_connection(&domain->conn);
1452 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1453 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1454 if (!domain->internal) {
1455 set_domain_offline(domain);
1457 /* store partial response. */
1458 if (*num_entries > 0) {
1460 * humm, what about the status used for cache?
1461 * Should it be NT_STATUS_OK?
1466 * domain is offline now, and there is no user entries,
1467 * try to fetch from cache again.
1469 if (cache->tdb && !domain->online && !domain->internal) {
1470 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1471 /* partial response... */
1475 goto do_fetch_cache;
1482 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1486 refresh_sequence_number(domain, false);
1487 if (!NT_STATUS_IS_OK(status)) {
1490 centry = centry_start(domain, status);
1493 centry_put_uint32(centry, *num_entries);
1494 for (i=0; i<(*num_entries); i++) {
1495 centry_put_string(centry, (*info)[i].acct_name);
1496 centry_put_string(centry, (*info)[i].full_name);
1497 centry_put_string(centry, (*info)[i].homedir);
1498 centry_put_string(centry, (*info)[i].shell);
1499 centry_put_sid(centry, &(*info)[i].user_sid);
1500 centry_put_sid(centry, &(*info)[i].group_sid);
1501 if (domain->backend && domain->backend->consistent) {
1502 /* when the backend is consistent we can pre-prime some mappings */
1503 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1505 (*info)[i].acct_name,
1506 &(*info)[i].user_sid,
1508 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1509 &(*info)[i].user_sid,
1511 (*info)[i].acct_name,
1513 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1516 centry_end(centry, "UL/%s", domain->name);
1517 centry_free(centry);
1523 /* list all domain groups */
1524 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1525 TALLOC_CTX *mem_ctx,
1526 uint32 *num_entries,
1527 struct acct_info **info)
1529 struct winbind_cache *cache = get_cache(domain);
1530 struct cache_entry *centry = NULL;
1537 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1542 *num_entries = centry_uint32(centry);
1544 if (*num_entries == 0)
1547 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1549 smb_panic_fn("enum_dom_groups out of memory");
1551 for (i=0; i<(*num_entries); i++) {
1552 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1553 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1554 (*info)[i].rid = centry_uint32(centry);
1558 status = centry->status;
1560 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1561 domain->name, nt_errstr(status) ));
1563 centry_free(centry);
1570 /* Return status value returned by seq number check */
1572 if (!NT_STATUS_IS_OK(domain->last_status))
1573 return domain->last_status;
1575 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1578 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1580 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1581 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1582 if (!domain->internal) {
1583 set_domain_offline(domain);
1587 !domain->internal) {
1588 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1590 goto do_fetch_cache;
1595 refresh_sequence_number(domain, false);
1596 if (!NT_STATUS_IS_OK(status)) {
1599 centry = centry_start(domain, status);
1602 centry_put_uint32(centry, *num_entries);
1603 for (i=0; i<(*num_entries); i++) {
1604 centry_put_string(centry, (*info)[i].acct_name);
1605 centry_put_string(centry, (*info)[i].acct_desc);
1606 centry_put_uint32(centry, (*info)[i].rid);
1608 centry_end(centry, "GL/%s/domain", domain->name);
1609 centry_free(centry);
1615 /* list all domain groups */
1616 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1617 TALLOC_CTX *mem_ctx,
1618 uint32 *num_entries,
1619 struct acct_info **info)
1621 struct winbind_cache *cache = get_cache(domain);
1622 struct cache_entry *centry = NULL;
1629 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1634 *num_entries = centry_uint32(centry);
1636 if (*num_entries == 0)
1639 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1641 smb_panic_fn("enum_dom_groups out of memory");
1643 for (i=0; i<(*num_entries); i++) {
1644 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1645 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1646 (*info)[i].rid = centry_uint32(centry);
1651 /* If we are returning cached data and the domain controller
1652 is down then we don't know whether the data is up to date
1653 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1656 if (wcache_server_down(domain)) {
1657 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1658 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1660 status = centry->status;
1662 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1663 domain->name, nt_errstr(status) ));
1665 centry_free(centry);
1672 /* Return status value returned by seq number check */
1674 if (!NT_STATUS_IS_OK(domain->last_status))
1675 return domain->last_status;
1677 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1680 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1682 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1683 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1684 if (!domain->internal) {
1685 set_domain_offline(domain);
1688 !domain->internal &&
1690 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1692 goto do_fetch_cache;
1697 refresh_sequence_number(domain, false);
1698 if (!NT_STATUS_IS_OK(status)) {
1701 centry = centry_start(domain, status);
1704 centry_put_uint32(centry, *num_entries);
1705 for (i=0; i<(*num_entries); i++) {
1706 centry_put_string(centry, (*info)[i].acct_name);
1707 centry_put_string(centry, (*info)[i].acct_desc);
1708 centry_put_uint32(centry, (*info)[i].rid);
1710 centry_end(centry, "GL/%s/local", domain->name);
1711 centry_free(centry);
1717 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1718 const char *domain_name,
1720 struct dom_sid *sid,
1721 enum lsa_SidType *type)
1723 struct winbind_cache *cache = get_cache(domain);
1724 struct cache_entry *centry;
1728 if (cache->tdb == NULL) {
1729 return NT_STATUS_NOT_FOUND;
1732 uname = talloc_strdup_upper(talloc_tos(), name);
1733 if (uname == NULL) {
1734 return NT_STATUS_NO_MEMORY;
1737 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1739 if (centry == NULL) {
1740 return NT_STATUS_NOT_FOUND;
1743 status = centry->status;
1744 if (NT_STATUS_IS_OK(status)) {
1745 *type = (enum lsa_SidType)centry_uint32(centry);
1746 centry_sid(centry, sid);
1749 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1750 "%s\n", domain->name, nt_errstr(status) ));
1752 centry_free(centry);
1756 /* convert a single name to a sid in a domain */
1757 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1758 TALLOC_CTX *mem_ctx,
1759 const char *domain_name,
1763 enum lsa_SidType *type)
1767 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1768 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1774 /* If the seq number check indicated that there is a problem
1775 * with this DC, then return that status... except for
1776 * access_denied. This is special because the dc may be in
1777 * "restrict anonymous = 1" mode, in which case it will deny
1778 * most unauthenticated operations, but *will* allow the LSA
1779 * name-to-sid that we try as a fallback. */
1781 if (!(NT_STATUS_IS_OK(domain->last_status)
1782 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1783 return domain->last_status;
1785 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1788 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1789 name, flags, sid, type);
1791 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1792 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1793 if (!domain->internal) {
1794 set_domain_offline(domain);
1796 if (!domain->internal &&
1798 NTSTATUS cache_status;
1799 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1800 return cache_status;
1804 refresh_sequence_number(domain, false);
1806 if (domain->online &&
1807 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1808 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1810 /* Only save the reverse mapping if this was not a UPN */
1811 if (!strchr(name, '@')) {
1812 strupper_m(CONST_DISCARD(char *,domain_name));
1813 strlower_m(CONST_DISCARD(char *,name));
1814 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1821 NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1822 const struct dom_sid *sid,
1823 TALLOC_CTX *mem_ctx,
1826 enum lsa_SidType *type)
1828 struct winbind_cache *cache = get_cache(domain);
1829 struct cache_entry *centry;
1833 if (cache->tdb == NULL) {
1834 return NT_STATUS_NOT_FOUND;
1837 sid_string = sid_string_tos(sid);
1838 if (sid_string == NULL) {
1839 return NT_STATUS_NO_MEMORY;
1842 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1843 TALLOC_FREE(sid_string);
1844 if (centry == NULL) {
1845 return NT_STATUS_NOT_FOUND;
1848 if (NT_STATUS_IS_OK(centry->status)) {
1849 *type = (enum lsa_SidType)centry_uint32(centry);
1850 *domain_name = centry_string(centry, mem_ctx);
1851 *name = centry_string(centry, mem_ctx);
1854 status = centry->status;
1855 centry_free(centry);
1857 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1858 "%s\n", domain->name, nt_errstr(status) ));
1863 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1865 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1866 TALLOC_CTX *mem_ctx,
1870 enum lsa_SidType *type)
1874 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1876 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1881 *domain_name = NULL;
1883 /* If the seq number check indicated that there is a problem
1884 * with this DC, then return that status... except for
1885 * access_denied. This is special because the dc may be in
1886 * "restrict anonymous = 1" mode, in which case it will deny
1887 * most unauthenticated operations, but *will* allow the LSA
1888 * sid-to-name that we try as a fallback. */
1890 if (!(NT_STATUS_IS_OK(domain->last_status)
1891 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1892 return domain->last_status;
1894 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1897 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1899 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1900 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1901 if (!domain->internal) {
1902 set_domain_offline(domain);
1904 if (!domain->internal &&
1906 NTSTATUS cache_status;
1907 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1908 domain_name, name, type);
1909 return cache_status;
1913 refresh_sequence_number(domain, false);
1914 if (!NT_STATUS_IS_OK(status)) {
1917 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1919 /* We can't save the name to sid mapping here, as with sid history a
1920 * later name2sid would give the wrong sid. */
1925 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1926 TALLOC_CTX *mem_ctx,
1927 const DOM_SID *domain_sid,
1932 enum lsa_SidType **types)
1934 struct winbind_cache *cache = get_cache(domain);
1936 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1940 *domain_name = NULL;
1948 if (num_rids == 0) {
1949 return NT_STATUS_OK;
1952 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1953 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1955 if ((*names == NULL) || (*types == NULL)) {
1956 result = NT_STATUS_NO_MEMORY;
1960 have_mapped = have_unmapped = false;
1962 for (i=0; i<num_rids; i++) {
1964 struct cache_entry *centry;
1967 if (!sid_compose(&sid, domain_sid, rids[i])) {
1968 result = NT_STATUS_INTERNAL_ERROR;
1972 centry = wcache_fetch(cache, domain, "SN/%s",
1973 sid_to_fstring(tmp, &sid));
1978 (*types)[i] = SID_NAME_UNKNOWN;
1979 (*names)[i] = talloc_strdup(*names, "");
1981 if (NT_STATUS_IS_OK(centry->status)) {
1984 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1986 dom = centry_string(centry, mem_ctx);
1987 if (*domain_name == NULL) {
1993 (*names)[i] = centry_string(centry, *names);
1995 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
1996 have_unmapped = true;
1999 /* something's definitely wrong */
2000 result = centry->status;
2004 centry_free(centry);
2008 return NT_STATUS_NONE_MAPPED;
2010 if (!have_unmapped) {
2011 return NT_STATUS_OK;
2013 return STATUS_SOME_UNMAPPED;
2017 TALLOC_FREE(*names);
2018 TALLOC_FREE(*types);
2020 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2021 rids, num_rids, domain_name,
2024 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2025 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2026 if (!domain->internal) {
2027 set_domain_offline(domain);
2030 !domain->internal &&
2032 have_mapped = have_unmapped = false;
2034 for (i=0; i<num_rids; i++) {
2036 struct cache_entry *centry;
2039 if (!sid_compose(&sid, domain_sid, rids[i])) {
2040 result = NT_STATUS_INTERNAL_ERROR;
2044 centry = wcache_fetch(cache, domain, "SN/%s",
2045 sid_to_fstring(tmp, &sid));
2047 (*types)[i] = SID_NAME_UNKNOWN;
2048 (*names)[i] = talloc_strdup(*names, "");
2052 (*types)[i] = SID_NAME_UNKNOWN;
2053 (*names)[i] = talloc_strdup(*names, "");
2055 if (NT_STATUS_IS_OK(centry->status)) {
2058 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2060 dom = centry_string(centry, mem_ctx);
2061 if (*domain_name == NULL) {
2067 (*names)[i] = centry_string(centry, *names);
2069 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2070 have_unmapped = true;
2073 /* something's definitely wrong */
2074 result = centry->status;
2078 centry_free(centry);
2082 return NT_STATUS_NONE_MAPPED;
2084 if (!have_unmapped) {
2085 return NT_STATUS_OK;
2087 return STATUS_SOME_UNMAPPED;
2091 None of the queried rids has been found so save all negative entries
2093 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2094 for (i = 0; i < num_rids; i++) {
2096 const char *name = "";
2097 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2098 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2100 if (!sid_compose(&sid, domain_sid, rids[i])) {
2101 return NT_STATUS_INTERNAL_ERROR;
2104 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2112 Some or all of the queried rids have been found.
2114 if (!NT_STATUS_IS_OK(result) &&
2115 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2119 refresh_sequence_number(domain, false);
2121 for (i=0; i<num_rids; i++) {
2125 if (!sid_compose(&sid, domain_sid, rids[i])) {
2126 result = NT_STATUS_INTERNAL_ERROR;
2130 status = (*types)[i] == SID_NAME_UNKNOWN ?
2131 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2133 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2134 (*names)[i], (*types)[i]);
2140 TALLOC_FREE(*names);
2141 TALLOC_FREE(*types);
2145 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2146 TALLOC_CTX *mem_ctx,
2147 const struct dom_sid *user_sid,
2148 struct wbint_userinfo *info)
2150 struct winbind_cache *cache = get_cache(domain);
2151 struct cache_entry *centry = NULL;
2155 if (cache->tdb == NULL) {
2156 return NT_STATUS_NOT_FOUND;
2159 sid_string = sid_string_tos(user_sid);
2160 if (sid_string == NULL) {
2161 return NT_STATUS_NO_MEMORY;
2164 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2165 TALLOC_FREE(sid_string);
2166 if (centry == NULL) {
2167 return NT_STATUS_NOT_FOUND;
2171 * If we have an access denied cache entry and a cached info3
2172 * in the samlogon cache then do a query. This will force the
2173 * rpc back end to return the info3 data.
2176 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2177 netsamlogon_cache_have(user_sid)) {
2178 DEBUG(10, ("query_user: cached access denied and have cached "
2180 domain->last_status = NT_STATUS_OK;
2181 centry_free(centry);
2182 return NT_STATUS_NOT_FOUND;
2185 /* if status is not ok then this is a negative hit
2186 and the rest of the data doesn't matter */
2187 status = centry->status;
2188 if (NT_STATUS_IS_OK(status)) {
2189 info->acct_name = centry_string(centry, mem_ctx);
2190 info->full_name = centry_string(centry, mem_ctx);
2191 info->homedir = centry_string(centry, mem_ctx);
2192 info->shell = centry_string(centry, mem_ctx);
2193 info->primary_gid = centry_uint32(centry);
2194 centry_sid(centry, &info->user_sid);
2195 centry_sid(centry, &info->group_sid);
2198 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2199 "%s\n", domain->name, nt_errstr(status) ));
2201 centry_free(centry);
2205 /* Lookup user information from a rid */
2206 static NTSTATUS query_user(struct winbindd_domain *domain,
2207 TALLOC_CTX *mem_ctx,
2208 const DOM_SID *user_sid,
2209 struct wbint_userinfo *info)
2213 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2214 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2220 /* Return status value returned by seq number check */
2222 if (!NT_STATUS_IS_OK(domain->last_status))
2223 return domain->last_status;
2225 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2228 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2230 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2231 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2232 if (!domain->internal) {
2233 set_domain_offline(domain);
2235 if (!domain->internal &&
2237 NTSTATUS cache_status;
2238 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2239 return cache_status;
2243 refresh_sequence_number(domain, false);
2244 if (!NT_STATUS_IS_OK(status)) {
2247 wcache_save_user(domain, status, info);
2252 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2253 TALLOC_CTX *mem_ctx,
2254 const struct dom_sid *user_sid,
2255 uint32_t *pnum_sids,
2256 struct dom_sid **psids)
2258 struct winbind_cache *cache = get_cache(domain);
2259 struct cache_entry *centry = NULL;
2261 uint32_t i, num_sids;
2262 struct dom_sid *sids;
2265 if (cache->tdb == NULL) {
2266 return NT_STATUS_NOT_FOUND;
2269 centry = wcache_fetch(cache, domain, "UG/%s",
2270 sid_to_fstring(sid_string, user_sid));
2271 if (centry == NULL) {
2272 return NT_STATUS_NOT_FOUND;
2275 /* If we have an access denied cache entry and a cached info3 in the
2276 samlogon cache then do a query. This will force the rpc back end
2277 to return the info3 data. */
2279 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2280 && netsamlogon_cache_have(user_sid)) {
2281 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2283 domain->last_status = NT_STATUS_OK;
2284 centry_free(centry);
2285 return NT_STATUS_NOT_FOUND;
2288 num_sids = centry_uint32(centry);
2289 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2291 centry_free(centry);
2292 return NT_STATUS_NO_MEMORY;
2295 for (i=0; i<num_sids; i++) {
2296 centry_sid(centry, &sids[i]);
2299 status = centry->status;
2301 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2302 "status: %s\n", domain->name, nt_errstr(status)));
2304 centry_free(centry);
2306 *pnum_sids = num_sids;
2311 /* Lookup groups a user is a member of. */
2312 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2313 TALLOC_CTX *mem_ctx,
2314 const DOM_SID *user_sid,
2315 uint32 *num_groups, DOM_SID **user_gids)
2317 struct cache_entry *centry = NULL;
2322 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2323 num_groups, user_gids);
2324 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2329 (*user_gids) = NULL;
2331 /* Return status value returned by seq number check */
2333 if (!NT_STATUS_IS_OK(domain->last_status))
2334 return domain->last_status;
2336 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2339 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2341 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2342 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2343 if (!domain->internal) {
2344 set_domain_offline(domain);
2346 if (!domain->internal &&
2348 NTSTATUS cache_status;
2349 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2350 num_groups, user_gids);
2351 return cache_status;
2354 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2358 refresh_sequence_number(domain, false);
2359 if (!NT_STATUS_IS_OK(status)) {
2362 centry = centry_start(domain, status);
2366 centry_put_uint32(centry, *num_groups);
2367 for (i=0; i<(*num_groups); i++) {
2368 centry_put_sid(centry, &(*user_gids)[i]);
2371 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2372 centry_free(centry);
2378 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2379 const struct dom_sid *sids)
2384 sidlist = talloc_strdup(mem_ctx, "");
2385 if (sidlist == NULL) {
2388 for (i=0; i<num_sids; i++) {
2390 sidlist = talloc_asprintf_append_buffer(
2391 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2392 if (sidlist == NULL) {
2399 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2400 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2401 const struct dom_sid *sids,
2402 uint32_t *pnum_aliases, uint32_t **paliases)
2404 struct winbind_cache *cache = get_cache(domain);
2405 struct cache_entry *centry = NULL;
2406 uint32_t num_aliases;
2412 if (cache->tdb == NULL) {
2413 return NT_STATUS_NOT_FOUND;
2416 if (num_sids == 0) {
2419 return NT_STATUS_OK;
2422 /* We need to cache indexed by the whole list of SIDs, the aliases
2423 * resulting might come from any of the SIDs. */
2425 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2426 if (sidlist == NULL) {
2427 return NT_STATUS_NO_MEMORY;
2430 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2431 TALLOC_FREE(sidlist);
2432 if (centry == NULL) {
2433 return NT_STATUS_NOT_FOUND;
2436 num_aliases = centry_uint32(centry);
2437 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2438 if (aliases == NULL) {
2439 centry_free(centry);
2440 return NT_STATUS_NO_MEMORY;
2443 for (i=0; i<num_aliases; i++) {
2444 aliases[i] = centry_uint32(centry);
2447 status = centry->status;
2449 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2450 "status %s\n", domain->name, nt_errstr(status)));
2452 centry_free(centry);
2454 *pnum_aliases = num_aliases;
2455 *paliases = aliases;
2460 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2461 TALLOC_CTX *mem_ctx,
2462 uint32 num_sids, const DOM_SID *sids,
2463 uint32 *num_aliases, uint32 **alias_rids)
2465 struct cache_entry *centry = NULL;
2470 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2471 num_aliases, alias_rids);
2472 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2477 (*alias_rids) = NULL;
2479 if (!NT_STATUS_IS_OK(domain->last_status))
2480 return domain->last_status;
2482 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2483 "for domain %s\n", domain->name ));
2485 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2486 if (sidlist == NULL) {
2487 return NT_STATUS_NO_MEMORY;
2490 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2492 num_aliases, alias_rids);
2494 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2495 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2496 if (!domain->internal) {
2497 set_domain_offline(domain);
2499 if (!domain->internal &&
2501 NTSTATUS cache_status;
2502 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2503 sids, num_aliases, alias_rids);
2504 return cache_status;
2508 refresh_sequence_number(domain, false);
2509 if (!NT_STATUS_IS_OK(status)) {
2512 centry = centry_start(domain, status);
2515 centry_put_uint32(centry, *num_aliases);
2516 for (i=0; i<(*num_aliases); i++)
2517 centry_put_uint32(centry, (*alias_rids)[i]);
2518 centry_end(centry, "UA%s", sidlist);
2519 centry_free(centry);
2525 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2526 TALLOC_CTX *mem_ctx,
2527 const struct dom_sid *group_sid,
2528 uint32_t *num_names,
2529 struct dom_sid **sid_mem, char ***names,
2530 uint32_t **name_types)
2532 struct winbind_cache *cache = get_cache(domain);
2533 struct cache_entry *centry = NULL;
2538 if (cache->tdb == NULL) {
2539 return NT_STATUS_NOT_FOUND;
2542 sid_string = sid_string_tos(group_sid);
2543 if (sid_string == NULL) {
2544 return NT_STATUS_NO_MEMORY;
2547 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2548 TALLOC_FREE(sid_string);
2549 if (centry == NULL) {
2550 return NT_STATUS_NOT_FOUND;
2557 *num_names = centry_uint32(centry);
2558 if (*num_names == 0) {
2559 centry_free(centry);
2560 return NT_STATUS_OK;
2563 *sid_mem = talloc_array(mem_ctx, DOM_SID, *num_names);
2564 *names = talloc_array(mem_ctx, char *, *num_names);
2565 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2567 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2568 TALLOC_FREE(*sid_mem);
2569 TALLOC_FREE(*names);
2570 TALLOC_FREE(*name_types);
2571 centry_free(centry);
2572 return NT_STATUS_NO_MEMORY;
2575 for (i=0; i<(*num_names); i++) {
2576 centry_sid(centry, &(*sid_mem)[i]);
2577 (*names)[i] = centry_string(centry, mem_ctx);
2578 (*name_types)[i] = centry_uint32(centry);
2581 status = centry->status;
2583 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2584 "status: %s\n", domain->name, nt_errstr(status)));
2586 centry_free(centry);
2590 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2591 TALLOC_CTX *mem_ctx,
2592 const DOM_SID *group_sid,
2593 enum lsa_SidType type,
2595 DOM_SID **sid_mem, char ***names,
2596 uint32 **name_types)
2598 struct cache_entry *centry = NULL;
2603 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2604 sid_mem, names, name_types);
2605 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2612 (*name_types) = NULL;
2614 /* Return status value returned by seq number check */
2616 if (!NT_STATUS_IS_OK(domain->last_status))
2617 return domain->last_status;
2619 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2622 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2624 sid_mem, names, name_types);
2626 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2627 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2628 if (!domain->internal) {
2629 set_domain_offline(domain);
2631 if (!domain->internal &&
2633 NTSTATUS cache_status;
2634 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2635 num_names, sid_mem, names,
2637 return cache_status;
2641 refresh_sequence_number(domain, false);
2642 if (!NT_STATUS_IS_OK(status)) {
2645 centry = centry_start(domain, status);
2648 centry_put_uint32(centry, *num_names);
2649 for (i=0; i<(*num_names); i++) {
2650 centry_put_sid(centry, &(*sid_mem)[i]);
2651 centry_put_string(centry, (*names)[i]);
2652 centry_put_uint32(centry, (*name_types)[i]);
2654 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2655 centry_free(centry);
2661 /* find the sequence number for a domain */
2662 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2664 refresh_sequence_number(domain, false);
2666 *seq = domain->sequence_number;
2668 return NT_STATUS_OK;
2671 /* enumerate trusted domains
2672 * (we need to have the list of trustdoms in the cache when we go offline) -
2674 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2675 TALLOC_CTX *mem_ctx,
2676 struct netr_DomainTrustList *trusts)
2680 /* Return status value returned by seq number check */
2682 if (!NT_STATUS_IS_OK(domain->last_status))
2683 return domain->last_status;
2685 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2688 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2690 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2691 * so that the generic centry handling still applies correctly -
2694 if (!NT_STATUS_IS_ERR(status)) {
2695 status = NT_STATUS_OK;
2700 /* get lockout policy */
2701 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2702 TALLOC_CTX *mem_ctx,
2703 struct samr_DomInfo12 *policy)
2705 struct winbind_cache *cache = get_cache(domain);
2706 struct cache_entry *centry = NULL;
2712 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2718 policy->lockout_duration = centry_nttime(centry);
2719 policy->lockout_window = centry_nttime(centry);
2720 policy->lockout_threshold = centry_uint16(centry);
2722 status = centry->status;
2724 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2725 domain->name, nt_errstr(status) ));
2727 centry_free(centry);
2731 ZERO_STRUCTP(policy);
2733 /* Return status value returned by seq number check */
2735 if (!NT_STATUS_IS_OK(domain->last_status))
2736 return domain->last_status;
2738 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2741 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2743 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2744 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2745 if (!domain->internal) {
2746 set_domain_offline(domain);
2749 !domain->internal &&
2751 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2753 goto do_fetch_cache;
2758 refresh_sequence_number(domain, false);
2759 if (!NT_STATUS_IS_OK(status)) {
2762 wcache_save_lockout_policy(domain, status, policy);
2767 /* get password policy */
2768 static NTSTATUS password_policy(struct winbindd_domain *domain,
2769 TALLOC_CTX *mem_ctx,
2770 struct samr_DomInfo1 *policy)
2772 struct winbind_cache *cache = get_cache(domain);
2773 struct cache_entry *centry = NULL;
2779 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2785 policy->min_password_length = centry_uint16(centry);
2786 policy->password_history_length = centry_uint16(centry);
2787 policy->password_properties = centry_uint32(centry);
2788 policy->max_password_age = centry_nttime(centry);
2789 policy->min_password_age = centry_nttime(centry);
2791 status = centry->status;
2793 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2794 domain->name, nt_errstr(status) ));
2796 centry_free(centry);
2800 ZERO_STRUCTP(policy);
2802 /* Return status value returned by seq number check */
2804 if (!NT_STATUS_IS_OK(domain->last_status))
2805 return domain->last_status;
2807 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2810 status = domain->backend->password_policy(domain, mem_ctx, policy);
2812 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2813 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2814 if (!domain->internal) {
2815 set_domain_offline(domain);
2818 !domain->internal &&
2820 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2822 goto do_fetch_cache;
2827 refresh_sequence_number(domain, false);
2828 if (!NT_STATUS_IS_OK(status)) {
2831 wcache_save_password_policy(domain, status, policy);
2837 /* Invalidate cached user and group lists coherently */
2839 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2842 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2843 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2844 tdb_delete(the_tdb, kbuf);
2849 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2851 void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2852 struct netr_SamInfo3 *info3)
2855 fstring key_str, sid_string;
2856 struct winbind_cache *cache;
2858 /* dont clear cached U/SID and UG/SID entries when we want to logon
2861 if (lp_winbind_offline_logon()) {
2868 cache = get_cache(domain);
2874 sid_copy(&sid, info3->base.domain_sid);
2875 sid_append_rid(&sid, info3->base.rid);
2877 /* Clear U/SID cache entry */
2878 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2879 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2880 tdb_delete(cache->tdb, string_tdb_data(key_str));
2882 /* Clear UG/SID cache entry */
2883 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2884 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2885 tdb_delete(cache->tdb, string_tdb_data(key_str));
2887 /* Samba/winbindd never needs this. */
2888 netsamlogon_clear_cached_user(info3);
2891 bool wcache_invalidate_cache(void)
2893 struct winbindd_domain *domain;
2895 for (domain = domain_list(); domain; domain = domain->next) {
2896 struct winbind_cache *cache = get_cache(domain);
2898 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2899 "entries for %s\n", domain->name));
2902 tdb_traverse(cache->tdb, traverse_fn, NULL);
2911 bool init_wcache(void)
2913 if (wcache == NULL) {
2914 wcache = SMB_XMALLOC_P(struct winbind_cache);
2915 ZERO_STRUCTP(wcache);
2918 if (wcache->tdb != NULL)
2921 /* when working offline we must not clear the cache on restart */
2922 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2923 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2924 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2925 O_RDWR|O_CREAT, 0600);
2927 if (wcache->tdb == NULL) {
2928 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2935 /************************************************************************
2936 This is called by the parent to initialize the cache file.
2937 We don't need sophisticated locking here as we know we're the
2939 ************************************************************************/
2941 bool initialize_winbindd_cache(void)
2943 bool cache_bad = true;
2946 if (!init_wcache()) {
2947 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2951 /* Check version number. */
2952 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2953 vers == WINBINDD_CACHE_VERSION) {
2958 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2959 "and re-creating with version number %d\n",
2960 WINBINDD_CACHE_VERSION ));
2962 tdb_close(wcache->tdb);
2965 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2966 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2967 cache_path("winbindd_cache.tdb"),
2971 if (!init_wcache()) {
2972 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2973 "init_wcache failed.\n"));
2977 /* Write the version. */
2978 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2979 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2980 tdb_errorstr(wcache->tdb) ));
2985 tdb_close(wcache->tdb);
2990 void close_winbindd_cache(void)
2996 tdb_close(wcache->tdb);
3001 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
3002 char **domain_name, char **name,
3003 enum lsa_SidType *type)
3005 struct winbindd_domain *domain;
3008 domain = find_lookup_domain_from_sid(sid);
3009 if (domain == NULL) {
3012 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3014 return NT_STATUS_IS_OK(status);
3017 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
3018 const char *domain_name,
3021 enum lsa_SidType *type)
3023 struct winbindd_domain *domain;
3025 bool original_online_state;
3027 domain = find_lookup_domain_from_name(domain_name);
3028 if (domain == NULL) {
3032 /* If we are doing a cached logon, temporarily set the domain
3033 offline so the cache won't expire the entry */
3035 original_online_state = domain->online;
3036 domain->online = false;
3037 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3038 domain->online = original_online_state;
3040 return NT_STATUS_IS_OK(status);
3043 void cache_name2sid(struct winbindd_domain *domain,
3044 const char *domain_name, const char *name,
3045 enum lsa_SidType type, const DOM_SID *sid)
3047 refresh_sequence_number(domain, false);
3048 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3053 * The original idea that this cache only contains centries has
3054 * been blurred - now other stuff gets put in here. Ensure we
3055 * ignore these things on cleanup.
3058 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3059 TDB_DATA dbuf, void *state)
3061 struct cache_entry *centry;
3063 if (is_non_centry_key(kbuf)) {
3067 centry = wcache_fetch_raw((char *)kbuf.dptr);
3072 if (!NT_STATUS_IS_OK(centry->status)) {
3073 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3074 tdb_delete(the_tdb, kbuf);
3077 centry_free(centry);
3081 /* flush the cache */
3082 void wcache_flush_cache(void)
3087 tdb_close(wcache->tdb);
3090 if (!winbindd_use_cache()) {
3094 /* when working offline we must not clear the cache on restart */
3095 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3096 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3097 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
3098 O_RDWR|O_CREAT, 0600);
3101 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3105 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3107 DEBUG(10,("wcache_flush_cache success\n"));
3110 /* Count cached creds */
3112 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3115 int *cred_count = (int*)state;
3117 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3123 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3125 struct winbind_cache *cache = get_cache(domain);
3130 return NT_STATUS_INTERNAL_DB_ERROR;
3133 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3135 return NT_STATUS_OK;
3139 struct cred_list *prev, *next;
3144 static struct cred_list *wcache_cred_list;
3146 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3149 struct cred_list *cred;
3151 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3153 cred = SMB_MALLOC_P(struct cred_list);
3155 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3161 /* save a copy of the key */
3163 fstrcpy(cred->name, (const char *)kbuf.dptr);
3164 DLIST_ADD(wcache_cred_list, cred);
3170 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
3172 struct winbind_cache *cache = get_cache(domain);
3175 struct cred_list *cred, *oldest = NULL;
3178 return NT_STATUS_INTERNAL_DB_ERROR;
3181 /* we possibly already have an entry */
3182 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3184 fstring key_str, tmp;
3186 DEBUG(11,("we already have an entry, deleting that\n"));
3188 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3190 tdb_delete(cache->tdb, string_tdb_data(key_str));
3192 return NT_STATUS_OK;
3195 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3197 return NT_STATUS_OK;
3198 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3199 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3202 ZERO_STRUCTP(oldest);
3204 for (cred = wcache_cred_list; cred; cred = cred->next) {
3209 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3211 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3213 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3217 t = IVAL(data.dptr, 0);
3218 SAFE_FREE(data.dptr);
3221 oldest = SMB_MALLOC_P(struct cred_list);
3222 if (oldest == NULL) {
3223 status = NT_STATUS_NO_MEMORY;
3227 fstrcpy(oldest->name, cred->name);
3228 oldest->created = t;
3232 if (t < oldest->created) {
3233 fstrcpy(oldest->name, cred->name);
3234 oldest->created = t;
3238 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3239 status = NT_STATUS_OK;
3241 status = NT_STATUS_UNSUCCESSFUL;
3244 SAFE_FREE(wcache_cred_list);
3250 /* Change the global online/offline state. */
3251 bool set_global_winbindd_state_offline(void)
3255 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3257 /* Only go offline if someone has created
3258 the key "WINBINDD_OFFLINE" in the cache tdb. */
3260 if (wcache == NULL || wcache->tdb == NULL) {
3261 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3265 if (!lp_winbind_offline_logon()) {
3266 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3270 if (global_winbindd_offline_state) {
3271 /* Already offline. */
3275 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3277 if (!data.dptr || data.dsize != 4) {
3278 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3279 SAFE_FREE(data.dptr);
3282 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3283 global_winbindd_offline_state = true;
3284 SAFE_FREE(data.dptr);
3289 void set_global_winbindd_state_online(void)
3291 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3293 if (!lp_winbind_offline_logon()) {
3294 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3298 if (!global_winbindd_offline_state) {
3299 /* Already online. */
3302 global_winbindd_offline_state = false;
3308 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3309 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3312 bool get_global_winbindd_state_offline(void)
3314 return global_winbindd_offline_state;
3317 /***********************************************************************
3318 Validate functions for all possible cache tdb keys.
3319 ***********************************************************************/
3321 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3322 struct tdb_validation_status *state)
3324 struct cache_entry *centry;
3326 centry = SMB_XMALLOC_P(struct cache_entry);
3327 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3328 if (!centry->data) {
3332 centry->len = data.dsize;
3335 if (centry->len < 8) {
3336 /* huh? corrupt cache? */
3337 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3338 centry_free(centry);
3339 state->bad_entry = true;
3340 state->success = false;
3344 centry->status = NT_STATUS(centry_uint32(centry));
3345 centry->sequence_number = centry_uint32(centry);
3349 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3350 struct tdb_validation_status *state)
3352 if (dbuf.dsize != 8) {
3353 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3354 keystr, (unsigned int)dbuf.dsize ));
3355 state->bad_entry = true;
3361 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3362 struct tdb_validation_status *state)
3364 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3369 (void)centry_uint32(centry);
3370 if (NT_STATUS_IS_OK(centry->status)) {
3372 (void)centry_sid(centry, &sid);
3375 centry_free(centry);
3377 if (!(state->success)) {
3380 DEBUG(10,("validate_ns: %s ok\n", keystr));
3384 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3385 struct tdb_validation_status *state)
3387 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3392 if (NT_STATUS_IS_OK(centry->status)) {
3393 (void)centry_uint32(centry);
3394 (void)centry_string(centry, mem_ctx);
3395 (void)centry_string(centry, mem_ctx);
3398 centry_free(centry);
3400 if (!(state->success)) {
3403 DEBUG(10,("validate_sn: %s ok\n", keystr));
3407 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3408 struct tdb_validation_status *state)
3410 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3417 (void)centry_string(centry, mem_ctx);
3418 (void)centry_string(centry, mem_ctx);
3419 (void)centry_string(centry, mem_ctx);
3420 (void)centry_string(centry, mem_ctx);
3421 (void)centry_uint32(centry);
3422 (void)centry_sid(centry, &sid);
3423 (void)centry_sid(centry, &sid);
3425 centry_free(centry);
3427 if (!(state->success)) {
3430 DEBUG(10,("validate_u: %s ok\n", keystr));
3434 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3435 struct tdb_validation_status *state)
3437 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3443 (void)centry_nttime(centry);
3444 (void)centry_nttime(centry);
3445 (void)centry_uint16(centry);
3447 centry_free(centry);
3449 if (!(state->success)) {
3452 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3456 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3457 struct tdb_validation_status *state)
3459 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3465 (void)centry_uint16(centry);
3466 (void)centry_uint16(centry);
3467 (void)centry_uint32(centry);
3468 (void)centry_nttime(centry);
3469 (void)centry_nttime(centry);
3471 centry_free(centry);
3473 if (!(state->success)) {
3476 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3480 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3481 struct tdb_validation_status *state)
3483 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3489 (void)centry_time(centry);
3490 (void)centry_hash16(centry, mem_ctx);
3492 /* We only have 17 bytes more data in the salted cred case. */
3493 if (centry->len - centry->ofs == 17) {
3494 (void)centry_hash16(centry, mem_ctx);
3497 centry_free(centry);
3499 if (!(state->success)) {
3502 DEBUG(10,("validate_cred: %s ok\n", keystr));
3506 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3507 struct tdb_validation_status *state)
3509 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3510 int32 num_entries, i;
3516 num_entries = (int32)centry_uint32(centry);
3518 for (i=0; i< num_entries; i++) {
3520 (void)centry_string(centry, mem_ctx);
3521 (void)centry_string(centry, mem_ctx);
3522 (void)centry_string(centry, mem_ctx);
3523 (void)centry_string(centry, mem_ctx);
3524 (void)centry_sid(centry, &sid);
3525 (void)centry_sid(centry, &sid);
3528 centry_free(centry);
3530 if (!(state->success)) {
3533 DEBUG(10,("validate_ul: %s ok\n", keystr));
3537 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3538 struct tdb_validation_status *state)
3540 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3541 int32 num_entries, i;
3547 num_entries = centry_uint32(centry);
3549 for (i=0; i< num_entries; i++) {
3550 (void)centry_string(centry, mem_ctx);
3551 (void)centry_string(centry, mem_ctx);
3552 (void)centry_uint32(centry);
3555 centry_free(centry);
3557 if (!(state->success)) {
3560 DEBUG(10,("validate_gl: %s ok\n", keystr));
3564 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3565 struct tdb_validation_status *state)
3567 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3568 int32 num_groups, i;
3574 num_groups = centry_uint32(centry);
3576 for (i=0; i< num_groups; i++) {
3578 centry_sid(centry, &sid);
3581 centry_free(centry);
3583 if (!(state->success)) {
3586 DEBUG(10,("validate_ug: %s ok\n", keystr));
3590 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3591 struct tdb_validation_status *state)
3593 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3594 int32 num_aliases, i;
3600 num_aliases = centry_uint32(centry);
3602 for (i=0; i < num_aliases; i++) {
3603 (void)centry_uint32(centry);
3606 centry_free(centry);
3608 if (!(state->success)) {
3611 DEBUG(10,("validate_ua: %s ok\n", keystr));
3615 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3616 struct tdb_validation_status *state)
3618 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3625 num_names = centry_uint32(centry);
3627 for (i=0; i< num_names; i++) {
3629 centry_sid(centry, &sid);
3630 (void)centry_string(centry, mem_ctx);
3631 (void)centry_uint32(centry);
3634 centry_free(centry);
3636 if (!(state->success)) {
3639 DEBUG(10,("validate_gm: %s ok\n", keystr));
3643 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3644 struct tdb_validation_status *state)
3646 /* Can't say anything about this other than must be nonzero. */
3647 if (dbuf.dsize == 0) {
3648 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3650 state->bad_entry = true;
3651 state->success = false;
3655 DEBUG(10,("validate_dr: %s ok\n", keystr));
3659 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3660 struct tdb_validation_status *state)
3662 /* Can't say anything about this other than must be nonzero. */
3663 if (dbuf.dsize == 0) {
3664 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3666 state->bad_entry = true;
3667 state->success = false;
3671 DEBUG(10,("validate_de: %s ok\n", keystr));
3675 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3676 TDB_DATA dbuf, struct tdb_validation_status *state)
3678 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3684 (void)centry_string(centry, mem_ctx);
3685 (void)centry_string(centry, mem_ctx);
3686 (void)centry_string(centry, mem_ctx);
3687 (void)centry_uint32(centry);
3689 centry_free(centry);
3691 if (!(state->success)) {
3694 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3698 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3700 struct tdb_validation_status *state)
3702 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3708 (void)centry_string( centry, mem_ctx );
3710 centry_free(centry);
3712 if (!(state->success)) {
3715 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3719 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3721 struct tdb_validation_status *state)
3723 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3729 (void)centry_string( centry, mem_ctx );
3731 centry_free(centry);
3733 if (!(state->success)) {
3736 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3740 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3742 struct tdb_validation_status *state)
3744 if (dbuf.dsize == 0) {
3745 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3746 "key %s (len ==0) ?\n", keystr));
3747 state->bad_entry = true;
3748 state->success = false;
3752 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3753 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3757 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3758 struct tdb_validation_status *state)
3760 if (dbuf.dsize != 4) {
3761 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3762 keystr, (unsigned int)dbuf.dsize ));
3763 state->bad_entry = true;
3764 state->success = false;
3767 DEBUG(10,("validate_offline: %s ok\n", keystr));
3771 static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3772 struct tdb_validation_status *state)
3775 * Ignore validation for now. The proper way to do this is with a
3776 * checksum. Just pure parsing does not really catch much.
3781 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3782 struct tdb_validation_status *state)
3784 if (dbuf.dsize != 4) {
3785 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3786 "key %s (len %u != 4) ?\n",
3787 keystr, (unsigned int)dbuf.dsize));
3788 state->bad_entry = true;
3789 state->success = false;
3793 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3797 /***********************************************************************
3798 A list of all possible cache tdb keys with associated validation
3800 ***********************************************************************/
3802 struct key_val_struct {
3803 const char *keyname;
3804 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3806 {"SEQNUM/", validate_seqnum},
3807 {"NS/", validate_ns},
3808 {"SN/", validate_sn},
3810 {"LOC_POL/", validate_loc_pol},
3811 {"PWD_POL/", validate_pwd_pol},
3812 {"CRED/", validate_cred},
3813 {"UL/", validate_ul},
3814 {"GL/", validate_gl},
3815 {"UG/", validate_ug},
3816 {"UA", validate_ua},
3817 {"GM/", validate_gm},
3818 {"DR/", validate_dr},
3819 {"DE/", validate_de},
3820 {"NSS/PWINFO/", validate_pwinfo},
3821 {"TRUSTDOMCACHE/", validate_trustdomcache},
3822 {"NSS/NA/", validate_nss_na},
3823 {"NSS/AN/", validate_nss_an},
3824 {"WINBINDD_OFFLINE", validate_offline},
3825 {"NDR/", validate_ndr},
3826 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3830 /***********************************************************************
3831 Function to look at every entry in the tdb and validate it as far as
3833 ***********************************************************************/
3835 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3838 unsigned int max_key_len = 1024;
3839 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3841 /* Paranoia check. */
3842 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3843 max_key_len = 1024 * 1024;
3845 if (kbuf.dsize > max_key_len) {
3846 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3848 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3852 for (i = 0; key_val[i].keyname; i++) {
3853 size_t namelen = strlen(key_val[i].keyname);
3854 if (kbuf.dsize >= namelen && (
3855 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3856 TALLOC_CTX *mem_ctx;
3860 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3864 memcpy(keystr, kbuf.dptr, kbuf.dsize);
3865 keystr[kbuf.dsize] = '\0';
3867 mem_ctx = talloc_init("validate_ctx");
3873 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
3877 talloc_destroy(mem_ctx);
3882 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3883 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3884 DEBUG(0,("data :\n"));
3885 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3886 v_state->unknown_key = true;
3887 v_state->success = false;
3888 return 1; /* terminate. */
3891 static void validate_panic(const char *const why)
3893 DEBUG(0,("validating cache: would panic %s\n", why ));
3894 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3898 /***********************************************************************
3899 Try and validate every entry in the winbindd cache. If we fail here,
3900 delete the cache tdb and return non-zero.
3901 ***********************************************************************/
3903 int winbindd_validate_cache(void)
3906 const char *tdb_path = cache_path("winbindd_cache.tdb");
3907 TDB_CONTEXT *tdb = NULL;
3909 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3910 smb_panic_fn = validate_panic;
3913 tdb = tdb_open_log(tdb_path,
3914 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3915 ( lp_winbind_offline_logon()
3917 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
3921 DEBUG(0, ("winbindd_validate_cache: "
3922 "error opening/initializing tdb\n"));
3927 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3930 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3931 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3936 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3937 smb_panic_fn = smb_panic;
3941 /***********************************************************************
3942 Try and validate every entry in the winbindd cache.
3943 ***********************************************************************/
3945 int winbindd_validate_cache_nobackup(void)
3948 const char *tdb_path = cache_path("winbindd_cache.tdb");
3950 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3951 smb_panic_fn = validate_panic;
3954 if (wcache == NULL || wcache->tdb == NULL) {
3955 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3957 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3961 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3965 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3967 smb_panic_fn = smb_panic;
3971 bool winbindd_cache_validate_and_initialize(void)
3973 close_winbindd_cache();
3975 if (lp_winbind_offline_logon()) {
3976 if (winbindd_validate_cache() < 0) {
3977 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3978 "could be restored.\n"));
3982 return initialize_winbindd_cache();
3985 /*********************************************************************
3986 ********************************************************************/
3988 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3989 struct winbindd_tdc_domain **domains,
3990 size_t *num_domains )
3992 struct winbindd_tdc_domain *list = NULL;
3995 bool set_only = false;
3997 /* don't allow duplicates */
4002 for ( i=0; i< (*num_domains); i++ ) {
4003 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4004 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4015 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4018 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4019 struct winbindd_tdc_domain,
4024 ZERO_STRUCT( list[idx] );
4030 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4031 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4033 if ( !is_null_sid( &new_dom->sid ) ) {
4034 sid_copy( &list[idx].sid, &new_dom->sid );
4036 sid_copy(&list[idx].sid, &global_sid_NULL);
4039 if ( new_dom->domain_flags != 0x0 )
4040 list[idx].trust_flags = new_dom->domain_flags;
4042 if ( new_dom->domain_type != 0x0 )
4043 list[idx].trust_type = new_dom->domain_type;
4045 if ( new_dom->domain_trust_attribs != 0x0 )
4046 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4050 *num_domains = idx + 1;
4056 /*********************************************************************
4057 ********************************************************************/
4059 static TDB_DATA make_tdc_key( const char *domain_name )
4061 char *keystr = NULL;
4062 TDB_DATA key = { NULL, 0 };
4064 if ( !domain_name ) {
4065 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4069 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4072 key = string_term_tdb_data(keystr);
4077 /*********************************************************************
4078 ********************************************************************/
4080 static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4082 unsigned char **buf )
4084 unsigned char *buffer = NULL;
4089 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4097 /* Store the number of array items first */
4098 len += tdb_pack( buffer+len, buflen-len, "d",
4101 /* now pack each domain trust record */
4102 for ( i=0; i<num_domains; i++ ) {
4107 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4108 domains[i].domain_name,
4109 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4112 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4113 domains[i].domain_name,
4114 domains[i].dns_name,
4115 sid_to_fstring(tmp, &domains[i].sid),
4116 domains[i].trust_flags,
4117 domains[i].trust_attribs,
4118 domains[i].trust_type );
4121 if ( buflen < len ) {
4123 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4124 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4138 /*********************************************************************
4139 ********************************************************************/
4141 static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4142 struct winbindd_tdc_domain **domains )
4144 fstring domain_name, dns_name, sid_string;
4145 uint32 type, attribs, flags;
4149 struct winbindd_tdc_domain *list = NULL;
4151 /* get the number of domains */
4152 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4154 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4158 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4160 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4164 for ( i=0; i<num_domains; i++ ) {
4165 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4174 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4175 TALLOC_FREE( list );
4179 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4180 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4181 domain_name, dns_name, sid_string,
4182 flags, attribs, type));
4184 list[i].domain_name = talloc_strdup( list, domain_name );
4185 list[i].dns_name = talloc_strdup( list, dns_name );
4186 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4187 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4190 list[i].trust_flags = flags;
4191 list[i].trust_attribs = attribs;
4192 list[i].trust_type = type;
4200 /*********************************************************************
4201 ********************************************************************/
4203 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4205 TDB_DATA key = make_tdc_key( lp_workgroup() );
4206 TDB_DATA data = { NULL, 0 };
4212 /* See if we were asked to delete the cache entry */
4215 ret = tdb_delete( wcache->tdb, key );
4219 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4226 ret = tdb_store( wcache->tdb, key, data, 0 );
4229 SAFE_FREE( data.dptr );
4230 SAFE_FREE( key.dptr );
4232 return ( ret != -1 );
4235 /*********************************************************************
4236 ********************************************************************/
4238 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4240 TDB_DATA key = make_tdc_key( lp_workgroup() );
4241 TDB_DATA data = { NULL, 0 };
4249 data = tdb_fetch( wcache->tdb, key );
4251 SAFE_FREE( key.dptr );
4256 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4258 SAFE_FREE( data.dptr );
4266 /*********************************************************************
4267 ********************************************************************/
4269 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4271 struct winbindd_tdc_domain *dom_list = NULL;
4272 size_t num_domains = 0;
4275 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4276 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4277 domain->name, domain->alt_name,
4278 sid_string_dbg(&domain->sid),
4279 domain->domain_flags,
4280 domain->domain_trust_attribs,
4281 domain->domain_type));
4283 if ( !init_wcache() ) {
4287 /* fetch the list */
4289 wcache_tdc_fetch_list( &dom_list, &num_domains );
4291 /* add the new domain */
4293 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4297 /* pack the domain */
4299 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4307 TALLOC_FREE( dom_list );
4312 /*********************************************************************
4313 ********************************************************************/
4315 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4317 struct winbindd_tdc_domain *dom_list = NULL;
4318 size_t num_domains = 0;
4320 struct winbindd_tdc_domain *d = NULL;
4322 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4324 if ( !init_wcache() ) {
4328 /* fetch the list */
4330 wcache_tdc_fetch_list( &dom_list, &num_domains );
4332 for ( i=0; i<num_domains; i++ ) {
4333 if ( strequal(name, dom_list[i].domain_name) ||
4334 strequal(name, dom_list[i].dns_name) )
4336 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4339 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4343 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4344 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4345 sid_copy( &d->sid, &dom_list[i].sid );
4346 d->trust_flags = dom_list[i].trust_flags;
4347 d->trust_type = dom_list[i].trust_type;
4348 d->trust_attribs = dom_list[i].trust_attribs;
4354 TALLOC_FREE( dom_list );
4360 /*********************************************************************
4361 ********************************************************************/
4363 void wcache_tdc_clear( void )
4365 if ( !init_wcache() )
4368 wcache_tdc_store_list( NULL, 0 );
4374 /*********************************************************************
4375 ********************************************************************/
4377 static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4379 const DOM_SID *user_sid,
4380 const char *homedir,
4385 struct cache_entry *centry;
4388 if ( (centry = centry_start(domain, status)) == NULL )
4391 centry_put_string( centry, homedir );
4392 centry_put_string( centry, shell );
4393 centry_put_string( centry, gecos );
4394 centry_put_uint32( centry, gid );
4396 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4398 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4400 centry_free(centry);
4403 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4404 const DOM_SID *user_sid,
4406 ADS_STRUCT *ads, LDAPMessage *msg,
4407 const char **homedir, const char **shell,
4408 const char **gecos, gid_t *p_gid)
4410 struct winbind_cache *cache = get_cache(domain);
4411 struct cache_entry *centry = NULL;
4418 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4419 sid_to_fstring(tmp, user_sid));
4424 *homedir = centry_string( centry, ctx );
4425 *shell = centry_string( centry, ctx );
4426 *gecos = centry_string( centry, ctx );
4427 *p_gid = centry_uint32( centry );
4429 centry_free(centry);
4431 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4432 sid_string_dbg(user_sid)));
4434 return NT_STATUS_OK;
4438 nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
4439 homedir, shell, gecos, p_gid );
4441 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4443 if ( NT_STATUS_IS_OK(nt_status) ) {
4444 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4445 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4446 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4447 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4449 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4450 *homedir, *shell, *gecos, *p_gid );
4453 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4454 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4456 set_domain_offline( domain );
4463 /* the cache backend methods are exposed via this structure */
4464 struct winbindd_methods cache_methods = {
4482 static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4483 uint32_t opnum, const DATA_BLOB *req,
4489 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4493 keylen = talloc_get_size(key) - 1;
4495 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4499 memcpy(key + keylen, req->data, req->length);
4501 pkey->dptr = (uint8_t *)key;
4502 pkey->dsize = talloc_get_size(key);
4506 static bool wcache_opnum_cacheable(uint32_t opnum)
4509 case NDR_WBINT_PING:
4510 case NDR_WBINT_QUERYSEQUENCENUMBER:
4511 case NDR_WBINT_ALLOCATEUID:
4512 case NDR_WBINT_ALLOCATEGID:
4513 case NDR_WBINT_CHECKMACHINEACCOUNT:
4514 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4515 case NDR_WBINT_PINGDC:
4521 bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4522 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4527 if (!wcache_opnum_cacheable(opnum)) {
4531 if (wcache->tdb == NULL) {
4535 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4538 data = tdb_fetch(wcache->tdb, key);
4539 TALLOC_FREE(key.dptr);
4541 if (data.dptr == NULL) {
4544 if (data.dsize < 4) {
4548 if (!is_domain_offline(domain)) {
4549 uint32_t entry_seqnum, dom_seqnum, last_check;
4551 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4555 entry_seqnum = IVAL(data.dptr, 0);
4556 if (entry_seqnum != dom_seqnum) {
4557 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4558 (int)entry_seqnum));
4563 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 4,
4565 if (resp->data == NULL) {
4566 DEBUG(10, ("talloc failed\n"));
4569 resp->length = data.dsize - 4;
4573 SAFE_FREE(data.dptr);
4577 void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4578 const DATA_BLOB *req, const DATA_BLOB *resp)
4581 uint32_t dom_seqnum, last_check;
4583 if (!wcache_opnum_cacheable(opnum)) {
4587 if (wcache->tdb == NULL) {
4591 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4592 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4597 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4601 data.dsize = resp->length + 4;
4602 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4603 if (data.dptr == NULL) {
4607 SIVAL(data.dptr, 0, dom_seqnum);
4608 memcpy(data.dptr+4, resp->data, resp->length);
4610 tdb_store(wcache->tdb, key, data, 0);
4613 TALLOC_FREE(key.dptr);