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/>.
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods
;
36 extern bool opt_nocache
;
38 extern struct winbindd_methods ads_methods
;
40 extern struct winbindd_methods builtin_passdb_methods
;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys
[] = {
53 WINBINDD_CACHE_VERSION_KEYSTR
,
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf
)
65 if (kbuf
.dptr
== NULL
|| kbuf
.dsize
== 0) {
68 for (i
= 0; non_centry_keys
[i
] != NULL
; i
++) {
69 size_t namelen
= strlen(non_centry_keys
[i
]);
70 if (kbuf
.dsize
< namelen
) {
73 if (strncmp(non_centry_keys
[i
], (const char *)kbuf
.dptr
, namelen
) == 0) {
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state
;
86 struct winbind_cache
{
92 uint32 sequence_number
;
97 void (*smb_panic_fn
)(const char *const why
) = smb_panic
;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache
*wcache
;
103 void winbindd_check_cache_size(time_t t
)
105 static time_t last_check_time
;
108 if (last_check_time
== (time_t)0)
111 if (t
- last_check_time
< 60 && t
- last_check_time
> 0)
114 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
119 if (fstat(tdb_fd(wcache
->tdb
), &st
) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno
) ));
124 if (st
.st_size
> WINBINDD_MAX_CACHE_SIZE
) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st
.st_size
,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE
));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache
*get_cache(struct winbindd_domain
*domain
)
135 struct winbind_cache
*ret
= wcache
;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain
->internal
) {
140 domain
->backend
= &builtin_passdb_methods
;
141 domain
->initialized
= True
;
143 if ( !domain
->initialized
) {
144 init_dc_connection( domain
);
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
165 if (!domain
->backend
) {
167 struct winbindd_domain
*our_domain
= domain
;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain
->primary
)
173 our_domain
= find_our_domain();
175 if ((our_domain
->active_directory
|| IS_DC
)
176 && domain
->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain
->name
));
179 domain
->backend
= &ads_methods
;
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain
->name
));
183 domain
->backend
= &reconnect_methods
;
186 #endif /* HAVE_ADS */
192 ret
= SMB_XMALLOC_P(struct winbind_cache
);
196 wcache_flush_cache();
202 free a centry structure
204 static void centry_free(struct cache_entry
*centry
)
208 SAFE_FREE(centry
->data
);
212 static bool centry_check_bytes(struct cache_entry
*centry
, size_t nbytes
)
214 if (centry
->len
- centry
->ofs
< nbytes
) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes
,
217 centry
->len
- centry
->ofs
));
224 pull a uint32 from a cache entry
226 static uint32
centry_uint32(struct cache_entry
*centry
)
230 if (!centry_check_bytes(centry
, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret
= IVAL(centry
->data
, centry
->ofs
);
239 pull a uint16 from a cache entry
241 static uint16
centry_uint16(struct cache_entry
*centry
)
244 if (!centry_check_bytes(centry
, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret
= CVAL(centry
->data
, centry
->ofs
);
253 pull a uint8 from a cache entry
255 static uint8
centry_uint8(struct cache_entry
*centry
)
258 if (!centry_check_bytes(centry
, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret
= CVAL(centry
->data
, centry
->ofs
);
267 pull a NTTIME from a cache entry
269 static NTTIME
centry_nttime(struct cache_entry
*centry
)
272 if (!centry_check_bytes(centry
, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret
= IVAL(centry
->data
, centry
->ofs
);
277 ret
+= (uint64_t)IVAL(centry
->data
, centry
->ofs
) << 32;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry
*centry
)
287 return (time_t)centry_nttime(centry
);
290 /* pull a string from a cache entry, using the supplied
293 static char *centry_string(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
298 len
= centry_uint8(centry
);
301 /* a deliberate NULL string */
305 if (!centry_check_bytes(centry
, (size_t)len
)) {
306 smb_panic_fn("centry_string");
309 ret
= TALLOC_ARRAY(mem_ctx
, char, len
+1);
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret
,centry
->data
+ centry
->ofs
, len
);
319 /* pull a hash16 from a cache entry, using the supplied
322 static char *centry_hash16(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
327 len
= centry_uint8(centry
);
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 if (!centry_check_bytes(centry
, 16)) {
339 ret
= TALLOC_ARRAY(mem_ctx
, char, 16);
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret
,centry
->data
+ centry
->ofs
, 16);
348 /* pull a sid from a cache entry, using the supplied
351 static bool centry_sid(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
, DOM_SID
*sid
)
354 sid_string
= centry_string(centry
, mem_ctx
);
355 if ((sid_string
== NULL
) || (!string_to_sid(sid
, sid_string
))) {
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS
centry_ntstatus(struct cache_entry
*centry
)
369 status
= NT_STATUS(centry_uint32(centry
));
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain
*domain
)
382 ret
= (domain
->sequence_number
== DOM_SEQUENCE_NONE
);
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
390 static NTSTATUS
fetch_cache_seqnum( struct winbindd_domain
*domain
, time_t now
)
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL
;
401 fstr_sprintf( key
, "SEQNUM/%s", domain
->name
);
403 data
= tdb_fetch_bystring( wcache
->tdb
, key
);
404 if ( !data
.dptr
|| data
.dsize
!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key
));
406 return NT_STATUS_UNSUCCESSFUL
;
409 domain
->sequence_number
= IVAL(data
.dptr
, 0);
410 domain
->last_seq_check
= IVAL(data
.dptr
, 4);
412 SAFE_FREE(data
.dptr
);
414 /* have we expired? */
416 time_diff
= now
- domain
->last_seq_check
;
417 if ( time_diff
> lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain
->name
, domain
->sequence_number
,
420 (uint32
)domain
->last_seq_check
));
421 return NT_STATUS_UNSUCCESSFUL
;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain
->name
, domain
->sequence_number
,
426 (uint32
)domain
->last_seq_check
));
431 static NTSTATUS
store_cache_seqnum( struct winbindd_domain
*domain
)
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL
;
442 fstr_sprintf( key_str
, "SEQNUM/%s", domain
->name
);
444 SIVAL(buf
, 0, domain
->sequence_number
);
445 SIVAL(buf
, 4, domain
->last_seq_check
);
449 if ( tdb_store_bystring( wcache
->tdb
, key_str
, data
, TDB_REPLACE
) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str
));
451 return NT_STATUS_UNSUCCESSFUL
;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain
->name
, domain
->sequence_number
,
456 (uint32
)domain
->last_seq_check
));
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain
*domain
, bool force
)
470 time_t t
= time(NULL
);
471 unsigned cache_time
= lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain
) ) {
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain
->sequence_number
== DOM_SEQUENCE_NONE
) {
486 time_diff
= t
- domain
->last_seq_check
;
488 /* see if we have to refetch the domain sequence number */
489 if (!force
&& (time_diff
< cache_time
)) {
490 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain
->name
));
494 /* try to get the sequence number from the tdb cache first */
495 /* this will update the timestamp as well */
497 status
= fetch_cache_seqnum( domain
, t
);
498 if ( NT_STATUS_IS_OK(status
) )
501 /* important! make sure that we know if this is a native
502 mode domain or not. And that we can contact it. */
504 if ( winbindd_can_contact_domain( domain
) ) {
505 status
= domain
->backend
->sequence_number(domain
,
506 &domain
->sequence_number
);
508 /* just use the current time */
509 status
= NT_STATUS_OK
;
510 domain
->sequence_number
= time(NULL
);
514 /* the above call could have set our domain->backend to NULL when
515 * coming from offline to online mode, make sure to reinitialize the
516 * backend - Guenther */
519 if (!NT_STATUS_IS_OK(status
)) {
520 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status
)));
521 domain
->sequence_number
= DOM_SEQUENCE_NONE
;
524 domain
->last_status
= status
;
525 domain
->last_seq_check
= time(NULL
);
527 /* save the new sequence number in the cache */
528 store_cache_seqnum( domain
);
531 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
532 domain
->name
, domain
->sequence_number
));
538 decide if a cache entry has expired
540 static bool centry_expired(struct winbindd_domain
*domain
, const char *keystr
, struct cache_entry
*centry
)
542 /* If we've been told to be offline - stay in that state... */
543 if (lp_winbind_offline_logon() && global_winbindd_offline_state
) {
544 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
545 keystr
, domain
->name
));
549 /* when the domain is offline return the cached entry.
550 * This deals with transient offline states... */
552 if (!domain
->online
) {
553 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
554 keystr
, domain
->name
));
558 /* if the server is OK and our cache entry came from when it was down then
559 the entry is invalid */
560 if ((domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
561 (centry
->sequence_number
== DOM_SEQUENCE_NONE
)) {
562 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
563 keystr
, domain
->name
));
567 /* if the server is down or the cache entry is not older than the
568 current sequence number then it is OK */
569 if (wcache_server_down(domain
) ||
570 centry
->sequence_number
== domain
->sequence_number
) {
571 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
572 keystr
, domain
->name
));
576 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
577 keystr
, domain
->name
));
583 static struct cache_entry
*wcache_fetch_raw(char *kstr
)
586 struct cache_entry
*centry
;
589 key
= string_tdb_data(kstr
);
590 data
= tdb_fetch(wcache
->tdb
, key
);
596 centry
= SMB_XMALLOC_P(struct cache_entry
);
597 centry
->data
= (unsigned char *)data
.dptr
;
598 centry
->len
= data
.dsize
;
601 if (centry
->len
< 8) {
602 /* huh? corrupt cache? */
603 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr
));
608 centry
->status
= centry_ntstatus(centry
);
609 centry
->sequence_number
= centry_uint32(centry
);
615 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
616 number and return status
618 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
619 struct winbindd_domain
*domain
,
620 const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
621 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
622 struct winbindd_domain
*domain
,
623 const char *format
, ...)
627 struct cache_entry
*centry
;
633 refresh_sequence_number(domain
, false);
635 va_start(ap
, format
);
636 smb_xvasprintf(&kstr
, format
, ap
);
639 centry
= wcache_fetch_raw(kstr
);
640 if (centry
== NULL
) {
645 if (centry_expired(domain
, kstr
, centry
)) {
647 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
648 kstr
, domain
->name
));
655 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
656 kstr
, domain
->name
));
662 static void wcache_delete(const char *format
, ...) PRINTF_ATTRIBUTE(1,2);
663 static void wcache_delete(const char *format
, ...)
669 va_start(ap
, format
);
670 smb_xvasprintf(&kstr
, format
, ap
);
673 key
= string_tdb_data(kstr
);
675 tdb_delete(wcache
->tdb
, key
);
680 make sure we have at least len bytes available in a centry
682 static void centry_expand(struct cache_entry
*centry
, uint32 len
)
684 if (centry
->len
- centry
->ofs
>= len
)
687 centry
->data
= SMB_REALLOC_ARRAY(centry
->data
, unsigned char,
690 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry
->len
));
691 smb_panic_fn("out of memory in centry_expand");
696 push a uint32 into a centry
698 static void centry_put_uint32(struct cache_entry
*centry
, uint32 v
)
700 centry_expand(centry
, 4);
701 SIVAL(centry
->data
, centry
->ofs
, v
);
706 push a uint16 into a centry
708 static void centry_put_uint16(struct cache_entry
*centry
, uint16 v
)
710 centry_expand(centry
, 2);
711 SIVAL(centry
->data
, centry
->ofs
, v
);
716 push a uint8 into a centry
718 static void centry_put_uint8(struct cache_entry
*centry
, uint8 v
)
720 centry_expand(centry
, 1);
721 SCVAL(centry
->data
, centry
->ofs
, v
);
726 push a string into a centry
728 static void centry_put_string(struct cache_entry
*centry
, const char *s
)
733 /* null strings are marked as len 0xFFFF */
734 centry_put_uint8(centry
, 0xFF);
739 /* can't handle more than 254 char strings. Truncating is probably best */
741 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len
));
744 centry_put_uint8(centry
, len
);
745 centry_expand(centry
, len
);
746 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
751 push a 16 byte hash into a centry - treat as 16 byte string.
753 static void centry_put_hash16(struct cache_entry
*centry
, const uint8 val
[16])
755 centry_put_uint8(centry
, 16);
756 centry_expand(centry
, 16);
757 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
761 static void centry_put_sid(struct cache_entry
*centry
, const DOM_SID
*sid
)
764 centry_put_string(centry
, sid_to_fstring(sid_string
, sid
));
769 put NTSTATUS into a centry
771 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
773 uint32 status_value
= NT_STATUS_V(status
);
774 centry_put_uint32(centry
, status_value
);
779 push a NTTIME into a centry
781 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
783 centry_expand(centry
, 8);
784 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
786 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
791 push a time_t into a centry - use a 64 bit size.
792 NTTIME here is being used as a convenient 64-bit size.
794 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
796 NTTIME nt
= (NTTIME
)t
;
797 centry_put_nttime(centry
, nt
);
801 start a centry for output. When finished, call centry_end()
803 struct cache_entry
*centry_start(struct winbindd_domain
*domain
, NTSTATUS status
)
805 struct cache_entry
*centry
;
810 centry
= SMB_XMALLOC_P(struct cache_entry
);
812 centry
->len
= 8192; /* reasonable default */
813 centry
->data
= SMB_XMALLOC_ARRAY(uint8
, centry
->len
);
815 centry
->sequence_number
= domain
->sequence_number
;
816 centry_put_ntstatus(centry
, status
);
817 centry_put_uint32(centry
, centry
->sequence_number
);
822 finish a centry and write it to the tdb
824 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
825 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
835 va_start(ap
, format
);
836 smb_xvasprintf(&kstr
, format
, ap
);
839 key
= string_tdb_data(kstr
);
840 data
.dptr
= centry
->data
;
841 data
.dsize
= centry
->ofs
;
843 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
847 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
848 NTSTATUS status
, const char *domain_name
,
849 const char *name
, const DOM_SID
*sid
,
850 enum lsa_SidType type
)
852 struct cache_entry
*centry
;
855 centry
= centry_start(domain
, status
);
858 centry_put_uint32(centry
, type
);
859 centry_put_sid(centry
, sid
);
860 fstrcpy(uname
, name
);
862 centry_end(centry
, "NS/%s/%s", domain_name
, uname
);
863 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name
,
864 uname
, sid_string_dbg(sid
), nt_errstr(status
)));
868 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
869 const DOM_SID
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
871 struct cache_entry
*centry
;
874 centry
= centry_start(domain
, status
);
878 if (NT_STATUS_IS_OK(status
)) {
879 centry_put_uint32(centry
, type
);
880 centry_put_string(centry
, domain_name
);
881 centry_put_string(centry
, name
);
884 centry_end(centry
, "SN/%s", sid_to_fstring(sid_string
, sid
));
885 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string
,
886 name
, nt_errstr(status
)));
891 static void wcache_save_user(struct winbindd_domain
*domain
, NTSTATUS status
, WINBIND_USERINFO
*info
)
893 struct cache_entry
*centry
;
896 if (is_null_sid(&info
->user_sid
)) {
900 centry
= centry_start(domain
, status
);
903 centry_put_string(centry
, info
->acct_name
);
904 centry_put_string(centry
, info
->full_name
);
905 centry_put_string(centry
, info
->homedir
);
906 centry_put_string(centry
, info
->shell
);
907 centry_put_uint32(centry
, info
->primary_gid
);
908 centry_put_sid(centry
, &info
->user_sid
);
909 centry_put_sid(centry
, &info
->group_sid
);
910 centry_end(centry
, "U/%s", sid_to_fstring(sid_string
,
912 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string
, info
->acct_name
));
916 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
918 struct samr_DomInfo12
*lockout_policy
)
920 struct cache_entry
*centry
;
922 centry
= centry_start(domain
, status
);
926 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
927 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
928 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
930 centry_end(centry
, "LOC_POL/%s", domain
->name
);
932 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain
->name
));
939 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
941 struct samr_DomInfo1
*policy
)
943 struct cache_entry
*centry
;
945 centry
= centry_start(domain
, status
);
949 centry_put_uint16(centry
, policy
->min_password_length
);
950 centry_put_uint16(centry
, policy
->password_history_length
);
951 centry_put_uint32(centry
, policy
->password_properties
);
952 centry_put_nttime(centry
, policy
->max_password_age
);
953 centry_put_nttime(centry
, policy
->min_password_age
);
955 centry_end(centry
, "PWD_POL/%s", domain
->name
);
957 DEBUG(10,("wcache_save_password_policy: %s\n", domain
->name
));
962 /***************************************************************************
963 ***************************************************************************/
965 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
967 const char *name
, const char *alias
)
969 struct cache_entry
*centry
;
972 if ( (centry
= centry_start(domain
, status
)) == NULL
)
975 centry_put_string( centry
, alias
);
977 fstrcpy(uname
, name
);
979 centry_end(centry
, "NSS/NA/%s", uname
);
981 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name
, alias
));
986 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
988 const char *alias
, const char *name
)
990 struct cache_entry
*centry
;
993 if ( (centry
= centry_start(domain
, status
)) == NULL
)
996 centry_put_string( centry
, name
);
998 fstrcpy(uname
, alias
);
1000 centry_end(centry
, "NSS/AN/%s", uname
);
1002 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias
, name
));
1004 centry_free(centry
);
1007 /***************************************************************************
1008 ***************************************************************************/
1010 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1011 struct winbindd_domain
*domain
,
1012 const char *name
, char **alias
)
1014 struct winbind_cache
*cache
= get_cache(domain
);
1015 struct cache_entry
*centry
= NULL
;
1019 if ( domain
->internal
)
1020 return NT_STATUS_NOT_SUPPORTED
;
1025 if ( (upper_name
= SMB_STRDUP(name
)) == NULL
)
1026 return NT_STATUS_NO_MEMORY
;
1027 strupper_m(upper_name
);
1029 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1031 SAFE_FREE( upper_name
);
1036 status
= centry
->status
;
1038 if (!NT_STATUS_IS_OK(status
)) {
1039 centry_free(centry
);
1043 *alias
= centry_string( centry
, mem_ctx
);
1045 centry_free(centry
);
1047 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1048 name
, *alias
? *alias
: "(none)"));
1050 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1054 /* If its not in cache and we are offline, then fail */
1056 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1057 DEBUG(8,("resolve_username_to_alias: rejecting query "
1058 "in offline mode\n"));
1059 return NT_STATUS_NOT_FOUND
;
1062 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1064 if ( NT_STATUS_IS_OK( status
) ) {
1065 wcache_save_username_alias(domain
, status
, name
, *alias
);
1068 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1069 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1072 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1073 nt_errstr(status
)));
1075 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1076 set_domain_offline( domain
);
1082 /***************************************************************************
1083 ***************************************************************************/
1085 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1086 struct winbindd_domain
*domain
,
1087 const char *alias
, char **name
)
1089 struct winbind_cache
*cache
= get_cache(domain
);
1090 struct cache_entry
*centry
= NULL
;
1094 if ( domain
->internal
)
1095 return NT_STATUS_NOT_SUPPORTED
;
1100 if ( (upper_name
= SMB_STRDUP(alias
)) == NULL
)
1101 return NT_STATUS_NO_MEMORY
;
1102 strupper_m(upper_name
);
1104 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1106 SAFE_FREE( upper_name
);
1111 status
= centry
->status
;
1113 if (!NT_STATUS_IS_OK(status
)) {
1114 centry_free(centry
);
1118 *name
= centry_string( centry
, mem_ctx
);
1120 centry_free(centry
);
1122 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1123 alias
, *name
? *name
: "(none)"));
1125 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1129 /* If its not in cache and we are offline, then fail */
1131 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1132 DEBUG(8,("resolve_alias_to_username: rejecting query "
1133 "in offline mode\n"));
1134 return NT_STATUS_NOT_FOUND
;
1137 /* an alias cannot contain a domain prefix or '@' */
1139 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1140 DEBUG(10,("resolve_alias_to_username: skipping fully "
1141 "qualified name %s\n", alias
));
1142 return NT_STATUS_OBJECT_NAME_INVALID
;
1145 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1147 if ( NT_STATUS_IS_OK( status
) ) {
1148 wcache_save_alias_username( domain
, status
, alias
, *name
);
1151 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1152 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1155 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1156 nt_errstr(status
)));
1158 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1159 set_domain_offline( domain
);
1165 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const DOM_SID
*sid
)
1167 struct winbind_cache
*cache
= get_cache(domain
);
1169 fstring key_str
, tmp
;
1173 return NT_STATUS_INTERNAL_DB_ERROR
;
1176 if (is_null_sid(sid
)) {
1177 return NT_STATUS_INVALID_SID
;
1180 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1181 return NT_STATUS_INVALID_SID
;
1184 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
1186 data
= tdb_fetch(cache
->tdb
, string_tdb_data(key_str
));
1188 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1191 SAFE_FREE(data
.dptr
);
1192 return NT_STATUS_OK
;
1195 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1196 as new salted ones. */
1198 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1199 TALLOC_CTX
*mem_ctx
,
1201 const uint8
**cached_nt_pass
,
1202 const uint8
**cached_salt
)
1204 struct winbind_cache
*cache
= get_cache(domain
);
1205 struct cache_entry
*centry
= NULL
;
1212 return NT_STATUS_INTERNAL_DB_ERROR
;
1215 if (is_null_sid(sid
)) {
1216 return NT_STATUS_INVALID_SID
;
1219 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1220 return NT_STATUS_INVALID_SID
;
1223 /* Try and get a salted cred first. If we can't
1224 fall back to an unsalted cred. */
1226 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1227 sid_to_fstring(tmp
, sid
));
1229 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1230 sid_string_dbg(sid
)));
1231 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1234 t
= centry_time(centry
);
1236 /* In the salted case this isn't actually the nt_hash itself,
1237 but the MD5 of the salt + nt_hash. Let the caller
1238 sort this out. It can tell as we only return the cached_salt
1239 if we are returning a salted cred. */
1241 *cached_nt_pass
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1242 if (*cached_nt_pass
== NULL
) {
1245 sid_to_fstring(sidstr
, sid
);
1247 /* Bad (old) cred cache. Delete and pretend we
1249 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1251 wcache_delete("CRED/%s", sidstr
);
1252 centry_free(centry
);
1253 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1256 /* We only have 17 bytes more data in the salted cred case. */
1257 if (centry
->len
- centry
->ofs
== 17) {
1258 *cached_salt
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1260 *cached_salt
= NULL
;
1263 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1265 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1268 status
= centry
->status
;
1270 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1271 sid_string_dbg(sid
), nt_errstr(status
) ));
1273 centry_free(centry
);
1277 /* Store creds for a SID - only writes out new salted ones. */
1279 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1280 TALLOC_CTX
*mem_ctx
,
1282 const uint8 nt_pass
[NT_HASH_LEN
])
1284 struct cache_entry
*centry
;
1287 uint8 cred_salt
[NT_HASH_LEN
];
1288 uint8 salted_hash
[NT_HASH_LEN
];
1290 if (is_null_sid(sid
)) {
1291 return NT_STATUS_INVALID_SID
;
1294 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1295 return NT_STATUS_INVALID_SID
;
1298 centry
= centry_start(domain
, NT_STATUS_OK
);
1300 return NT_STATUS_INTERNAL_DB_ERROR
;
1303 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1305 centry_put_time(centry
, time(NULL
));
1307 /* Create a salt and then salt the hash. */
1308 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1309 E_md5hash(cred_salt
, nt_pass
, salted_hash
);
1311 centry_put_hash16(centry
, salted_hash
);
1312 centry_put_hash16(centry
, cred_salt
);
1313 centry_end(centry
, "CRED/%s", sid_to_fstring(sid_string
, sid
));
1315 DEBUG(10,("wcache_save_creds: %s\n", sid_string
));
1317 centry_free(centry
);
1319 return NT_STATUS_OK
;
1323 /* Query display info. This is the basic user list fn */
1324 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
1325 TALLOC_CTX
*mem_ctx
,
1326 uint32
*num_entries
,
1327 WINBIND_USERINFO
**info
)
1329 struct winbind_cache
*cache
= get_cache(domain
);
1330 struct cache_entry
*centry
= NULL
;
1332 unsigned int i
, retry
;
1337 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1341 *num_entries
= centry_uint32(centry
);
1343 if (*num_entries
== 0)
1346 (*info
) = TALLOC_ARRAY(mem_ctx
, WINBIND_USERINFO
, *num_entries
);
1348 smb_panic_fn("query_user_list out of memory");
1350 for (i
=0; i
<(*num_entries
); i
++) {
1351 (*info
)[i
].acct_name
= centry_string(centry
, mem_ctx
);
1352 (*info
)[i
].full_name
= centry_string(centry
, mem_ctx
);
1353 (*info
)[i
].homedir
= centry_string(centry
, mem_ctx
);
1354 (*info
)[i
].shell
= centry_string(centry
, mem_ctx
);
1355 centry_sid(centry
, mem_ctx
, &(*info
)[i
].user_sid
);
1356 centry_sid(centry
, mem_ctx
, &(*info
)[i
].group_sid
);
1360 status
= centry
->status
;
1362 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1363 domain
->name
, nt_errstr(status
) ));
1365 centry_free(centry
);
1372 /* Return status value returned by seq number check */
1374 if (!NT_STATUS_IS_OK(domain
->last_status
))
1375 return domain
->last_status
;
1377 /* Put the query_user_list() in a retry loop. There appears to be
1378 * some bug either with Windows 2000 or Samba's handling of large
1379 * rpc replies. This manifests itself as sudden disconnection
1380 * at a random point in the enumeration of a large (60k) user list.
1381 * The retry loop simply tries the operation again. )-: It's not
1382 * pretty but an acceptable workaround until we work out what the
1383 * real problem is. */
1388 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1391 status
= domain
->backend
->query_user_list(domain
, mem_ctx
, num_entries
, info
);
1392 if (!NT_STATUS_IS_OK(status
)) {
1393 DEBUG(3, ("query_user_list: returned 0x%08x, "
1394 "retrying\n", NT_STATUS_V(status
)));
1396 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1397 DEBUG(3, ("query_user_list: flushing "
1398 "connection cache\n"));
1399 invalidate_cm_connection(&domain
->conn
);
1402 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1406 refresh_sequence_number(domain
, false);
1407 centry
= centry_start(domain
, status
);
1410 centry_put_uint32(centry
, *num_entries
);
1411 for (i
=0; i
<(*num_entries
); i
++) {
1412 centry_put_string(centry
, (*info
)[i
].acct_name
);
1413 centry_put_string(centry
, (*info
)[i
].full_name
);
1414 centry_put_string(centry
, (*info
)[i
].homedir
);
1415 centry_put_string(centry
, (*info
)[i
].shell
);
1416 centry_put_sid(centry
, &(*info
)[i
].user_sid
);
1417 centry_put_sid(centry
, &(*info
)[i
].group_sid
);
1418 if (domain
->backend
&& domain
->backend
->consistent
) {
1419 /* when the backend is consistent we can pre-prime some mappings */
1420 wcache_save_name_to_sid(domain
, NT_STATUS_OK
,
1422 (*info
)[i
].acct_name
,
1423 &(*info
)[i
].user_sid
,
1425 wcache_save_sid_to_name(domain
, NT_STATUS_OK
,
1426 &(*info
)[i
].user_sid
,
1428 (*info
)[i
].acct_name
,
1430 wcache_save_user(domain
, NT_STATUS_OK
, &(*info
)[i
]);
1433 centry_end(centry
, "UL/%s", domain
->name
);
1434 centry_free(centry
);
1440 /* list all domain groups */
1441 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
1442 TALLOC_CTX
*mem_ctx
,
1443 uint32
*num_entries
,
1444 struct acct_info
**info
)
1446 struct winbind_cache
*cache
= get_cache(domain
);
1447 struct cache_entry
*centry
= NULL
;
1454 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1458 *num_entries
= centry_uint32(centry
);
1460 if (*num_entries
== 0)
1463 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1465 smb_panic_fn("enum_dom_groups out of memory");
1467 for (i
=0; i
<(*num_entries
); i
++) {
1468 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1469 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1470 (*info
)[i
].rid
= centry_uint32(centry
);
1474 status
= centry
->status
;
1476 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1477 domain
->name
, nt_errstr(status
) ));
1479 centry_free(centry
);
1486 /* Return status value returned by seq number check */
1488 if (!NT_STATUS_IS_OK(domain
->last_status
))
1489 return domain
->last_status
;
1491 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1494 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1497 refresh_sequence_number(domain
, false);
1498 centry
= centry_start(domain
, status
);
1501 centry_put_uint32(centry
, *num_entries
);
1502 for (i
=0; i
<(*num_entries
); i
++) {
1503 centry_put_string(centry
, (*info
)[i
].acct_name
);
1504 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1505 centry_put_uint32(centry
, (*info
)[i
].rid
);
1507 centry_end(centry
, "GL/%s/domain", domain
->name
);
1508 centry_free(centry
);
1514 /* list all domain groups */
1515 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
1516 TALLOC_CTX
*mem_ctx
,
1517 uint32
*num_entries
,
1518 struct acct_info
**info
)
1520 struct winbind_cache
*cache
= get_cache(domain
);
1521 struct cache_entry
*centry
= NULL
;
1528 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1532 *num_entries
= centry_uint32(centry
);
1534 if (*num_entries
== 0)
1537 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1539 smb_panic_fn("enum_dom_groups out of memory");
1541 for (i
=0; i
<(*num_entries
); i
++) {
1542 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1543 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1544 (*info
)[i
].rid
= centry_uint32(centry
);
1549 /* If we are returning cached data and the domain controller
1550 is down then we don't know whether the data is up to date
1551 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1554 if (wcache_server_down(domain
)) {
1555 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1556 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1558 status
= centry
->status
;
1560 DEBUG(10,("enum_local_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_local_groups: [Cached] - doing backend query for list for domain %s\n",
1578 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1581 refresh_sequence_number(domain
, false);
1582 centry
= centry_start(domain
, status
);
1585 centry_put_uint32(centry
, *num_entries
);
1586 for (i
=0; i
<(*num_entries
); i
++) {
1587 centry_put_string(centry
, (*info
)[i
].acct_name
);
1588 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1589 centry_put_uint32(centry
, (*info
)[i
].rid
);
1591 centry_end(centry
, "GL/%s/local", domain
->name
);
1592 centry_free(centry
);
1598 /* convert a single name to a sid in a domain */
1599 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
1600 TALLOC_CTX
*mem_ctx
,
1601 enum winbindd_cmd orig_cmd
,
1602 const char *domain_name
,
1605 enum lsa_SidType
*type
)
1607 struct winbind_cache
*cache
= get_cache(domain
);
1608 struct cache_entry
*centry
= NULL
;
1615 fstrcpy(uname
, name
);
1617 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
1621 status
= centry
->status
;
1622 if (NT_STATUS_IS_OK(status
)) {
1623 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1624 centry_sid(centry
, mem_ctx
, sid
);
1627 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1628 domain
->name
, nt_errstr(status
) ));
1630 centry_free(centry
);
1636 /* If the seq number check indicated that there is a problem
1637 * with this DC, then return that status... except for
1638 * access_denied. This is special because the dc may be in
1639 * "restrict anonymous = 1" mode, in which case it will deny
1640 * most unauthenticated operations, but *will* allow the LSA
1641 * name-to-sid that we try as a fallback. */
1643 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1644 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1645 return domain
->last_status
;
1647 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1650 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, orig_cmd
,
1651 domain_name
, name
, sid
, type
);
1654 refresh_sequence_number(domain
, false);
1656 if (domain
->online
&&
1657 (NT_STATUS_IS_OK(status
) || NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))) {
1658 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
, *type
);
1660 /* Only save the reverse mapping if this was not a UPN */
1661 if (!strchr(name
, '@')) {
1662 strupper_m(CONST_DISCARD(char *,domain_name
));
1663 strlower_m(CONST_DISCARD(char *,name
));
1664 wcache_save_sid_to_name(domain
, status
, sid
, domain_name
, name
, *type
);
1671 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1673 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
1674 TALLOC_CTX
*mem_ctx
,
1678 enum lsa_SidType
*type
)
1680 struct winbind_cache
*cache
= get_cache(domain
);
1681 struct cache_entry
*centry
= NULL
;
1688 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1689 sid_to_fstring(sid_string
, sid
));
1693 status
= centry
->status
;
1694 if (NT_STATUS_IS_OK(status
)) {
1695 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1696 *domain_name
= centry_string(centry
, mem_ctx
);
1697 *name
= centry_string(centry
, mem_ctx
);
1700 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1701 domain
->name
, nt_errstr(status
) ));
1703 centry_free(centry
);
1708 *domain_name
= NULL
;
1710 /* If the seq number check indicated that there is a problem
1711 * with this DC, then return that status... except for
1712 * access_denied. This is special because the dc may be in
1713 * "restrict anonymous = 1" mode, in which case it will deny
1714 * most unauthenticated operations, but *will* allow the LSA
1715 * sid-to-name that we try as a fallback. */
1717 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1718 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1719 return domain
->last_status
;
1721 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1724 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1727 refresh_sequence_number(domain
, false);
1728 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1730 /* We can't save the name to sid mapping here, as with sid history a
1731 * later name2sid would give the wrong sid. */
1736 static NTSTATUS
rids_to_names(struct winbindd_domain
*domain
,
1737 TALLOC_CTX
*mem_ctx
,
1738 const DOM_SID
*domain_sid
,
1743 enum lsa_SidType
**types
)
1745 struct winbind_cache
*cache
= get_cache(domain
);
1747 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1751 *domain_name
= NULL
;
1759 if (num_rids
== 0) {
1760 return NT_STATUS_OK
;
1763 *names
= TALLOC_ARRAY(mem_ctx
, char *, num_rids
);
1764 *types
= TALLOC_ARRAY(mem_ctx
, enum lsa_SidType
, num_rids
);
1766 if ((*names
== NULL
) || (*types
== NULL
)) {
1767 result
= NT_STATUS_NO_MEMORY
;
1771 have_mapped
= have_unmapped
= false;
1773 for (i
=0; i
<num_rids
; i
++) {
1775 struct cache_entry
*centry
;
1778 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1779 result
= NT_STATUS_INTERNAL_ERROR
;
1783 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1784 sid_to_fstring(tmp
, &sid
));
1789 (*types
)[i
] = SID_NAME_UNKNOWN
;
1790 (*names
)[i
] = talloc_strdup(*names
, "");
1792 if (NT_STATUS_IS_OK(centry
->status
)) {
1795 (*types
)[i
] = (enum lsa_SidType
)centry_uint32(centry
);
1797 dom
= centry_string(centry
, mem_ctx
);
1798 if (*domain_name
== NULL
) {
1804 (*names
)[i
] = centry_string(centry
, *names
);
1806 } else if (NT_STATUS_EQUAL(centry
->status
, NT_STATUS_NONE_MAPPED
)) {
1807 have_unmapped
= true;
1810 /* something's definitely wrong */
1811 result
= centry
->status
;
1815 centry_free(centry
);
1819 return NT_STATUS_NONE_MAPPED
;
1821 if (!have_unmapped
) {
1822 return NT_STATUS_OK
;
1824 return STATUS_SOME_UNMAPPED
;
1828 TALLOC_FREE(*names
);
1829 TALLOC_FREE(*types
);
1831 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
1832 rids
, num_rids
, domain_name
,
1836 None of the queried rids has been found so save all negative entries
1838 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
1839 for (i
= 0; i
< num_rids
; i
++) {
1841 const char *name
= "";
1842 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
1843 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
1845 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1846 return NT_STATUS_INTERNAL_ERROR
;
1849 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1857 Some or all of the queried rids have been found.
1859 if (!NT_STATUS_IS_OK(result
) &&
1860 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
1864 refresh_sequence_number(domain
, false);
1866 for (i
=0; i
<num_rids
; i
++) {
1870 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1871 result
= NT_STATUS_INTERNAL_ERROR
;
1875 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
1876 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
1878 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1879 (*names
)[i
], (*types
)[i
]);
1886 TALLOC_FREE(*names
);
1887 TALLOC_FREE(*types
);
1891 /* Lookup user information from a rid */
1892 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
1893 TALLOC_CTX
*mem_ctx
,
1894 const DOM_SID
*user_sid
,
1895 WINBIND_USERINFO
*info
)
1897 struct winbind_cache
*cache
= get_cache(domain
);
1898 struct cache_entry
*centry
= NULL
;
1905 centry
= wcache_fetch(cache
, domain
, "U/%s",
1906 sid_to_fstring(tmp
, user_sid
));
1908 /* If we have an access denied cache entry and a cached info3 in the
1909 samlogon cache then do a query. This will force the rpc back end
1910 to return the info3 data. */
1912 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1913 netsamlogon_cache_have(user_sid
)) {
1914 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1915 domain
->last_status
= NT_STATUS_OK
;
1916 centry_free(centry
);
1923 /* if status is not ok then this is a negative hit
1924 and the rest of the data doesn't matter */
1925 status
= centry
->status
;
1926 if (NT_STATUS_IS_OK(status
)) {
1927 info
->acct_name
= centry_string(centry
, mem_ctx
);
1928 info
->full_name
= centry_string(centry
, mem_ctx
);
1929 info
->homedir
= centry_string(centry
, mem_ctx
);
1930 info
->shell
= centry_string(centry
, mem_ctx
);
1931 info
->primary_gid
= centry_uint32(centry
);
1932 centry_sid(centry
, mem_ctx
, &info
->user_sid
);
1933 centry_sid(centry
, mem_ctx
, &info
->group_sid
);
1936 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1937 domain
->name
, nt_errstr(status
) ));
1939 centry_free(centry
);
1945 /* Return status value returned by seq number check */
1947 if (!NT_STATUS_IS_OK(domain
->last_status
))
1948 return domain
->last_status
;
1950 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1953 status
= domain
->backend
->query_user(domain
, mem_ctx
, user_sid
, info
);
1956 refresh_sequence_number(domain
, false);
1957 wcache_save_user(domain
, status
, info
);
1963 /* Lookup groups a user is a member of. */
1964 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
1965 TALLOC_CTX
*mem_ctx
,
1966 const DOM_SID
*user_sid
,
1967 uint32
*num_groups
, DOM_SID
**user_gids
)
1969 struct winbind_cache
*cache
= get_cache(domain
);
1970 struct cache_entry
*centry
= NULL
;
1978 centry
= wcache_fetch(cache
, domain
, "UG/%s",
1979 sid_to_fstring(sid_string
, user_sid
));
1981 /* If we have an access denied cache entry and a cached info3 in the
1982 samlogon cache then do a query. This will force the rpc back end
1983 to return the info3 data. */
1985 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1986 netsamlogon_cache_have(user_sid
)) {
1987 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1988 domain
->last_status
= NT_STATUS_OK
;
1989 centry_free(centry
);
1996 *num_groups
= centry_uint32(centry
);
1998 if (*num_groups
== 0)
2001 (*user_gids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_groups
);
2002 if (! (*user_gids
)) {
2003 smb_panic_fn("lookup_usergroups out of memory");
2005 for (i
=0; i
<(*num_groups
); i
++) {
2006 centry_sid(centry
, mem_ctx
, &(*user_gids
)[i
]);
2010 status
= centry
->status
;
2012 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2013 domain
->name
, nt_errstr(status
) ));
2015 centry_free(centry
);
2020 (*user_gids
) = NULL
;
2022 /* Return status value returned by seq number check */
2024 if (!NT_STATUS_IS_OK(domain
->last_status
))
2025 return domain
->last_status
;
2027 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2030 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2032 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2036 refresh_sequence_number(domain
, false);
2037 centry
= centry_start(domain
, status
);
2041 centry_put_uint32(centry
, *num_groups
);
2042 for (i
=0; i
<(*num_groups
); i
++) {
2043 centry_put_sid(centry
, &(*user_gids
)[i
]);
2046 centry_end(centry
, "UG/%s", sid_to_fstring(sid_string
, user_sid
));
2047 centry_free(centry
);
2053 static NTSTATUS
lookup_useraliases(struct winbindd_domain
*domain
,
2054 TALLOC_CTX
*mem_ctx
,
2055 uint32 num_sids
, const DOM_SID
*sids
,
2056 uint32
*num_aliases
, uint32
**alias_rids
)
2058 struct winbind_cache
*cache
= get_cache(domain
);
2059 struct cache_entry
*centry
= NULL
;
2061 char *sidlist
= talloc_strdup(mem_ctx
, "");
2067 if (num_sids
== 0) {
2070 return NT_STATUS_OK
;
2073 /* We need to cache indexed by the whole list of SIDs, the aliases
2074 * resulting might come from any of the SIDs. */
2076 for (i
=0; i
<num_sids
; i
++) {
2078 sidlist
= talloc_asprintf(mem_ctx
, "%s/%s", sidlist
,
2079 sid_to_fstring(tmp
, &sids
[i
]));
2080 if (sidlist
== NULL
)
2081 return NT_STATUS_NO_MEMORY
;
2084 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2089 *num_aliases
= centry_uint32(centry
);
2093 (*alias_rids
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_aliases
);
2095 if ((*alias_rids
) == NULL
) {
2096 centry_free(centry
);
2097 return NT_STATUS_NO_MEMORY
;
2100 (*alias_rids
) = NULL
;
2103 for (i
=0; i
<(*num_aliases
); i
++)
2104 (*alias_rids
)[i
] = centry_uint32(centry
);
2106 status
= centry
->status
;
2108 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2109 "status %s\n", domain
->name
, nt_errstr(status
)));
2111 centry_free(centry
);
2116 (*alias_rids
) = NULL
;
2118 if (!NT_STATUS_IS_OK(domain
->last_status
))
2119 return domain
->last_status
;
2121 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2122 "for domain %s\n", domain
->name
));
2124 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2126 num_aliases
, alias_rids
);
2129 refresh_sequence_number(domain
, false);
2130 centry
= centry_start(domain
, status
);
2133 centry_put_uint32(centry
, *num_aliases
);
2134 for (i
=0; i
<(*num_aliases
); i
++)
2135 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2136 centry_end(centry
, "UA%s", sidlist
);
2137 centry_free(centry
);
2144 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
2145 TALLOC_CTX
*mem_ctx
,
2146 const DOM_SID
*group_sid
, uint32
*num_names
,
2147 DOM_SID
**sid_mem
, char ***names
,
2148 uint32
**name_types
)
2150 struct winbind_cache
*cache
= get_cache(domain
);
2151 struct cache_entry
*centry
= NULL
;
2159 centry
= wcache_fetch(cache
, domain
, "GM/%s",
2160 sid_to_fstring(sid_string
, group_sid
));
2164 *num_names
= centry_uint32(centry
);
2166 if (*num_names
== 0)
2169 (*sid_mem
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_names
);
2170 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_names
);
2171 (*name_types
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_names
);
2173 if (! (*sid_mem
) || ! (*names
) || ! (*name_types
)) {
2174 smb_panic_fn("lookup_groupmem out of memory");
2177 for (i
=0; i
<(*num_names
); i
++) {
2178 centry_sid(centry
, mem_ctx
, &(*sid_mem
)[i
]);
2179 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2180 (*name_types
)[i
] = centry_uint32(centry
);
2184 status
= centry
->status
;
2186 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2187 domain
->name
, nt_errstr(status
)));
2189 centry_free(centry
);
2196 (*name_types
) = NULL
;
2198 /* Return status value returned by seq number check */
2200 if (!NT_STATUS_IS_OK(domain
->last_status
))
2201 return domain
->last_status
;
2203 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2206 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2207 sid_mem
, names
, name_types
);
2210 refresh_sequence_number(domain
, false);
2211 centry
= centry_start(domain
, status
);
2214 centry_put_uint32(centry
, *num_names
);
2215 for (i
=0; i
<(*num_names
); i
++) {
2216 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2217 centry_put_string(centry
, (*names
)[i
]);
2218 centry_put_uint32(centry
, (*name_types
)[i
]);
2220 centry_end(centry
, "GM/%s", sid_to_fstring(sid_string
, group_sid
));
2221 centry_free(centry
);
2227 /* find the sequence number for a domain */
2228 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
2230 refresh_sequence_number(domain
, false);
2232 *seq
= domain
->sequence_number
;
2234 return NT_STATUS_OK
;
2237 /* enumerate trusted domains
2238 * (we need to have the list of trustdoms in the cache when we go offline) -
2240 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
2241 TALLOC_CTX
*mem_ctx
,
2242 uint32
*num_domains
,
2247 struct winbind_cache
*cache
= get_cache(domain
);
2248 struct cache_entry
*centry
= NULL
;
2255 centry
= wcache_fetch(cache
, domain
, "TRUSTDOMS/%s", domain
->name
);
2261 *num_domains
= centry_uint32(centry
);
2264 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2265 (*alt_names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2266 (*dom_sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_domains
);
2268 if (! (*dom_sids
) || ! (*names
) || ! (*alt_names
)) {
2269 smb_panic_fn("trusted_domains out of memory");
2273 (*alt_names
) = NULL
;
2277 for (i
=0; i
<(*num_domains
); i
++) {
2278 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2279 (*alt_names
)[i
] = centry_string(centry
, mem_ctx
);
2280 if (!centry_sid(centry
, mem_ctx
, &(*dom_sids
)[i
])) {
2281 sid_copy(&(*dom_sids
)[i
], &global_sid_NULL
);
2285 status
= centry
->status
;
2287 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2288 domain
->name
, *num_domains
, nt_errstr(status
) ));
2290 centry_free(centry
);
2297 (*alt_names
) = NULL
;
2299 /* Return status value returned by seq number check */
2301 if (!NT_STATUS_IS_OK(domain
->last_status
))
2302 return domain
->last_status
;
2304 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2307 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, num_domains
,
2308 names
, alt_names
, dom_sids
);
2310 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2311 * so that the generic centry handling still applies correctly -
2314 if (!NT_STATUS_IS_ERR(status
)) {
2315 status
= NT_STATUS_OK
;
2319 #if 0 /* Disabled as we want the trust dom list to be managed by
2320 the main parent and always to make the query. --jerry */
2323 refresh_sequence_number(domain
, false);
2325 centry
= centry_start(domain
, status
);
2329 centry_put_uint32(centry
, *num_domains
);
2331 for (i
=0; i
<(*num_domains
); i
++) {
2332 centry_put_string(centry
, (*names
)[i
]);
2333 centry_put_string(centry
, (*alt_names
)[i
]);
2334 centry_put_sid(centry
, &(*dom_sids
)[i
]);
2337 centry_end(centry
, "TRUSTDOMS/%s", domain
->name
);
2339 centry_free(centry
);
2347 /* get lockout policy */
2348 static NTSTATUS
lockout_policy(struct winbindd_domain
*domain
,
2349 TALLOC_CTX
*mem_ctx
,
2350 struct samr_DomInfo12
*policy
)
2352 struct winbind_cache
*cache
= get_cache(domain
);
2353 struct cache_entry
*centry
= NULL
;
2359 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2364 policy
->lockout_duration
= centry_nttime(centry
);
2365 policy
->lockout_window
= centry_nttime(centry
);
2366 policy
->lockout_threshold
= centry_uint16(centry
);
2368 status
= centry
->status
;
2370 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2371 domain
->name
, nt_errstr(status
) ));
2373 centry_free(centry
);
2377 ZERO_STRUCTP(policy
);
2379 /* Return status value returned by seq number check */
2381 if (!NT_STATUS_IS_OK(domain
->last_status
))
2382 return domain
->last_status
;
2384 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2387 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2390 refresh_sequence_number(domain
, false);
2391 wcache_save_lockout_policy(domain
, status
, policy
);
2396 /* get password policy */
2397 static NTSTATUS
password_policy(struct winbindd_domain
*domain
,
2398 TALLOC_CTX
*mem_ctx
,
2399 struct samr_DomInfo1
*policy
)
2401 struct winbind_cache
*cache
= get_cache(domain
);
2402 struct cache_entry
*centry
= NULL
;
2408 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
2413 policy
->min_password_length
= centry_uint16(centry
);
2414 policy
->password_history_length
= centry_uint16(centry
);
2415 policy
->password_properties
= centry_uint32(centry
);
2416 policy
->max_password_age
= centry_nttime(centry
);
2417 policy
->min_password_age
= centry_nttime(centry
);
2419 status
= centry
->status
;
2421 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2422 domain
->name
, nt_errstr(status
) ));
2424 centry_free(centry
);
2428 ZERO_STRUCTP(policy
);
2430 /* Return status value returned by seq number check */
2432 if (!NT_STATUS_IS_OK(domain
->last_status
))
2433 return domain
->last_status
;
2435 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2438 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
2441 refresh_sequence_number(domain
, false);
2442 if (NT_STATUS_IS_OK(status
)) {
2443 wcache_save_password_policy(domain
, status
, policy
);
2450 /* Invalidate cached user and group lists coherently */
2452 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2455 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
2456 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
2457 tdb_delete(the_tdb
, kbuf
);
2462 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2464 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
2465 struct netr_SamInfo3
*info3
)
2468 fstring key_str
, sid_string
;
2469 struct winbind_cache
*cache
;
2471 /* dont clear cached U/SID and UG/SID entries when we want to logon
2474 if (lp_winbind_offline_logon()) {
2481 cache
= get_cache(domain
);
2487 sid_copy(&sid
, info3
->base
.domain_sid
);
2488 sid_append_rid(&sid
, info3
->base
.rid
);
2490 /* Clear U/SID cache entry */
2491 fstr_sprintf(key_str
, "U/%s", sid_to_fstring(sid_string
, &sid
));
2492 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2493 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2495 /* Clear UG/SID cache entry */
2496 fstr_sprintf(key_str
, "UG/%s", sid_to_fstring(sid_string
, &sid
));
2497 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2498 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2500 /* Samba/winbindd never needs this. */
2501 netsamlogon_clear_cached_user(info3
);
2504 bool wcache_invalidate_cache(void)
2506 struct winbindd_domain
*domain
;
2508 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
2509 struct winbind_cache
*cache
= get_cache(domain
);
2511 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2512 "entries for %s\n", domain
->name
));
2515 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
2524 bool init_wcache(void)
2526 if (wcache
== NULL
) {
2527 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
2528 ZERO_STRUCTP(wcache
);
2531 if (wcache
->tdb
!= NULL
)
2534 /* when working offline we must not clear the cache on restart */
2535 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"),
2536 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
2537 lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
2538 O_RDWR
|O_CREAT
, 0600);
2540 if (wcache
->tdb
== NULL
) {
2541 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2548 /************************************************************************
2549 This is called by the parent to initialize the cache file.
2550 We don't need sophisticated locking here as we know we're the
2552 ************************************************************************/
2554 bool initialize_winbindd_cache(void)
2556 bool cache_bad
= true;
2559 if (!init_wcache()) {
2560 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2564 /* Check version number. */
2565 if (tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
) &&
2566 vers
== WINBINDD_CACHE_VERSION
) {
2571 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2572 "and re-creating with version number %d\n",
2573 WINBINDD_CACHE_VERSION
));
2575 tdb_close(wcache
->tdb
);
2578 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2579 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2580 lock_path("winbindd_cache.tdb"),
2584 if (!init_wcache()) {
2585 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2586 "init_wcache failed.\n"));
2590 /* Write the version. */
2591 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
2592 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2593 tdb_errorstr(wcache
->tdb
) ));
2598 tdb_close(wcache
->tdb
);
2603 void close_winbindd_cache(void)
2609 tdb_close(wcache
->tdb
);
2614 void cache_store_response(pid_t pid
, struct winbindd_response
*response
)
2621 DEBUG(10, ("Storing response for pid %d, len %d\n",
2622 pid
, response
->length
));
2624 fstr_sprintf(key_str
, "DR/%d", pid
);
2625 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2626 make_tdb_data((uint8
*)response
, sizeof(*response
)),
2630 if (response
->length
== sizeof(*response
))
2633 /* There's extra data */
2635 DEBUG(10, ("Storing extra data: len=%d\n",
2636 (int)(response
->length
- sizeof(*response
))));
2638 fstr_sprintf(key_str
, "DE/%d", pid
);
2639 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2640 make_tdb_data((uint8
*)response
->extra_data
.data
,
2641 response
->length
- sizeof(*response
)),
2645 /* We could not store the extra data, make sure the tdb does not
2646 * contain a main record with wrong dangling extra data */
2648 fstr_sprintf(key_str
, "DR/%d", pid
);
2649 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2654 bool cache_retrieve_response(pid_t pid
, struct winbindd_response
* response
)
2662 DEBUG(10, ("Retrieving response for pid %d\n", pid
));
2664 fstr_sprintf(key_str
, "DR/%d", pid
);
2665 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2667 if (data
.dptr
== NULL
)
2670 if (data
.dsize
!= sizeof(*response
))
2673 memcpy(response
, data
.dptr
, data
.dsize
);
2674 SAFE_FREE(data
.dptr
);
2676 if (response
->length
== sizeof(*response
)) {
2677 response
->extra_data
.data
= NULL
;
2681 /* There's extra data */
2683 DEBUG(10, ("Retrieving extra data length=%d\n",
2684 (int)(response
->length
- sizeof(*response
))));
2686 fstr_sprintf(key_str
, "DE/%d", pid
);
2687 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2689 if (data
.dptr
== NULL
) {
2690 DEBUG(0, ("Did not find extra data\n"));
2694 if (data
.dsize
!= (response
->length
- sizeof(*response
))) {
2695 DEBUG(0, ("Invalid extra data length: %d\n", (int)data
.dsize
));
2696 SAFE_FREE(data
.dptr
);
2700 dump_data(11, (uint8
*)data
.dptr
, data
.dsize
);
2702 response
->extra_data
.data
= data
.dptr
;
2706 void cache_cleanup_response(pid_t pid
)
2713 fstr_sprintf(key_str
, "DR/%d", pid
);
2714 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2716 fstr_sprintf(key_str
, "DE/%d", pid
);
2717 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2723 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
2724 char **domain_name
, char **name
,
2725 enum lsa_SidType
*type
)
2727 struct winbindd_domain
*domain
;
2728 struct winbind_cache
*cache
;
2729 struct cache_entry
*centry
= NULL
;
2733 domain
= find_lookup_domain_from_sid(sid
);
2734 if (domain
== NULL
) {
2738 cache
= get_cache(domain
);
2740 if (cache
->tdb
== NULL
) {
2744 centry
= wcache_fetch(cache
, domain
, "SN/%s",
2745 sid_to_fstring(tmp
, sid
));
2746 if (centry
== NULL
) {
2750 if (NT_STATUS_IS_OK(centry
->status
)) {
2751 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2752 *domain_name
= centry_string(centry
, mem_ctx
);
2753 *name
= centry_string(centry
, mem_ctx
);
2756 status
= centry
->status
;
2757 centry_free(centry
);
2758 return NT_STATUS_IS_OK(status
);
2761 bool lookup_cached_name(TALLOC_CTX
*mem_ctx
,
2762 const char *domain_name
,
2765 enum lsa_SidType
*type
)
2767 struct winbindd_domain
*domain
;
2768 struct winbind_cache
*cache
;
2769 struct cache_entry
*centry
= NULL
;
2772 bool original_online_state
;
2774 domain
= find_lookup_domain_from_name(domain_name
);
2775 if (domain
== NULL
) {
2779 cache
= get_cache(domain
);
2781 if (cache
->tdb
== NULL
) {
2785 fstrcpy(uname
, name
);
2788 /* If we are doing a cached logon, temporarily set the domain
2789 offline so the cache won't expire the entry */
2791 original_online_state
= domain
->online
;
2792 domain
->online
= false;
2793 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
2794 domain
->online
= original_online_state
;
2796 if (centry
== NULL
) {
2800 if (NT_STATUS_IS_OK(centry
->status
)) {
2801 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2802 centry_sid(centry
, mem_ctx
, sid
);
2805 status
= centry
->status
;
2806 centry_free(centry
);
2808 return NT_STATUS_IS_OK(status
);
2811 void cache_name2sid(struct winbindd_domain
*domain
,
2812 const char *domain_name
, const char *name
,
2813 enum lsa_SidType type
, const DOM_SID
*sid
)
2815 refresh_sequence_number(domain
, false);
2816 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
2821 * The original idea that this cache only contains centries has
2822 * been blurred - now other stuff gets put in here. Ensure we
2823 * ignore these things on cleanup.
2826 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
2827 TDB_DATA dbuf
, void *state
)
2829 struct cache_entry
*centry
;
2831 if (is_non_centry_key(kbuf
)) {
2835 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
2840 if (!NT_STATUS_IS_OK(centry
->status
)) {
2841 DEBUG(10,("deleting centry %s\n", (const char *)kbuf
.dptr
));
2842 tdb_delete(the_tdb
, kbuf
);
2845 centry_free(centry
);
2849 /* flush the cache */
2850 void wcache_flush_cache(void)
2855 tdb_close(wcache
->tdb
);
2861 /* when working offline we must not clear the cache on restart */
2862 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"),
2863 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
2864 lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
2865 O_RDWR
|O_CREAT
, 0600);
2868 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2872 tdb_traverse(wcache
->tdb
, traverse_fn_cleanup
, NULL
);
2874 DEBUG(10,("wcache_flush_cache success\n"));
2877 /* Count cached creds */
2879 static int traverse_fn_cached_creds(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2882 int *cred_count
= (int*)state
;
2884 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
2890 NTSTATUS
wcache_count_cached_creds(struct winbindd_domain
*domain
, int *count
)
2892 struct winbind_cache
*cache
= get_cache(domain
);
2897 return NT_STATUS_INTERNAL_DB_ERROR
;
2900 tdb_traverse(cache
->tdb
, traverse_fn_cached_creds
, (void *)count
);
2902 return NT_STATUS_OK
;
2906 struct cred_list
*prev
, *next
;
2911 static struct cred_list
*wcache_cred_list
;
2913 static int traverse_fn_get_credlist(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2916 struct cred_list
*cred
;
2918 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
2920 cred
= SMB_MALLOC_P(struct cred_list
);
2922 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2928 /* save a copy of the key */
2930 fstrcpy(cred
->name
, (const char *)kbuf
.dptr
);
2931 DLIST_ADD(wcache_cred_list
, cred
);
2937 NTSTATUS
wcache_remove_oldest_cached_creds(struct winbindd_domain
*domain
, const DOM_SID
*sid
)
2939 struct winbind_cache
*cache
= get_cache(domain
);
2942 struct cred_list
*cred
, *oldest
= NULL
;
2945 return NT_STATUS_INTERNAL_DB_ERROR
;
2948 /* we possibly already have an entry */
2949 if (sid
&& NT_STATUS_IS_OK(wcache_cached_creds_exist(domain
, sid
))) {
2951 fstring key_str
, tmp
;
2953 DEBUG(11,("we already have an entry, deleting that\n"));
2955 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
2957 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2959 return NT_STATUS_OK
;
2962 ret
= tdb_traverse(cache
->tdb
, traverse_fn_get_credlist
, NULL
);
2964 return NT_STATUS_OK
;
2965 } else if ((ret
== -1) || (wcache_cred_list
== NULL
)) {
2966 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
2969 ZERO_STRUCTP(oldest
);
2971 for (cred
= wcache_cred_list
; cred
; cred
= cred
->next
) {
2976 data
= tdb_fetch(cache
->tdb
, string_tdb_data(cred
->name
));
2978 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2980 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
2984 t
= IVAL(data
.dptr
, 0);
2985 SAFE_FREE(data
.dptr
);
2988 oldest
= SMB_MALLOC_P(struct cred_list
);
2989 if (oldest
== NULL
) {
2990 status
= NT_STATUS_NO_MEMORY
;
2994 fstrcpy(oldest
->name
, cred
->name
);
2995 oldest
->created
= t
;
2999 if (t
< oldest
->created
) {
3000 fstrcpy(oldest
->name
, cred
->name
);
3001 oldest
->created
= t
;
3005 if (tdb_delete(cache
->tdb
, string_tdb_data(oldest
->name
)) == 0) {
3006 status
= NT_STATUS_OK
;
3008 status
= NT_STATUS_UNSUCCESSFUL
;
3011 SAFE_FREE(wcache_cred_list
);
3017 /* Change the global online/offline state. */
3018 bool set_global_winbindd_state_offline(void)
3022 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3024 /* Only go offline if someone has created
3025 the key "WINBINDD_OFFLINE" in the cache tdb. */
3027 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3028 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3032 if (!lp_winbind_offline_logon()) {
3033 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3037 if (global_winbindd_offline_state
) {
3038 /* Already offline. */
3042 data
= tdb_fetch_bystring( wcache
->tdb
, "WINBINDD_OFFLINE" );
3044 if (!data
.dptr
|| data
.dsize
!= 4) {
3045 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3046 SAFE_FREE(data
.dptr
);
3049 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3050 global_winbindd_offline_state
= true;
3051 SAFE_FREE(data
.dptr
);
3056 void set_global_winbindd_state_online(void)
3058 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3060 if (!lp_winbind_offline_logon()) {
3061 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3065 if (!global_winbindd_offline_state
) {
3066 /* Already online. */
3069 global_winbindd_offline_state
= false;
3075 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3076 tdb_delete_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3079 bool get_global_winbindd_state_offline(void)
3081 return global_winbindd_offline_state
;
3084 /***********************************************************************
3085 Validate functions for all possible cache tdb keys.
3086 ***********************************************************************/
3088 static struct cache_entry
*create_centry_validate(const char *kstr
, TDB_DATA data
,
3089 struct tdb_validation_status
*state
)
3091 struct cache_entry
*centry
;
3093 centry
= SMB_XMALLOC_P(struct cache_entry
);
3094 centry
->data
= (unsigned char *)memdup(data
.dptr
, data
.dsize
);
3095 if (!centry
->data
) {
3099 centry
->len
= data
.dsize
;
3102 if (centry
->len
< 8) {
3103 /* huh? corrupt cache? */
3104 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr
));
3105 centry_free(centry
);
3106 state
->bad_entry
= true;
3107 state
->success
= false;
3111 centry
->status
= NT_STATUS(centry_uint32(centry
));
3112 centry
->sequence_number
= centry_uint32(centry
);
3116 static int validate_seqnum(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3117 struct tdb_validation_status
*state
)
3119 if (dbuf
.dsize
!= 8) {
3120 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3121 keystr
, (unsigned int)dbuf
.dsize
));
3122 state
->bad_entry
= true;
3128 static int validate_ns(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3129 struct tdb_validation_status
*state
)
3131 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3136 (void)centry_uint32(centry
);
3137 if (NT_STATUS_IS_OK(centry
->status
)) {
3139 (void)centry_sid(centry
, mem_ctx
, &sid
);
3142 centry_free(centry
);
3144 if (!(state
->success
)) {
3147 DEBUG(10,("validate_ns: %s ok\n", keystr
));
3151 static int validate_sn(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3152 struct tdb_validation_status
*state
)
3154 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3159 if (NT_STATUS_IS_OK(centry
->status
)) {
3160 (void)centry_uint32(centry
);
3161 (void)centry_string(centry
, mem_ctx
);
3162 (void)centry_string(centry
, mem_ctx
);
3165 centry_free(centry
);
3167 if (!(state
->success
)) {
3170 DEBUG(10,("validate_sn: %s ok\n", keystr
));
3174 static int validate_u(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3175 struct tdb_validation_status
*state
)
3177 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3184 (void)centry_string(centry
, mem_ctx
);
3185 (void)centry_string(centry
, mem_ctx
);
3186 (void)centry_string(centry
, mem_ctx
);
3187 (void)centry_string(centry
, mem_ctx
);
3188 (void)centry_uint32(centry
);
3189 (void)centry_sid(centry
, mem_ctx
, &sid
);
3190 (void)centry_sid(centry
, mem_ctx
, &sid
);
3192 centry_free(centry
);
3194 if (!(state
->success
)) {
3197 DEBUG(10,("validate_u: %s ok\n", keystr
));
3201 static int validate_loc_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3202 struct tdb_validation_status
*state
)
3204 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3210 (void)centry_nttime(centry
);
3211 (void)centry_nttime(centry
);
3212 (void)centry_uint16(centry
);
3214 centry_free(centry
);
3216 if (!(state
->success
)) {
3219 DEBUG(10,("validate_loc_pol: %s ok\n", keystr
));
3223 static int validate_pwd_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3224 struct tdb_validation_status
*state
)
3226 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3232 (void)centry_uint16(centry
);
3233 (void)centry_uint16(centry
);
3234 (void)centry_uint32(centry
);
3235 (void)centry_nttime(centry
);
3236 (void)centry_nttime(centry
);
3238 centry_free(centry
);
3240 if (!(state
->success
)) {
3243 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr
));
3247 static int validate_cred(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3248 struct tdb_validation_status
*state
)
3250 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3256 (void)centry_time(centry
);
3257 (void)centry_hash16(centry
, mem_ctx
);
3259 /* We only have 17 bytes more data in the salted cred case. */
3260 if (centry
->len
- centry
->ofs
== 17) {
3261 (void)centry_hash16(centry
, mem_ctx
);
3264 centry_free(centry
);
3266 if (!(state
->success
)) {
3269 DEBUG(10,("validate_cred: %s ok\n", keystr
));
3273 static int validate_ul(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3274 struct tdb_validation_status
*state
)
3276 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3277 int32 num_entries
, i
;
3283 num_entries
= (int32
)centry_uint32(centry
);
3285 for (i
=0; i
< num_entries
; i
++) {
3287 (void)centry_string(centry
, mem_ctx
);
3288 (void)centry_string(centry
, mem_ctx
);
3289 (void)centry_string(centry
, mem_ctx
);
3290 (void)centry_string(centry
, mem_ctx
);
3291 (void)centry_sid(centry
, mem_ctx
, &sid
);
3292 (void)centry_sid(centry
, mem_ctx
, &sid
);
3295 centry_free(centry
);
3297 if (!(state
->success
)) {
3300 DEBUG(10,("validate_ul: %s ok\n", keystr
));
3304 static int validate_gl(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3305 struct tdb_validation_status
*state
)
3307 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3308 int32 num_entries
, i
;
3314 num_entries
= centry_uint32(centry
);
3316 for (i
=0; i
< num_entries
; i
++) {
3317 (void)centry_string(centry
, mem_ctx
);
3318 (void)centry_string(centry
, mem_ctx
);
3319 (void)centry_uint32(centry
);
3322 centry_free(centry
);
3324 if (!(state
->success
)) {
3327 DEBUG(10,("validate_gl: %s ok\n", keystr
));
3331 static int validate_ug(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3332 struct tdb_validation_status
*state
)
3334 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3335 int32 num_groups
, i
;
3341 num_groups
= centry_uint32(centry
);
3343 for (i
=0; i
< num_groups
; i
++) {
3345 centry_sid(centry
, mem_ctx
, &sid
);
3348 centry_free(centry
);
3350 if (!(state
->success
)) {
3353 DEBUG(10,("validate_ug: %s ok\n", keystr
));
3357 static int validate_ua(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3358 struct tdb_validation_status
*state
)
3360 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3361 int32 num_aliases
, i
;
3367 num_aliases
= centry_uint32(centry
);
3369 for (i
=0; i
< num_aliases
; i
++) {
3370 (void)centry_uint32(centry
);
3373 centry_free(centry
);
3375 if (!(state
->success
)) {
3378 DEBUG(10,("validate_ua: %s ok\n", keystr
));
3382 static int validate_gm(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3383 struct tdb_validation_status
*state
)
3385 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3392 num_names
= centry_uint32(centry
);
3394 for (i
=0; i
< num_names
; i
++) {
3396 centry_sid(centry
, mem_ctx
, &sid
);
3397 (void)centry_string(centry
, mem_ctx
);
3398 (void)centry_uint32(centry
);
3401 centry_free(centry
);
3403 if (!(state
->success
)) {
3406 DEBUG(10,("validate_gm: %s ok\n", keystr
));
3410 static int validate_dr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3411 struct tdb_validation_status
*state
)
3413 /* Can't say anything about this other than must be nonzero. */
3414 if (dbuf
.dsize
== 0) {
3415 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3417 state
->bad_entry
= true;
3418 state
->success
= false;
3422 DEBUG(10,("validate_dr: %s ok\n", keystr
));
3426 static int validate_de(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3427 struct tdb_validation_status
*state
)
3429 /* Can't say anything about this other than must be nonzero. */
3430 if (dbuf
.dsize
== 0) {
3431 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3433 state
->bad_entry
= true;
3434 state
->success
= false;
3438 DEBUG(10,("validate_de: %s ok\n", keystr
));
3442 static int validate_pwinfo(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3443 TDB_DATA dbuf
, struct tdb_validation_status
*state
)
3445 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3451 (void)centry_string(centry
, mem_ctx
);
3452 (void)centry_string(centry
, mem_ctx
);
3453 (void)centry_string(centry
, mem_ctx
);
3454 (void)centry_uint32(centry
);
3456 centry_free(centry
);
3458 if (!(state
->success
)) {
3461 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3465 static int validate_nss_an(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3467 struct tdb_validation_status
*state
)
3469 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3475 (void)centry_string( centry
, mem_ctx
);
3477 centry_free(centry
);
3479 if (!(state
->success
)) {
3482 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3486 static int validate_nss_na(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3488 struct tdb_validation_status
*state
)
3490 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3496 (void)centry_string( centry
, mem_ctx
);
3498 centry_free(centry
);
3500 if (!(state
->success
)) {
3503 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3507 static int validate_trustdoms(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3508 struct tdb_validation_status
*state
)
3510 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3511 int32 num_domains
, i
;
3517 num_domains
= centry_uint32(centry
);
3519 for (i
=0; i
< num_domains
; i
++) {
3521 (void)centry_string(centry
, mem_ctx
);
3522 (void)centry_string(centry
, mem_ctx
);
3523 (void)centry_sid(centry
, mem_ctx
, &sid
);
3526 centry_free(centry
);
3528 if (!(state
->success
)) {
3531 DEBUG(10,("validate_trustdoms: %s ok\n", keystr
));
3535 static int validate_trustdomcache(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3537 struct tdb_validation_status
*state
)
3539 if (dbuf
.dsize
== 0) {
3540 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3541 "key %s (len ==0) ?\n", keystr
));
3542 state
->bad_entry
= true;
3543 state
->success
= false;
3547 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr
));
3548 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3552 static int validate_offline(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3553 struct tdb_validation_status
*state
)
3555 if (dbuf
.dsize
!= 4) {
3556 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3557 keystr
, (unsigned int)dbuf
.dsize
));
3558 state
->bad_entry
= true;
3559 state
->success
= false;
3562 DEBUG(10,("validate_offline: %s ok\n", keystr
));
3566 static int validate_cache_version(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3567 struct tdb_validation_status
*state
)
3569 if (dbuf
.dsize
!= 4) {
3570 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3571 "key %s (len %u != 4) ?\n",
3572 keystr
, (unsigned int)dbuf
.dsize
));
3573 state
->bad_entry
= true;
3574 state
->success
= false;
3578 DEBUG(10, ("validate_cache_version: %s ok\n", keystr
));
3582 /***********************************************************************
3583 A list of all possible cache tdb keys with associated validation
3585 ***********************************************************************/
3587 struct key_val_struct
{
3588 const char *keyname
;
3589 int (*validate_data_fn
)(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
, struct tdb_validation_status
* state
);
3591 {"SEQNUM/", validate_seqnum
},
3592 {"NS/", validate_ns
},
3593 {"SN/", validate_sn
},
3595 {"LOC_POL/", validate_loc_pol
},
3596 {"PWD_POL/", validate_pwd_pol
},
3597 {"CRED/", validate_cred
},
3598 {"UL/", validate_ul
},
3599 {"GL/", validate_gl
},
3600 {"UG/", validate_ug
},
3601 {"UA", validate_ua
},
3602 {"GM/", validate_gm
},
3603 {"DR/", validate_dr
},
3604 {"DE/", validate_de
},
3605 {"NSS/PWINFO/", validate_pwinfo
},
3606 {"TRUSTDOMS/", validate_trustdoms
},
3607 {"TRUSTDOMCACHE/", validate_trustdomcache
},
3608 {"NSS/NA/", validate_nss_na
},
3609 {"NSS/AN/", validate_nss_an
},
3610 {"WINBINDD_OFFLINE", validate_offline
},
3611 {WINBINDD_CACHE_VERSION_KEYSTR
, validate_cache_version
},
3615 /***********************************************************************
3616 Function to look at every entry in the tdb and validate it as far as
3618 ***********************************************************************/
3620 static int cache_traverse_validate_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
3623 unsigned int max_key_len
= 1024;
3624 struct tdb_validation_status
*v_state
= (struct tdb_validation_status
*)state
;
3626 /* Paranoia check. */
3627 if (strncmp("UA/", (const char *)kbuf
.dptr
, 3) == 0) {
3628 max_key_len
= 1024 * 1024;
3630 if (kbuf
.dsize
> max_key_len
) {
3631 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3633 (unsigned int)kbuf
.dsize
, (unsigned int)max_key_len
));
3637 for (i
= 0; key_val
[i
].keyname
; i
++) {
3638 size_t namelen
= strlen(key_val
[i
].keyname
);
3639 if (kbuf
.dsize
>= namelen
&& (
3640 strncmp(key_val
[i
].keyname
, (const char *)kbuf
.dptr
, namelen
)) == 0) {
3641 TALLOC_CTX
*mem_ctx
;
3645 keystr
= SMB_MALLOC_ARRAY(char, kbuf
.dsize
+1);
3649 memcpy(keystr
, kbuf
.dptr
, kbuf
.dsize
);
3650 keystr
[kbuf
.dsize
] = '\0';
3652 mem_ctx
= talloc_init("validate_ctx");
3658 ret
= key_val
[i
].validate_data_fn(mem_ctx
, keystr
, dbuf
,
3662 talloc_destroy(mem_ctx
);
3667 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3668 dump_data(0, (uint8
*)kbuf
.dptr
, kbuf
.dsize
);
3669 DEBUG(0,("data :\n"));
3670 dump_data(0, (uint8
*)dbuf
.dptr
, dbuf
.dsize
);
3671 v_state
->unknown_key
= true;
3672 v_state
->success
= false;
3673 return 1; /* terminate. */
3676 static void validate_panic(const char *const why
)
3678 DEBUG(0,("validating cache: would panic %s\n", why
));
3679 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3683 /***********************************************************************
3684 Try and validate every entry in the winbindd cache. If we fail here,
3685 delete the cache tdb and return non-zero.
3686 ***********************************************************************/
3688 int winbindd_validate_cache(void)
3691 const char *tdb_path
= lock_path("winbindd_cache.tdb");
3692 TDB_CONTEXT
*tdb
= NULL
;
3694 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3695 smb_panic_fn
= validate_panic
;
3698 tdb
= tdb_open_log(tdb_path
,
3699 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3700 ( lp_winbind_offline_logon()
3702 : TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
3706 DEBUG(0, ("winbindd_validate_cache: "
3707 "error opening/initializing tdb\n"));
3712 ret
= tdb_validate_and_backup(tdb_path
, cache_traverse_validate_fn
);
3715 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3716 DEBUGADD(10, ("removing tdb %s.\n", tdb_path
));
3721 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3722 smb_panic_fn
= smb_panic
;
3726 /***********************************************************************
3727 Try and validate every entry in the winbindd cache.
3728 ***********************************************************************/
3730 int winbindd_validate_cache_nobackup(void)
3733 const char *tdb_path
= lock_path("winbindd_cache.tdb");
3735 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3736 smb_panic_fn
= validate_panic
;
3739 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3740 ret
= tdb_validate_open(tdb_path
, cache_traverse_validate_fn
);
3742 ret
= tdb_validate(wcache
->tdb
, cache_traverse_validate_fn
);
3746 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3750 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3752 smb_panic_fn
= smb_panic
;
3756 bool winbindd_cache_validate_and_initialize(void)
3758 close_winbindd_cache();
3760 if (lp_winbind_offline_logon()) {
3761 if (winbindd_validate_cache() < 0) {
3762 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3763 "could be restored.\n"));
3767 return initialize_winbindd_cache();
3770 /*********************************************************************
3771 ********************************************************************/
3773 static bool add_wbdomain_to_tdc_array( struct winbindd_domain
*new_dom
,
3774 struct winbindd_tdc_domain
**domains
,
3775 size_t *num_domains
)
3777 struct winbindd_tdc_domain
*list
= NULL
;
3780 bool set_only
= false;
3782 /* don't allow duplicates */
3787 for ( i
=0; i
< (*num_domains
); i
++ ) {
3788 if ( strequal( new_dom
->name
, list
[i
].domain_name
) ) {
3789 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3800 list
= TALLOC_ARRAY( NULL
, struct winbindd_tdc_domain
, 1 );
3803 list
= TALLOC_REALLOC_ARRAY( *domains
, *domains
,
3804 struct winbindd_tdc_domain
,
3809 ZERO_STRUCT( list
[idx
] );
3815 list
[idx
].domain_name
= talloc_strdup( list
, new_dom
->name
);
3816 list
[idx
].dns_name
= talloc_strdup( list
, new_dom
->alt_name
);
3818 if ( !is_null_sid( &new_dom
->sid
) ) {
3819 sid_copy( &list
[idx
].sid
, &new_dom
->sid
);
3821 sid_copy(&list
[idx
].sid
, &global_sid_NULL
);
3824 if ( new_dom
->domain_flags
!= 0x0 )
3825 list
[idx
].trust_flags
= new_dom
->domain_flags
;
3827 if ( new_dom
->domain_type
!= 0x0 )
3828 list
[idx
].trust_type
= new_dom
->domain_type
;
3830 if ( new_dom
->domain_trust_attribs
!= 0x0 )
3831 list
[idx
].trust_attribs
= new_dom
->domain_trust_attribs
;
3835 *num_domains
= idx
+ 1;
3841 /*********************************************************************
3842 ********************************************************************/
3844 static TDB_DATA
make_tdc_key( const char *domain_name
)
3846 char *keystr
= NULL
;
3847 TDB_DATA key
= { NULL
, 0 };
3849 if ( !domain_name
) {
3850 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3855 asprintf( &keystr
, "TRUSTDOMCACHE/%s", domain_name
);
3856 key
= string_term_tdb_data(keystr
);
3861 /*********************************************************************
3862 ********************************************************************/
3864 static int pack_tdc_domains( struct winbindd_tdc_domain
*domains
,
3866 unsigned char **buf
)
3868 unsigned char *buffer
= NULL
;
3873 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3881 /* Store the number of array items first */
3882 len
+= tdb_pack( buffer
+len
, buflen
-len
, "d",
3885 /* now pack each domain trust record */
3886 for ( i
=0; i
<num_domains
; i
++ ) {
3891 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3892 domains
[i
].domain_name
,
3893 domains
[i
].dns_name
? domains
[i
].dns_name
: "UNKNOWN" ));
3896 len
+= tdb_pack( buffer
+len
, buflen
-len
, "fffddd",
3897 domains
[i
].domain_name
,
3898 domains
[i
].dns_name
,
3899 sid_to_fstring(tmp
, &domains
[i
].sid
),
3900 domains
[i
].trust_flags
,
3901 domains
[i
].trust_attribs
,
3902 domains
[i
].trust_type
);
3905 if ( buflen
< len
) {
3907 if ( (buffer
= SMB_MALLOC_ARRAY(unsigned char, len
)) == NULL
) {
3908 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3922 /*********************************************************************
3923 ********************************************************************/
3925 static size_t unpack_tdc_domains( unsigned char *buf
, int buflen
,
3926 struct winbindd_tdc_domain
**domains
)
3928 fstring domain_name
, dns_name
, sid_string
;
3929 uint32 type
, attribs
, flags
;
3933 struct winbindd_tdc_domain
*list
= NULL
;
3935 /* get the number of domains */
3936 len
+= tdb_unpack( buf
+len
, buflen
-len
, "d", &num_domains
);
3938 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3942 list
= TALLOC_ARRAY( NULL
, struct winbindd_tdc_domain
, num_domains
);
3944 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3948 for ( i
=0; i
<num_domains
; i
++ ) {
3949 len
+= tdb_unpack( buf
+len
, buflen
-len
, "fffddd",
3958 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3959 TALLOC_FREE( list
);
3963 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3964 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3965 domain_name
, dns_name
, sid_string
,
3966 flags
, attribs
, type
));
3968 list
[i
].domain_name
= talloc_strdup( list
, domain_name
);
3969 list
[i
].dns_name
= talloc_strdup( list
, dns_name
);
3970 if ( !string_to_sid( &(list
[i
].sid
), sid_string
) ) {
3971 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3974 list
[i
].trust_flags
= flags
;
3975 list
[i
].trust_attribs
= attribs
;
3976 list
[i
].trust_type
= type
;
3984 /*********************************************************************
3985 ********************************************************************/
3987 static bool wcache_tdc_store_list( struct winbindd_tdc_domain
*domains
, size_t num_domains
)
3989 TDB_DATA key
= make_tdc_key( lp_workgroup() );
3990 TDB_DATA data
= { NULL
, 0 };
3996 /* See if we were asked to delete the cache entry */
3999 ret
= tdb_delete( wcache
->tdb
, key
);
4003 data
.dsize
= pack_tdc_domains( domains
, num_domains
, &data
.dptr
);
4010 ret
= tdb_store( wcache
->tdb
, key
, data
, 0 );
4013 SAFE_FREE( data
.dptr
);
4014 SAFE_FREE( key
.dptr
);
4016 return ( ret
!= -1 );
4019 /*********************************************************************
4020 ********************************************************************/
4022 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain
**domains
, size_t *num_domains
)
4024 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4025 TDB_DATA data
= { NULL
, 0 };
4033 data
= tdb_fetch( wcache
->tdb
, key
);
4035 SAFE_FREE( key
.dptr
);
4040 *num_domains
= unpack_tdc_domains( data
.dptr
, data
.dsize
, domains
);
4042 SAFE_FREE( data
.dptr
);
4050 /*********************************************************************
4051 ********************************************************************/
4053 bool wcache_tdc_add_domain( struct winbindd_domain
*domain
)
4055 struct winbindd_tdc_domain
*dom_list
= NULL
;
4056 size_t num_domains
= 0;
4059 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4060 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4061 domain
->name
, domain
->alt_name
,
4062 sid_string_dbg(&domain
->sid
),
4063 domain
->domain_flags
,
4064 domain
->domain_trust_attribs
,
4065 domain
->domain_type
));
4067 if ( !init_wcache() ) {
4071 /* fetch the list */
4073 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4075 /* add the new domain */
4077 if ( !add_wbdomain_to_tdc_array( domain
, &dom_list
, &num_domains
) ) {
4081 /* pack the domain */
4083 if ( !wcache_tdc_store_list( dom_list
, num_domains
) ) {
4091 TALLOC_FREE( dom_list
);
4096 /*********************************************************************
4097 ********************************************************************/
4099 struct winbindd_tdc_domain
* wcache_tdc_fetch_domain( TALLOC_CTX
*ctx
, const char *name
)
4101 struct winbindd_tdc_domain
*dom_list
= NULL
;
4102 size_t num_domains
= 0;
4104 struct winbindd_tdc_domain
*d
= NULL
;
4106 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name
));
4108 if ( !init_wcache() ) {
4112 /* fetch the list */
4114 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4116 for ( i
=0; i
<num_domains
; i
++ ) {
4117 if ( strequal(name
, dom_list
[i
].domain_name
) ||
4118 strequal(name
, dom_list
[i
].dns_name
) )
4120 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4123 d
= TALLOC_P( ctx
, struct winbindd_tdc_domain
);
4127 d
->domain_name
= talloc_strdup( d
, dom_list
[i
].domain_name
);
4128 d
->dns_name
= talloc_strdup( d
, dom_list
[i
].dns_name
);
4129 sid_copy( &d
->sid
, &dom_list
[i
].sid
);
4130 d
->trust_flags
= dom_list
[i
].trust_flags
;
4131 d
->trust_type
= dom_list
[i
].trust_type
;
4132 d
->trust_attribs
= dom_list
[i
].trust_attribs
;
4138 TALLOC_FREE( dom_list
);
4144 /*********************************************************************
4145 ********************************************************************/
4147 void wcache_tdc_clear( void )
4149 if ( !init_wcache() )
4152 wcache_tdc_store_list( NULL
, 0 );
4158 /*********************************************************************
4159 ********************************************************************/
4161 static void wcache_save_user_pwinfo(struct winbindd_domain
*domain
,
4163 const DOM_SID
*user_sid
,
4164 const char *homedir
,
4169 struct cache_entry
*centry
;
4172 if ( (centry
= centry_start(domain
, status
)) == NULL
)
4175 centry_put_string( centry
, homedir
);
4176 centry_put_string( centry
, shell
);
4177 centry_put_string( centry
, gecos
);
4178 centry_put_uint32( centry
, gid
);
4180 centry_end(centry
, "NSS/PWINFO/%s", sid_to_fstring(tmp
, user_sid
) );
4182 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid
) ));
4184 centry_free(centry
);
4187 NTSTATUS
nss_get_info_cached( struct winbindd_domain
*domain
,
4188 const DOM_SID
*user_sid
,
4190 ADS_STRUCT
*ads
, LDAPMessage
*msg
,
4191 char **homedir
, char **shell
, char **gecos
,
4194 struct winbind_cache
*cache
= get_cache(domain
);
4195 struct cache_entry
*centry
= NULL
;
4202 centry
= wcache_fetch(cache
, domain
, "NSS/PWINFO/%s",
4203 sid_to_fstring(tmp
, user_sid
));
4208 *homedir
= centry_string( centry
, ctx
);
4209 *shell
= centry_string( centry
, ctx
);
4210 *gecos
= centry_string( centry
, ctx
);
4211 *p_gid
= centry_uint32( centry
);
4213 centry_free(centry
);
4215 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4216 sid_string_dbg(user_sid
)));
4218 return NT_STATUS_OK
;
4222 nt_status
= nss_get_info( domain
->name
, user_sid
, ctx
, ads
, msg
,
4223 homedir
, shell
, gecos
, p_gid
);
4225 if ( NT_STATUS_IS_OK(nt_status
) ) {
4226 wcache_save_user_pwinfo( domain
, nt_status
, user_sid
,
4227 *homedir
, *shell
, *gecos
, *p_gid
);
4230 if ( NT_STATUS_EQUAL( nt_status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
4231 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4233 set_domain_offline( domain
);
4240 /* the cache backend methods are exposed via this structure */
4241 struct winbindd_methods cache_methods
= {