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
;
37 extern struct winbindd_methods ads_methods
;
39 extern struct winbindd_methods builtin_passdb_methods
;
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
47 static const char *non_centry_keys
[] = {
52 WINBINDD_CACHE_VERSION_KEYSTR
,
56 /************************************************************************
57 Is this key a non-centry type ?
58 ************************************************************************/
60 static bool is_non_centry_key(TDB_DATA kbuf
)
64 if (kbuf
.dptr
== NULL
|| kbuf
.dsize
== 0) {
67 for (i
= 0; non_centry_keys
[i
] != NULL
; i
++) {
68 size_t namelen
= strlen(non_centry_keys
[i
]);
69 if (kbuf
.dsize
< namelen
) {
72 if (strncmp(non_centry_keys
[i
], (const char *)kbuf
.dptr
, namelen
) == 0) {
79 /* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
83 static bool global_winbindd_offline_state
;
85 struct winbind_cache
{
91 uint32 sequence_number
;
96 void (*smb_panic_fn
)(const char *const why
) = smb_panic
;
98 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100 static struct winbind_cache
*wcache
;
102 void winbindd_check_cache_size(time_t t
)
104 static time_t last_check_time
;
107 if (last_check_time
== (time_t)0)
110 if (t
- last_check_time
< 60 && t
- last_check_time
> 0)
113 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
114 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
118 if (fstat(tdb_fd(wcache
->tdb
), &st
) == -1) {
119 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno
) ));
123 if (st
.st_size
> WINBINDD_MAX_CACHE_SIZE
) {
124 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
125 (unsigned long)st
.st_size
,
126 (unsigned long)WINBINDD_MAX_CACHE_SIZE
));
127 wcache_flush_cache();
131 /* get the winbind_cache structure */
132 static struct winbind_cache
*get_cache(struct winbindd_domain
*domain
)
134 struct winbind_cache
*ret
= wcache
;
136 /* We have to know what type of domain we are dealing with first. */
138 if (domain
->internal
) {
139 domain
->backend
= &builtin_passdb_methods
;
140 domain
->initialized
= True
;
142 if ( !domain
->initialized
) {
143 init_dc_connection( domain
);
147 OK. listen up becasue I'm only going to say this once.
148 We have the following scenarios to consider
149 (a) trusted AD domains on a Samba DC,
150 (b) trusted AD domains and we are joined to a non-kerberos domain
151 (c) trusted AD domains and we are joined to a kerberos (AD) domain
153 For (a) we can always contact the trusted domain using krb5
154 since we have the domain trust account password
156 For (b) we can only use RPC since we have no way of
157 getting a krb5 ticket in our own domain
159 For (c) we can always use krb5 since we have a kerberos trust
164 if (!domain
->backend
) {
166 struct winbindd_domain
*our_domain
= domain
;
168 /* find our domain first so we can figure out if we
169 are joined to a kerberized domain */
171 if ( !domain
->primary
)
172 our_domain
= find_our_domain();
174 if ((our_domain
->active_directory
|| IS_DC
)
175 && domain
->active_directory
176 && !lp_winbind_rpc_only()) {
177 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain
->name
));
178 domain
->backend
= &ads_methods
;
180 #endif /* HAVE_ADS */
181 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain
->name
));
182 domain
->backend
= &reconnect_methods
;
185 #endif /* HAVE_ADS */
191 ret
= SMB_XMALLOC_P(struct winbind_cache
);
195 wcache_flush_cache();
201 free a centry structure
203 static void centry_free(struct cache_entry
*centry
)
207 SAFE_FREE(centry
->data
);
211 static bool centry_check_bytes(struct cache_entry
*centry
, size_t nbytes
)
213 if (centry
->len
- centry
->ofs
< nbytes
) {
214 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
215 (unsigned int)nbytes
,
216 centry
->len
- centry
->ofs
));
223 pull a uint32 from a cache entry
225 static uint32
centry_uint32(struct cache_entry
*centry
)
229 if (!centry_check_bytes(centry
, 4)) {
230 smb_panic_fn("centry_uint32");
232 ret
= IVAL(centry
->data
, centry
->ofs
);
238 pull a uint16 from a cache entry
240 static uint16
centry_uint16(struct cache_entry
*centry
)
243 if (!centry_check_bytes(centry
, 2)) {
244 smb_panic_fn("centry_uint16");
246 ret
= CVAL(centry
->data
, centry
->ofs
);
252 pull a uint8 from a cache entry
254 static uint8
centry_uint8(struct cache_entry
*centry
)
257 if (!centry_check_bytes(centry
, 1)) {
258 smb_panic_fn("centry_uint8");
260 ret
= CVAL(centry
->data
, centry
->ofs
);
266 pull a NTTIME from a cache entry
268 static NTTIME
centry_nttime(struct cache_entry
*centry
)
271 if (!centry_check_bytes(centry
, 8)) {
272 smb_panic_fn("centry_nttime");
274 ret
= IVAL(centry
->data
, centry
->ofs
);
276 ret
+= (uint64_t)IVAL(centry
->data
, centry
->ofs
) << 32;
282 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 static time_t centry_time(struct cache_entry
*centry
)
286 return (time_t)centry_nttime(centry
);
289 /* pull a string from a cache entry, using the supplied
292 static char *centry_string(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
297 len
= centry_uint8(centry
);
300 /* a deliberate NULL string */
304 if (!centry_check_bytes(centry
, (size_t)len
)) {
305 smb_panic_fn("centry_string");
308 ret
= TALLOC_ARRAY(mem_ctx
, char, len
+1);
310 smb_panic_fn("centry_string out of memory\n");
312 memcpy(ret
,centry
->data
+ centry
->ofs
, len
);
318 /* pull a hash16 from a cache entry, using the supplied
321 static char *centry_hash16(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
326 len
= centry_uint8(centry
);
329 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
334 if (!centry_check_bytes(centry
, 16)) {
338 ret
= TALLOC_ARRAY(mem_ctx
, char, 16);
340 smb_panic_fn("centry_hash out of memory\n");
342 memcpy(ret
,centry
->data
+ centry
->ofs
, 16);
347 /* pull a sid from a cache entry, using the supplied
350 static bool centry_sid(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
, DOM_SID
*sid
)
353 sid_string
= centry_string(centry
, mem_ctx
);
354 if ((sid_string
== NULL
) || (!string_to_sid(sid
, sid_string
))) {
362 pull a NTSTATUS from a cache entry
364 static NTSTATUS
centry_ntstatus(struct cache_entry
*centry
)
368 status
= NT_STATUS(centry_uint32(centry
));
373 /* the server is considered down if it can't give us a sequence number */
374 static bool wcache_server_down(struct winbindd_domain
*domain
)
381 ret
= (domain
->sequence_number
== DOM_SEQUENCE_NONE
);
384 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
389 static NTSTATUS
fetch_cache_seqnum( struct winbindd_domain
*domain
, time_t now
)
396 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
397 return NT_STATUS_UNSUCCESSFUL
;
400 fstr_sprintf( key
, "SEQNUM/%s", domain
->name
);
402 data
= tdb_fetch_bystring( wcache
->tdb
, key
);
403 if ( !data
.dptr
|| data
.dsize
!=8 ) {
404 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key
));
405 return NT_STATUS_UNSUCCESSFUL
;
408 domain
->sequence_number
= IVAL(data
.dptr
, 0);
409 domain
->last_seq_check
= IVAL(data
.dptr
, 4);
411 SAFE_FREE(data
.dptr
);
413 /* have we expired? */
415 time_diff
= now
- domain
->last_seq_check
;
416 if ( time_diff
> lp_winbind_cache_time() ) {
417 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
418 domain
->name
, domain
->sequence_number
,
419 (uint32
)domain
->last_seq_check
));
420 return NT_STATUS_UNSUCCESSFUL
;
423 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
424 domain
->name
, domain
->sequence_number
,
425 (uint32
)domain
->last_seq_check
));
430 static NTSTATUS
store_cache_seqnum( struct winbindd_domain
*domain
)
437 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
438 return NT_STATUS_UNSUCCESSFUL
;
441 fstr_sprintf( key_str
, "SEQNUM/%s", domain
->name
);
443 SIVAL(buf
, 0, domain
->sequence_number
);
444 SIVAL(buf
, 4, domain
->last_seq_check
);
448 if ( tdb_store_bystring( wcache
->tdb
, key_str
, data
, TDB_REPLACE
) == -1 ) {
449 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str
));
450 return NT_STATUS_UNSUCCESSFUL
;
453 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
454 domain
->name
, domain
->sequence_number
,
455 (uint32
)domain
->last_seq_check
));
461 refresh the domain sequence number. If force is true
462 then always refresh it, no matter how recently we fetched it
465 static void refresh_sequence_number(struct winbindd_domain
*domain
, bool force
)
469 time_t t
= time(NULL
);
470 unsigned cache_time
= lp_winbind_cache_time();
472 if ( IS_DOMAIN_OFFLINE(domain
) ) {
478 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
479 /* trying to reconnect is expensive, don't do it too often */
480 if (domain
->sequence_number
== DOM_SEQUENCE_NONE
) {
485 time_diff
= t
- domain
->last_seq_check
;
487 /* see if we have to refetch the domain sequence number */
488 if (!force
&& (time_diff
< cache_time
) &&
489 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
490 NT_STATUS_IS_OK(domain
->last_status
)) {
491 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain
->name
));
495 /* try to get the sequence number from the tdb cache first */
496 /* this will update the timestamp as well */
498 status
= fetch_cache_seqnum( domain
, t
);
499 if (NT_STATUS_IS_OK(status
) &&
500 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
501 NT_STATUS_IS_OK(domain
->last_status
)) {
505 /* important! make sure that we know if this is a native
506 mode domain or not. And that we can contact it. */
508 if ( winbindd_can_contact_domain( domain
) ) {
509 status
= domain
->backend
->sequence_number(domain
,
510 &domain
->sequence_number
);
512 /* just use the current time */
513 status
= NT_STATUS_OK
;
514 domain
->sequence_number
= time(NULL
);
518 /* the above call could have set our domain->backend to NULL when
519 * coming from offline to online mode, make sure to reinitialize the
520 * backend - Guenther */
523 if (!NT_STATUS_IS_OK(status
)) {
524 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status
)));
525 domain
->sequence_number
= DOM_SEQUENCE_NONE
;
528 domain
->last_status
= status
;
529 domain
->last_seq_check
= time(NULL
);
531 /* save the new sequence number in the cache */
532 store_cache_seqnum( domain
);
535 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
536 domain
->name
, domain
->sequence_number
));
542 decide if a cache entry has expired
544 static bool centry_expired(struct winbindd_domain
*domain
, const char *keystr
, struct cache_entry
*centry
)
546 /* If we've been told to be offline - stay in that state... */
547 if (lp_winbind_offline_logon() && global_winbindd_offline_state
) {
548 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
549 keystr
, domain
->name
));
553 /* when the domain is offline return the cached entry.
554 * This deals with transient offline states... */
556 if (!domain
->online
) {
557 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
558 keystr
, domain
->name
));
562 /* if the server is OK and our cache entry came from when it was down then
563 the entry is invalid */
564 if ((domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
565 (centry
->sequence_number
== DOM_SEQUENCE_NONE
)) {
566 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
567 keystr
, domain
->name
));
571 /* if the server is down or the cache entry is not older than the
572 current sequence number then it is OK */
573 if (wcache_server_down(domain
) ||
574 centry
->sequence_number
== domain
->sequence_number
) {
575 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
576 keystr
, domain
->name
));
580 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
581 keystr
, domain
->name
));
587 static struct cache_entry
*wcache_fetch_raw(char *kstr
)
590 struct cache_entry
*centry
;
593 key
= string_tdb_data(kstr
);
594 data
= tdb_fetch(wcache
->tdb
, key
);
600 centry
= SMB_XMALLOC_P(struct cache_entry
);
601 centry
->data
= (unsigned char *)data
.dptr
;
602 centry
->len
= data
.dsize
;
605 if (centry
->len
< 8) {
606 /* huh? corrupt cache? */
607 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr
));
612 centry
->status
= centry_ntstatus(centry
);
613 centry
->sequence_number
= centry_uint32(centry
);
619 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
620 number and return status
622 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
623 struct winbindd_domain
*domain
,
624 const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
625 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
626 struct winbindd_domain
*domain
,
627 const char *format
, ...)
631 struct cache_entry
*centry
;
633 if (!winbindd_use_cache()) {
637 refresh_sequence_number(domain
, false);
639 va_start(ap
, format
);
640 smb_xvasprintf(&kstr
, format
, ap
);
643 centry
= wcache_fetch_raw(kstr
);
644 if (centry
== NULL
) {
649 if (centry_expired(domain
, kstr
, centry
)) {
651 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
652 kstr
, domain
->name
));
659 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
660 kstr
, domain
->name
));
666 static void wcache_delete(const char *format
, ...) PRINTF_ATTRIBUTE(1,2);
667 static void wcache_delete(const char *format
, ...)
673 va_start(ap
, format
);
674 smb_xvasprintf(&kstr
, format
, ap
);
677 key
= string_tdb_data(kstr
);
679 tdb_delete(wcache
->tdb
, key
);
684 make sure we have at least len bytes available in a centry
686 static void centry_expand(struct cache_entry
*centry
, uint32 len
)
688 if (centry
->len
- centry
->ofs
>= len
)
691 centry
->data
= SMB_REALLOC_ARRAY(centry
->data
, unsigned char,
694 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry
->len
));
695 smb_panic_fn("out of memory in centry_expand");
700 push a uint32 into a centry
702 static void centry_put_uint32(struct cache_entry
*centry
, uint32 v
)
704 centry_expand(centry
, 4);
705 SIVAL(centry
->data
, centry
->ofs
, v
);
710 push a uint16 into a centry
712 static void centry_put_uint16(struct cache_entry
*centry
, uint16 v
)
714 centry_expand(centry
, 2);
715 SIVAL(centry
->data
, centry
->ofs
, v
);
720 push a uint8 into a centry
722 static void centry_put_uint8(struct cache_entry
*centry
, uint8 v
)
724 centry_expand(centry
, 1);
725 SCVAL(centry
->data
, centry
->ofs
, v
);
730 push a string into a centry
732 static void centry_put_string(struct cache_entry
*centry
, const char *s
)
737 /* null strings are marked as len 0xFFFF */
738 centry_put_uint8(centry
, 0xFF);
743 /* can't handle more than 254 char strings. Truncating is probably best */
745 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len
));
748 centry_put_uint8(centry
, len
);
749 centry_expand(centry
, len
);
750 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
755 push a 16 byte hash into a centry - treat as 16 byte string.
757 static void centry_put_hash16(struct cache_entry
*centry
, const uint8 val
[16])
759 centry_put_uint8(centry
, 16);
760 centry_expand(centry
, 16);
761 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
765 static void centry_put_sid(struct cache_entry
*centry
, const DOM_SID
*sid
)
768 centry_put_string(centry
, sid_to_fstring(sid_string
, sid
));
773 put NTSTATUS into a centry
775 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
777 uint32 status_value
= NT_STATUS_V(status
);
778 centry_put_uint32(centry
, status_value
);
783 push a NTTIME into a centry
785 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
787 centry_expand(centry
, 8);
788 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
790 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
795 push a time_t into a centry - use a 64 bit size.
796 NTTIME here is being used as a convenient 64-bit size.
798 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
800 NTTIME nt
= (NTTIME
)t
;
801 centry_put_nttime(centry
, nt
);
805 start a centry for output. When finished, call centry_end()
807 struct cache_entry
*centry_start(struct winbindd_domain
*domain
, NTSTATUS status
)
809 struct cache_entry
*centry
;
814 centry
= SMB_XMALLOC_P(struct cache_entry
);
816 centry
->len
= 8192; /* reasonable default */
817 centry
->data
= SMB_XMALLOC_ARRAY(uint8
, centry
->len
);
819 centry
->sequence_number
= domain
->sequence_number
;
820 centry_put_ntstatus(centry
, status
);
821 centry_put_uint32(centry
, centry
->sequence_number
);
826 finish a centry and write it to the tdb
828 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
829 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
835 if (!winbindd_use_cache()) {
839 va_start(ap
, format
);
840 smb_xvasprintf(&kstr
, format
, ap
);
843 key
= string_tdb_data(kstr
);
844 data
.dptr
= centry
->data
;
845 data
.dsize
= centry
->ofs
;
847 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
851 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
852 NTSTATUS status
, const char *domain_name
,
853 const char *name
, const DOM_SID
*sid
,
854 enum lsa_SidType type
)
856 struct cache_entry
*centry
;
859 centry
= centry_start(domain
, status
);
862 centry_put_uint32(centry
, type
);
863 centry_put_sid(centry
, sid
);
864 fstrcpy(uname
, name
);
866 centry_end(centry
, "NS/%s/%s", domain_name
, uname
);
867 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name
,
868 uname
, sid_string_dbg(sid
), nt_errstr(status
)));
872 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
873 const DOM_SID
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
875 struct cache_entry
*centry
;
878 centry
= centry_start(domain
, status
);
882 if (NT_STATUS_IS_OK(status
)) {
883 centry_put_uint32(centry
, type
);
884 centry_put_string(centry
, domain_name
);
885 centry_put_string(centry
, name
);
888 centry_end(centry
, "SN/%s", sid_to_fstring(sid_string
, sid
));
889 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string
,
890 name
, nt_errstr(status
)));
895 static void wcache_save_user(struct winbindd_domain
*domain
, NTSTATUS status
, WINBIND_USERINFO
*info
)
897 struct cache_entry
*centry
;
900 if (is_null_sid(&info
->user_sid
)) {
904 centry
= centry_start(domain
, status
);
907 centry_put_string(centry
, info
->acct_name
);
908 centry_put_string(centry
, info
->full_name
);
909 centry_put_string(centry
, info
->homedir
);
910 centry_put_string(centry
, info
->shell
);
911 centry_put_uint32(centry
, info
->primary_gid
);
912 centry_put_sid(centry
, &info
->user_sid
);
913 centry_put_sid(centry
, &info
->group_sid
);
914 centry_end(centry
, "U/%s", sid_to_fstring(sid_string
,
916 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string
, info
->acct_name
));
920 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
922 struct samr_DomInfo12
*lockout_policy
)
924 struct cache_entry
*centry
;
926 centry
= centry_start(domain
, status
);
930 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
931 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
932 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
934 centry_end(centry
, "LOC_POL/%s", domain
->name
);
936 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain
->name
));
943 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
945 struct samr_DomInfo1
*policy
)
947 struct cache_entry
*centry
;
949 centry
= centry_start(domain
, status
);
953 centry_put_uint16(centry
, policy
->min_password_length
);
954 centry_put_uint16(centry
, policy
->password_history_length
);
955 centry_put_uint32(centry
, policy
->password_properties
);
956 centry_put_nttime(centry
, policy
->max_password_age
);
957 centry_put_nttime(centry
, policy
->min_password_age
);
959 centry_end(centry
, "PWD_POL/%s", domain
->name
);
961 DEBUG(10,("wcache_save_password_policy: %s\n", domain
->name
));
966 /***************************************************************************
967 ***************************************************************************/
969 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
971 const char *name
, const char *alias
)
973 struct cache_entry
*centry
;
976 if ( (centry
= centry_start(domain
, status
)) == NULL
)
979 centry_put_string( centry
, alias
);
981 fstrcpy(uname
, name
);
983 centry_end(centry
, "NSS/NA/%s", uname
);
985 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name
, alias
));
990 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
992 const char *alias
, const char *name
)
994 struct cache_entry
*centry
;
997 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1000 centry_put_string( centry
, name
);
1002 fstrcpy(uname
, alias
);
1004 centry_end(centry
, "NSS/AN/%s", uname
);
1006 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias
, name
));
1008 centry_free(centry
);
1011 /***************************************************************************
1012 ***************************************************************************/
1014 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1015 struct winbindd_domain
*domain
,
1016 const char *name
, char **alias
)
1018 struct winbind_cache
*cache
= get_cache(domain
);
1019 struct cache_entry
*centry
= NULL
;
1023 if ( domain
->internal
)
1024 return NT_STATUS_NOT_SUPPORTED
;
1029 if ( (upper_name
= SMB_STRDUP(name
)) == NULL
)
1030 return NT_STATUS_NO_MEMORY
;
1031 strupper_m(upper_name
);
1033 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1035 SAFE_FREE( upper_name
);
1040 status
= centry
->status
;
1042 if (!NT_STATUS_IS_OK(status
)) {
1043 centry_free(centry
);
1047 *alias
= centry_string( centry
, mem_ctx
);
1049 centry_free(centry
);
1051 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1052 name
, *alias
? *alias
: "(none)"));
1054 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1058 /* If its not in cache and we are offline, then fail */
1060 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1061 DEBUG(8,("resolve_username_to_alias: rejecting query "
1062 "in offline mode\n"));
1063 return NT_STATUS_NOT_FOUND
;
1066 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1068 if ( NT_STATUS_IS_OK( status
) ) {
1069 wcache_save_username_alias(domain
, status
, name
, *alias
);
1072 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1073 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1076 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1077 nt_errstr(status
)));
1079 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1080 set_domain_offline( domain
);
1086 /***************************************************************************
1087 ***************************************************************************/
1089 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1090 struct winbindd_domain
*domain
,
1091 const char *alias
, char **name
)
1093 struct winbind_cache
*cache
= get_cache(domain
);
1094 struct cache_entry
*centry
= NULL
;
1098 if ( domain
->internal
)
1099 return NT_STATUS_NOT_SUPPORTED
;
1104 if ( (upper_name
= SMB_STRDUP(alias
)) == NULL
)
1105 return NT_STATUS_NO_MEMORY
;
1106 strupper_m(upper_name
);
1108 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1110 SAFE_FREE( upper_name
);
1115 status
= centry
->status
;
1117 if (!NT_STATUS_IS_OK(status
)) {
1118 centry_free(centry
);
1122 *name
= centry_string( centry
, mem_ctx
);
1124 centry_free(centry
);
1126 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1127 alias
, *name
? *name
: "(none)"));
1129 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1133 /* If its not in cache and we are offline, then fail */
1135 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1136 DEBUG(8,("resolve_alias_to_username: rejecting query "
1137 "in offline mode\n"));
1138 return NT_STATUS_NOT_FOUND
;
1141 /* an alias cannot contain a domain prefix or '@' */
1143 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1144 DEBUG(10,("resolve_alias_to_username: skipping fully "
1145 "qualified name %s\n", alias
));
1146 return NT_STATUS_OBJECT_NAME_INVALID
;
1149 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1151 if ( NT_STATUS_IS_OK( status
) ) {
1152 wcache_save_alias_username( domain
, status
, alias
, *name
);
1155 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1156 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1159 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1160 nt_errstr(status
)));
1162 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1163 set_domain_offline( domain
);
1169 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const DOM_SID
*sid
)
1171 struct winbind_cache
*cache
= get_cache(domain
);
1173 fstring key_str
, tmp
;
1177 return NT_STATUS_INTERNAL_DB_ERROR
;
1180 if (is_null_sid(sid
)) {
1181 return NT_STATUS_INVALID_SID
;
1184 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1185 return NT_STATUS_INVALID_SID
;
1188 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
1190 data
= tdb_fetch(cache
->tdb
, string_tdb_data(key_str
));
1192 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1195 SAFE_FREE(data
.dptr
);
1196 return NT_STATUS_OK
;
1199 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1200 as new salted ones. */
1202 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1203 TALLOC_CTX
*mem_ctx
,
1205 const uint8
**cached_nt_pass
,
1206 const uint8
**cached_salt
)
1208 struct winbind_cache
*cache
= get_cache(domain
);
1209 struct cache_entry
*centry
= NULL
;
1216 return NT_STATUS_INTERNAL_DB_ERROR
;
1219 if (is_null_sid(sid
)) {
1220 return NT_STATUS_INVALID_SID
;
1223 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1224 return NT_STATUS_INVALID_SID
;
1227 /* Try and get a salted cred first. If we can't
1228 fall back to an unsalted cred. */
1230 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1231 sid_to_fstring(tmp
, sid
));
1233 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1234 sid_string_dbg(sid
)));
1235 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1238 t
= centry_time(centry
);
1240 /* In the salted case this isn't actually the nt_hash itself,
1241 but the MD5 of the salt + nt_hash. Let the caller
1242 sort this out. It can tell as we only return the cached_salt
1243 if we are returning a salted cred. */
1245 *cached_nt_pass
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1246 if (*cached_nt_pass
== NULL
) {
1249 sid_to_fstring(sidstr
, sid
);
1251 /* Bad (old) cred cache. Delete and pretend we
1253 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1255 wcache_delete("CRED/%s", sidstr
);
1256 centry_free(centry
);
1257 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1260 /* We only have 17 bytes more data in the salted cred case. */
1261 if (centry
->len
- centry
->ofs
== 17) {
1262 *cached_salt
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1264 *cached_salt
= NULL
;
1267 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1269 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1272 status
= centry
->status
;
1274 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1275 sid_string_dbg(sid
), nt_errstr(status
) ));
1277 centry_free(centry
);
1281 /* Store creds for a SID - only writes out new salted ones. */
1283 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1284 TALLOC_CTX
*mem_ctx
,
1286 const uint8 nt_pass
[NT_HASH_LEN
])
1288 struct cache_entry
*centry
;
1291 uint8 cred_salt
[NT_HASH_LEN
];
1292 uint8 salted_hash
[NT_HASH_LEN
];
1294 if (is_null_sid(sid
)) {
1295 return NT_STATUS_INVALID_SID
;
1298 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1299 return NT_STATUS_INVALID_SID
;
1302 centry
= centry_start(domain
, NT_STATUS_OK
);
1304 return NT_STATUS_INTERNAL_DB_ERROR
;
1307 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1309 centry_put_time(centry
, time(NULL
));
1311 /* Create a salt and then salt the hash. */
1312 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1313 E_md5hash(cred_salt
, nt_pass
, salted_hash
);
1315 centry_put_hash16(centry
, salted_hash
);
1316 centry_put_hash16(centry
, cred_salt
);
1317 centry_end(centry
, "CRED/%s", sid_to_fstring(sid_string
, sid
));
1319 DEBUG(10,("wcache_save_creds: %s\n", sid_string
));
1321 centry_free(centry
);
1323 return NT_STATUS_OK
;
1327 /* Query display info. This is the basic user list fn */
1328 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
1329 TALLOC_CTX
*mem_ctx
,
1330 uint32
*num_entries
,
1331 WINBIND_USERINFO
**info
)
1333 struct winbind_cache
*cache
= get_cache(domain
);
1334 struct cache_entry
*centry
= NULL
;
1336 unsigned int i
, retry
;
1341 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1345 *num_entries
= centry_uint32(centry
);
1347 if (*num_entries
== 0)
1350 (*info
) = TALLOC_ARRAY(mem_ctx
, WINBIND_USERINFO
, *num_entries
);
1352 smb_panic_fn("query_user_list out of memory");
1354 for (i
=0; i
<(*num_entries
); i
++) {
1355 (*info
)[i
].acct_name
= centry_string(centry
, mem_ctx
);
1356 (*info
)[i
].full_name
= centry_string(centry
, mem_ctx
);
1357 (*info
)[i
].homedir
= centry_string(centry
, mem_ctx
);
1358 (*info
)[i
].shell
= centry_string(centry
, mem_ctx
);
1359 centry_sid(centry
, mem_ctx
, &(*info
)[i
].user_sid
);
1360 centry_sid(centry
, mem_ctx
, &(*info
)[i
].group_sid
);
1364 status
= centry
->status
;
1366 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1367 domain
->name
, nt_errstr(status
) ));
1369 centry_free(centry
);
1376 /* Return status value returned by seq number check */
1378 if (!NT_STATUS_IS_OK(domain
->last_status
))
1379 return domain
->last_status
;
1381 /* Put the query_user_list() in a retry loop. There appears to be
1382 * some bug either with Windows 2000 or Samba's handling of large
1383 * rpc replies. This manifests itself as sudden disconnection
1384 * at a random point in the enumeration of a large (60k) user list.
1385 * The retry loop simply tries the operation again. )-: It's not
1386 * pretty but an acceptable workaround until we work out what the
1387 * real problem is. */
1392 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1395 status
= domain
->backend
->query_user_list(domain
, mem_ctx
, num_entries
, info
);
1396 if (!NT_STATUS_IS_OK(status
)) {
1397 DEBUG(3, ("query_user_list: returned 0x%08x, "
1398 "retrying\n", NT_STATUS_V(status
)));
1400 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1401 DEBUG(3, ("query_user_list: flushing "
1402 "connection cache\n"));
1403 invalidate_cm_connection(&domain
->conn
);
1406 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1410 refresh_sequence_number(domain
, false);
1411 centry
= centry_start(domain
, status
);
1414 centry_put_uint32(centry
, *num_entries
);
1415 for (i
=0; i
<(*num_entries
); i
++) {
1416 centry_put_string(centry
, (*info
)[i
].acct_name
);
1417 centry_put_string(centry
, (*info
)[i
].full_name
);
1418 centry_put_string(centry
, (*info
)[i
].homedir
);
1419 centry_put_string(centry
, (*info
)[i
].shell
);
1420 centry_put_sid(centry
, &(*info
)[i
].user_sid
);
1421 centry_put_sid(centry
, &(*info
)[i
].group_sid
);
1422 if (domain
->backend
&& domain
->backend
->consistent
) {
1423 /* when the backend is consistent we can pre-prime some mappings */
1424 wcache_save_name_to_sid(domain
, NT_STATUS_OK
,
1426 (*info
)[i
].acct_name
,
1427 &(*info
)[i
].user_sid
,
1429 wcache_save_sid_to_name(domain
, NT_STATUS_OK
,
1430 &(*info
)[i
].user_sid
,
1432 (*info
)[i
].acct_name
,
1434 wcache_save_user(domain
, NT_STATUS_OK
, &(*info
)[i
]);
1437 centry_end(centry
, "UL/%s", domain
->name
);
1438 centry_free(centry
);
1444 /* list all domain groups */
1445 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
1446 TALLOC_CTX
*mem_ctx
,
1447 uint32
*num_entries
,
1448 struct acct_info
**info
)
1450 struct winbind_cache
*cache
= get_cache(domain
);
1451 struct cache_entry
*centry
= NULL
;
1458 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1462 *num_entries
= centry_uint32(centry
);
1464 if (*num_entries
== 0)
1467 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1469 smb_panic_fn("enum_dom_groups out of memory");
1471 for (i
=0; i
<(*num_entries
); i
++) {
1472 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1473 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1474 (*info
)[i
].rid
= centry_uint32(centry
);
1478 status
= centry
->status
;
1480 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1481 domain
->name
, nt_errstr(status
) ));
1483 centry_free(centry
);
1490 /* Return status value returned by seq number check */
1492 if (!NT_STATUS_IS_OK(domain
->last_status
))
1493 return domain
->last_status
;
1495 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1498 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1501 refresh_sequence_number(domain
, false);
1502 centry
= centry_start(domain
, status
);
1505 centry_put_uint32(centry
, *num_entries
);
1506 for (i
=0; i
<(*num_entries
); i
++) {
1507 centry_put_string(centry
, (*info
)[i
].acct_name
);
1508 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1509 centry_put_uint32(centry
, (*info
)[i
].rid
);
1511 centry_end(centry
, "GL/%s/domain", domain
->name
);
1512 centry_free(centry
);
1518 /* list all domain groups */
1519 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
1520 TALLOC_CTX
*mem_ctx
,
1521 uint32
*num_entries
,
1522 struct acct_info
**info
)
1524 struct winbind_cache
*cache
= get_cache(domain
);
1525 struct cache_entry
*centry
= NULL
;
1532 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1536 *num_entries
= centry_uint32(centry
);
1538 if (*num_entries
== 0)
1541 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1543 smb_panic_fn("enum_dom_groups out of memory");
1545 for (i
=0; i
<(*num_entries
); i
++) {
1546 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1547 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1548 (*info
)[i
].rid
= centry_uint32(centry
);
1553 /* If we are returning cached data and the domain controller
1554 is down then we don't know whether the data is up to date
1555 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1558 if (wcache_server_down(domain
)) {
1559 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1560 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1562 status
= centry
->status
;
1564 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1565 domain
->name
, nt_errstr(status
) ));
1567 centry_free(centry
);
1574 /* Return status value returned by seq number check */
1576 if (!NT_STATUS_IS_OK(domain
->last_status
))
1577 return domain
->last_status
;
1579 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1582 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1585 refresh_sequence_number(domain
, false);
1586 centry
= centry_start(domain
, status
);
1589 centry_put_uint32(centry
, *num_entries
);
1590 for (i
=0; i
<(*num_entries
); i
++) {
1591 centry_put_string(centry
, (*info
)[i
].acct_name
);
1592 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1593 centry_put_uint32(centry
, (*info
)[i
].rid
);
1595 centry_end(centry
, "GL/%s/local", domain
->name
);
1596 centry_free(centry
);
1602 /* convert a single name to a sid in a domain */
1603 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
1604 TALLOC_CTX
*mem_ctx
,
1605 enum winbindd_cmd orig_cmd
,
1606 const char *domain_name
,
1609 enum lsa_SidType
*type
)
1611 struct winbind_cache
*cache
= get_cache(domain
);
1612 struct cache_entry
*centry
= NULL
;
1619 fstrcpy(uname
, name
);
1621 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
1625 status
= centry
->status
;
1626 if (NT_STATUS_IS_OK(status
)) {
1627 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1628 centry_sid(centry
, mem_ctx
, sid
);
1631 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1632 domain
->name
, nt_errstr(status
) ));
1634 centry_free(centry
);
1640 /* If the seq number check indicated that there is a problem
1641 * with this DC, then return that status... except for
1642 * access_denied. This is special because the dc may be in
1643 * "restrict anonymous = 1" mode, in which case it will deny
1644 * most unauthenticated operations, but *will* allow the LSA
1645 * name-to-sid that we try as a fallback. */
1647 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1648 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1649 return domain
->last_status
;
1651 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1654 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, orig_cmd
,
1655 domain_name
, name
, sid
, type
);
1658 refresh_sequence_number(domain
, false);
1660 if (domain
->online
&&
1661 (NT_STATUS_IS_OK(status
) || NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))) {
1662 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
, *type
);
1664 /* Only save the reverse mapping if this was not a UPN */
1665 if (!strchr(name
, '@')) {
1666 strupper_m(CONST_DISCARD(char *,domain_name
));
1667 strlower_m(CONST_DISCARD(char *,name
));
1668 wcache_save_sid_to_name(domain
, status
, sid
, domain_name
, name
, *type
);
1675 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1677 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
1678 TALLOC_CTX
*mem_ctx
,
1682 enum lsa_SidType
*type
)
1684 struct winbind_cache
*cache
= get_cache(domain
);
1685 struct cache_entry
*centry
= NULL
;
1692 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1693 sid_to_fstring(sid_string
, sid
));
1697 status
= centry
->status
;
1698 if (NT_STATUS_IS_OK(status
)) {
1699 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1700 *domain_name
= centry_string(centry
, mem_ctx
);
1701 *name
= centry_string(centry
, mem_ctx
);
1704 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1705 domain
->name
, nt_errstr(status
) ));
1707 centry_free(centry
);
1712 *domain_name
= NULL
;
1714 /* If the seq number check indicated that there is a problem
1715 * with this DC, then return that status... except for
1716 * access_denied. This is special because the dc may be in
1717 * "restrict anonymous = 1" mode, in which case it will deny
1718 * most unauthenticated operations, but *will* allow the LSA
1719 * sid-to-name that we try as a fallback. */
1721 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1722 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1723 return domain
->last_status
;
1725 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1728 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1731 refresh_sequence_number(domain
, false);
1732 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1734 /* We can't save the name to sid mapping here, as with sid history a
1735 * later name2sid would give the wrong sid. */
1740 static NTSTATUS
rids_to_names(struct winbindd_domain
*domain
,
1741 TALLOC_CTX
*mem_ctx
,
1742 const DOM_SID
*domain_sid
,
1747 enum lsa_SidType
**types
)
1749 struct winbind_cache
*cache
= get_cache(domain
);
1751 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1755 *domain_name
= NULL
;
1763 if (num_rids
== 0) {
1764 return NT_STATUS_OK
;
1767 *names
= TALLOC_ARRAY(mem_ctx
, char *, num_rids
);
1768 *types
= TALLOC_ARRAY(mem_ctx
, enum lsa_SidType
, num_rids
);
1770 if ((*names
== NULL
) || (*types
== NULL
)) {
1771 result
= NT_STATUS_NO_MEMORY
;
1775 have_mapped
= have_unmapped
= false;
1777 for (i
=0; i
<num_rids
; i
++) {
1779 struct cache_entry
*centry
;
1782 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1783 result
= NT_STATUS_INTERNAL_ERROR
;
1787 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1788 sid_to_fstring(tmp
, &sid
));
1793 (*types
)[i
] = SID_NAME_UNKNOWN
;
1794 (*names
)[i
] = talloc_strdup(*names
, "");
1796 if (NT_STATUS_IS_OK(centry
->status
)) {
1799 (*types
)[i
] = (enum lsa_SidType
)centry_uint32(centry
);
1801 dom
= centry_string(centry
, mem_ctx
);
1802 if (*domain_name
== NULL
) {
1808 (*names
)[i
] = centry_string(centry
, *names
);
1810 } else if (NT_STATUS_EQUAL(centry
->status
, NT_STATUS_NONE_MAPPED
)) {
1811 have_unmapped
= true;
1814 /* something's definitely wrong */
1815 result
= centry
->status
;
1819 centry_free(centry
);
1823 return NT_STATUS_NONE_MAPPED
;
1825 if (!have_unmapped
) {
1826 return NT_STATUS_OK
;
1828 return STATUS_SOME_UNMAPPED
;
1832 TALLOC_FREE(*names
);
1833 TALLOC_FREE(*types
);
1835 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
1836 rids
, num_rids
, domain_name
,
1840 None of the queried rids has been found so save all negative entries
1842 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
1843 for (i
= 0; i
< num_rids
; i
++) {
1845 const char *name
= "";
1846 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
1847 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
1849 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1850 return NT_STATUS_INTERNAL_ERROR
;
1853 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1861 Some or all of the queried rids have been found.
1863 if (!NT_STATUS_IS_OK(result
) &&
1864 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
1868 refresh_sequence_number(domain
, false);
1870 for (i
=0; i
<num_rids
; i
++) {
1874 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1875 result
= NT_STATUS_INTERNAL_ERROR
;
1879 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
1880 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
1882 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1883 (*names
)[i
], (*types
)[i
]);
1890 TALLOC_FREE(*names
);
1891 TALLOC_FREE(*types
);
1895 /* Lookup user information from a rid */
1896 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
1897 TALLOC_CTX
*mem_ctx
,
1898 const DOM_SID
*user_sid
,
1899 WINBIND_USERINFO
*info
)
1901 struct winbind_cache
*cache
= get_cache(domain
);
1902 struct cache_entry
*centry
= NULL
;
1909 centry
= wcache_fetch(cache
, domain
, "U/%s",
1910 sid_to_fstring(tmp
, user_sid
));
1912 /* If we have an access denied cache entry and a cached info3 in the
1913 samlogon cache then do a query. This will force the rpc back end
1914 to return the info3 data. */
1916 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1917 netsamlogon_cache_have(user_sid
)) {
1918 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1919 domain
->last_status
= NT_STATUS_OK
;
1920 centry_free(centry
);
1927 /* if status is not ok then this is a negative hit
1928 and the rest of the data doesn't matter */
1929 status
= centry
->status
;
1930 if (NT_STATUS_IS_OK(status
)) {
1931 info
->acct_name
= centry_string(centry
, mem_ctx
);
1932 info
->full_name
= centry_string(centry
, mem_ctx
);
1933 info
->homedir
= centry_string(centry
, mem_ctx
);
1934 info
->shell
= centry_string(centry
, mem_ctx
);
1935 info
->primary_gid
= centry_uint32(centry
);
1936 centry_sid(centry
, mem_ctx
, &info
->user_sid
);
1937 centry_sid(centry
, mem_ctx
, &info
->group_sid
);
1940 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1941 domain
->name
, nt_errstr(status
) ));
1943 centry_free(centry
);
1949 /* Return status value returned by seq number check */
1951 if (!NT_STATUS_IS_OK(domain
->last_status
))
1952 return domain
->last_status
;
1954 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1957 status
= domain
->backend
->query_user(domain
, mem_ctx
, user_sid
, info
);
1960 refresh_sequence_number(domain
, false);
1961 wcache_save_user(domain
, status
, info
);
1967 /* Lookup groups a user is a member of. */
1968 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
1969 TALLOC_CTX
*mem_ctx
,
1970 const DOM_SID
*user_sid
,
1971 uint32
*num_groups
, DOM_SID
**user_gids
)
1973 struct winbind_cache
*cache
= get_cache(domain
);
1974 struct cache_entry
*centry
= NULL
;
1982 centry
= wcache_fetch(cache
, domain
, "UG/%s",
1983 sid_to_fstring(sid_string
, user_sid
));
1985 /* If we have an access denied cache entry and a cached info3 in the
1986 samlogon cache then do a query. This will force the rpc back end
1987 to return the info3 data. */
1989 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1990 netsamlogon_cache_have(user_sid
)) {
1991 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1992 domain
->last_status
= NT_STATUS_OK
;
1993 centry_free(centry
);
2000 *num_groups
= centry_uint32(centry
);
2002 if (*num_groups
== 0)
2005 (*user_gids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_groups
);
2006 if (! (*user_gids
)) {
2007 smb_panic_fn("lookup_usergroups out of memory");
2009 for (i
=0; i
<(*num_groups
); i
++) {
2010 centry_sid(centry
, mem_ctx
, &(*user_gids
)[i
]);
2014 status
= centry
->status
;
2016 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2017 domain
->name
, nt_errstr(status
) ));
2019 centry_free(centry
);
2024 (*user_gids
) = NULL
;
2026 /* Return status value returned by seq number check */
2028 if (!NT_STATUS_IS_OK(domain
->last_status
))
2029 return domain
->last_status
;
2031 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2034 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2036 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2040 refresh_sequence_number(domain
, false);
2041 centry
= centry_start(domain
, status
);
2045 centry_put_uint32(centry
, *num_groups
);
2046 for (i
=0; i
<(*num_groups
); i
++) {
2047 centry_put_sid(centry
, &(*user_gids
)[i
]);
2050 centry_end(centry
, "UG/%s", sid_to_fstring(sid_string
, user_sid
));
2051 centry_free(centry
);
2057 static NTSTATUS
lookup_useraliases(struct winbindd_domain
*domain
,
2058 TALLOC_CTX
*mem_ctx
,
2059 uint32 num_sids
, const DOM_SID
*sids
,
2060 uint32
*num_aliases
, uint32
**alias_rids
)
2062 struct winbind_cache
*cache
= get_cache(domain
);
2063 struct cache_entry
*centry
= NULL
;
2065 char *sidlist
= talloc_strdup(mem_ctx
, "");
2071 if (num_sids
== 0) {
2074 return NT_STATUS_OK
;
2077 /* We need to cache indexed by the whole list of SIDs, the aliases
2078 * resulting might come from any of the SIDs. */
2080 for (i
=0; i
<num_sids
; i
++) {
2082 sidlist
= talloc_asprintf(mem_ctx
, "%s/%s", sidlist
,
2083 sid_to_fstring(tmp
, &sids
[i
]));
2084 if (sidlist
== NULL
)
2085 return NT_STATUS_NO_MEMORY
;
2088 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2093 *num_aliases
= centry_uint32(centry
);
2097 (*alias_rids
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_aliases
);
2099 if ((*alias_rids
) == NULL
) {
2100 centry_free(centry
);
2101 return NT_STATUS_NO_MEMORY
;
2104 (*alias_rids
) = NULL
;
2107 for (i
=0; i
<(*num_aliases
); i
++)
2108 (*alias_rids
)[i
] = centry_uint32(centry
);
2110 status
= centry
->status
;
2112 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2113 "status %s\n", domain
->name
, nt_errstr(status
)));
2115 centry_free(centry
);
2120 (*alias_rids
) = NULL
;
2122 if (!NT_STATUS_IS_OK(domain
->last_status
))
2123 return domain
->last_status
;
2125 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2126 "for domain %s\n", domain
->name
));
2128 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2130 num_aliases
, alias_rids
);
2133 refresh_sequence_number(domain
, false);
2134 centry
= centry_start(domain
, status
);
2137 centry_put_uint32(centry
, *num_aliases
);
2138 for (i
=0; i
<(*num_aliases
); i
++)
2139 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2140 centry_end(centry
, "UA%s", sidlist
);
2141 centry_free(centry
);
2148 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
2149 TALLOC_CTX
*mem_ctx
,
2150 const DOM_SID
*group_sid
, uint32
*num_names
,
2151 DOM_SID
**sid_mem
, char ***names
,
2152 uint32
**name_types
)
2154 struct winbind_cache
*cache
= get_cache(domain
);
2155 struct cache_entry
*centry
= NULL
;
2163 centry
= wcache_fetch(cache
, domain
, "GM/%s",
2164 sid_to_fstring(sid_string
, group_sid
));
2168 *num_names
= centry_uint32(centry
);
2170 if (*num_names
== 0)
2173 (*sid_mem
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_names
);
2174 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_names
);
2175 (*name_types
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_names
);
2177 if (! (*sid_mem
) || ! (*names
) || ! (*name_types
)) {
2178 smb_panic_fn("lookup_groupmem out of memory");
2181 for (i
=0; i
<(*num_names
); i
++) {
2182 centry_sid(centry
, mem_ctx
, &(*sid_mem
)[i
]);
2183 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2184 (*name_types
)[i
] = centry_uint32(centry
);
2188 status
= centry
->status
;
2190 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2191 domain
->name
, nt_errstr(status
)));
2193 centry_free(centry
);
2200 (*name_types
) = NULL
;
2202 /* Return status value returned by seq number check */
2204 if (!NT_STATUS_IS_OK(domain
->last_status
))
2205 return domain
->last_status
;
2207 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2210 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2211 sid_mem
, names
, name_types
);
2214 refresh_sequence_number(domain
, false);
2215 centry
= centry_start(domain
, status
);
2218 centry_put_uint32(centry
, *num_names
);
2219 for (i
=0; i
<(*num_names
); i
++) {
2220 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2221 centry_put_string(centry
, (*names
)[i
]);
2222 centry_put_uint32(centry
, (*name_types
)[i
]);
2224 centry_end(centry
, "GM/%s", sid_to_fstring(sid_string
, group_sid
));
2225 centry_free(centry
);
2231 /* find the sequence number for a domain */
2232 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
2234 refresh_sequence_number(domain
, false);
2236 *seq
= domain
->sequence_number
;
2238 return NT_STATUS_OK
;
2241 /* enumerate trusted domains
2242 * (we need to have the list of trustdoms in the cache when we go offline) -
2244 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
2245 TALLOC_CTX
*mem_ctx
,
2246 uint32
*num_domains
,
2251 struct winbind_cache
*cache
= get_cache(domain
);
2252 struct cache_entry
*centry
= NULL
;
2259 centry
= wcache_fetch(cache
, domain
, "TRUSTDOMS/%s", domain
->name
);
2265 *num_domains
= centry_uint32(centry
);
2268 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2269 (*alt_names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2270 (*dom_sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_domains
);
2272 if (! (*dom_sids
) || ! (*names
) || ! (*alt_names
)) {
2273 smb_panic_fn("trusted_domains out of memory");
2277 (*alt_names
) = NULL
;
2281 for (i
=0; i
<(*num_domains
); i
++) {
2282 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2283 (*alt_names
)[i
] = centry_string(centry
, mem_ctx
);
2284 if (!centry_sid(centry
, mem_ctx
, &(*dom_sids
)[i
])) {
2285 sid_copy(&(*dom_sids
)[i
], &global_sid_NULL
);
2289 status
= centry
->status
;
2291 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2292 domain
->name
, *num_domains
, nt_errstr(status
) ));
2294 centry_free(centry
);
2301 (*alt_names
) = NULL
;
2303 /* Return status value returned by seq number check */
2305 if (!NT_STATUS_IS_OK(domain
->last_status
))
2306 return domain
->last_status
;
2308 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2311 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, num_domains
,
2312 names
, alt_names
, dom_sids
);
2314 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2315 * so that the generic centry handling still applies correctly -
2318 if (!NT_STATUS_IS_ERR(status
)) {
2319 status
= NT_STATUS_OK
;
2323 #if 0 /* Disabled as we want the trust dom list to be managed by
2324 the main parent and always to make the query. --jerry */
2327 refresh_sequence_number(domain
, false);
2329 centry
= centry_start(domain
, status
);
2333 centry_put_uint32(centry
, *num_domains
);
2335 for (i
=0; i
<(*num_domains
); i
++) {
2336 centry_put_string(centry
, (*names
)[i
]);
2337 centry_put_string(centry
, (*alt_names
)[i
]);
2338 centry_put_sid(centry
, &(*dom_sids
)[i
]);
2341 centry_end(centry
, "TRUSTDOMS/%s", domain
->name
);
2343 centry_free(centry
);
2351 /* get lockout policy */
2352 static NTSTATUS
lockout_policy(struct winbindd_domain
*domain
,
2353 TALLOC_CTX
*mem_ctx
,
2354 struct samr_DomInfo12
*policy
)
2356 struct winbind_cache
*cache
= get_cache(domain
);
2357 struct cache_entry
*centry
= NULL
;
2363 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2368 policy
->lockout_duration
= centry_nttime(centry
);
2369 policy
->lockout_window
= centry_nttime(centry
);
2370 policy
->lockout_threshold
= centry_uint16(centry
);
2372 status
= centry
->status
;
2374 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2375 domain
->name
, nt_errstr(status
) ));
2377 centry_free(centry
);
2381 ZERO_STRUCTP(policy
);
2383 /* Return status value returned by seq number check */
2385 if (!NT_STATUS_IS_OK(domain
->last_status
))
2386 return domain
->last_status
;
2388 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2391 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2394 refresh_sequence_number(domain
, false);
2395 wcache_save_lockout_policy(domain
, status
, policy
);
2400 /* get password policy */
2401 static NTSTATUS
password_policy(struct winbindd_domain
*domain
,
2402 TALLOC_CTX
*mem_ctx
,
2403 struct samr_DomInfo1
*policy
)
2405 struct winbind_cache
*cache
= get_cache(domain
);
2406 struct cache_entry
*centry
= NULL
;
2412 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
2417 policy
->min_password_length
= centry_uint16(centry
);
2418 policy
->password_history_length
= centry_uint16(centry
);
2419 policy
->password_properties
= centry_uint32(centry
);
2420 policy
->max_password_age
= centry_nttime(centry
);
2421 policy
->min_password_age
= centry_nttime(centry
);
2423 status
= centry
->status
;
2425 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2426 domain
->name
, nt_errstr(status
) ));
2428 centry_free(centry
);
2432 ZERO_STRUCTP(policy
);
2434 /* Return status value returned by seq number check */
2436 if (!NT_STATUS_IS_OK(domain
->last_status
))
2437 return domain
->last_status
;
2439 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2442 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
2445 refresh_sequence_number(domain
, false);
2446 if (NT_STATUS_IS_OK(status
)) {
2447 wcache_save_password_policy(domain
, status
, policy
);
2454 /* Invalidate cached user and group lists coherently */
2456 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2459 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
2460 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
2461 tdb_delete(the_tdb
, kbuf
);
2466 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2468 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
2469 struct netr_SamInfo3
*info3
)
2472 fstring key_str
, sid_string
;
2473 struct winbind_cache
*cache
;
2475 /* dont clear cached U/SID and UG/SID entries when we want to logon
2478 if (lp_winbind_offline_logon()) {
2485 cache
= get_cache(domain
);
2491 sid_copy(&sid
, info3
->base
.domain_sid
);
2492 sid_append_rid(&sid
, info3
->base
.rid
);
2494 /* Clear U/SID cache entry */
2495 fstr_sprintf(key_str
, "U/%s", sid_to_fstring(sid_string
, &sid
));
2496 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2497 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2499 /* Clear UG/SID cache entry */
2500 fstr_sprintf(key_str
, "UG/%s", sid_to_fstring(sid_string
, &sid
));
2501 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2502 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2504 /* Samba/winbindd never needs this. */
2505 netsamlogon_clear_cached_user(info3
);
2508 bool wcache_invalidate_cache(void)
2510 struct winbindd_domain
*domain
;
2512 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
2513 struct winbind_cache
*cache
= get_cache(domain
);
2515 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2516 "entries for %s\n", domain
->name
));
2519 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
2528 bool init_wcache(void)
2530 if (wcache
== NULL
) {
2531 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
2532 ZERO_STRUCTP(wcache
);
2535 if (wcache
->tdb
!= NULL
)
2538 /* when working offline we must not clear the cache on restart */
2539 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"),
2540 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
2541 lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
2542 O_RDWR
|O_CREAT
, 0600);
2544 if (wcache
->tdb
== NULL
) {
2545 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2552 /************************************************************************
2553 This is called by the parent to initialize the cache file.
2554 We don't need sophisticated locking here as we know we're the
2556 ************************************************************************/
2558 bool initialize_winbindd_cache(void)
2560 bool cache_bad
= true;
2563 if (!init_wcache()) {
2564 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2568 /* Check version number. */
2569 if (tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
) &&
2570 vers
== WINBINDD_CACHE_VERSION
) {
2575 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2576 "and re-creating with version number %d\n",
2577 WINBINDD_CACHE_VERSION
));
2579 tdb_close(wcache
->tdb
);
2582 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2583 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2584 lock_path("winbindd_cache.tdb"),
2588 if (!init_wcache()) {
2589 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2590 "init_wcache failed.\n"));
2594 /* Write the version. */
2595 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
2596 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2597 tdb_errorstr(wcache
->tdb
) ));
2602 tdb_close(wcache
->tdb
);
2607 void close_winbindd_cache(void)
2613 tdb_close(wcache
->tdb
);
2618 void cache_store_response(pid_t pid
, struct winbindd_response
*response
)
2625 DEBUG(10, ("Storing response for pid %d, len %d\n",
2626 (int)pid
, response
->length
));
2628 fstr_sprintf(key_str
, "DR/%d", (int)pid
);
2629 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2630 make_tdb_data((uint8
*)response
, sizeof(*response
)),
2634 if (response
->length
== sizeof(*response
))
2637 /* There's extra data */
2639 DEBUG(10, ("Storing extra data: len=%d\n",
2640 (int)(response
->length
- sizeof(*response
))));
2642 fstr_sprintf(key_str
, "DE/%d", (int)pid
);
2643 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2644 make_tdb_data((uint8
*)response
->extra_data
.data
,
2645 response
->length
- sizeof(*response
)),
2649 /* We could not store the extra data, make sure the tdb does not
2650 * contain a main record with wrong dangling extra data */
2652 fstr_sprintf(key_str
, "DR/%d", (int)pid
);
2653 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2658 bool cache_retrieve_response(pid_t pid
, struct winbindd_response
* response
)
2666 DEBUG(10, ("Retrieving response for pid %d\n", (int)pid
));
2668 fstr_sprintf(key_str
, "DR/%d", (int)pid
);
2669 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2671 if (data
.dptr
== NULL
)
2674 if (data
.dsize
!= sizeof(*response
))
2677 memcpy(response
, data
.dptr
, data
.dsize
);
2678 SAFE_FREE(data
.dptr
);
2680 if (response
->length
== sizeof(*response
)) {
2681 response
->extra_data
.data
= NULL
;
2685 /* There's extra data */
2687 DEBUG(10, ("Retrieving extra data length=%d\n",
2688 (int)(response
->length
- sizeof(*response
))));
2690 fstr_sprintf(key_str
, "DE/%d", (int)pid
);
2691 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2693 if (data
.dptr
== NULL
) {
2694 DEBUG(0, ("Did not find extra data\n"));
2698 if (data
.dsize
!= (response
->length
- sizeof(*response
))) {
2699 DEBUG(0, ("Invalid extra data length: %d\n", (int)data
.dsize
));
2700 SAFE_FREE(data
.dptr
);
2704 dump_data(11, (uint8
*)data
.dptr
, data
.dsize
);
2706 response
->extra_data
.data
= data
.dptr
;
2710 void cache_cleanup_response(pid_t pid
)
2717 fstr_sprintf(key_str
, "DR/%d", (int)pid
);
2718 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2720 fstr_sprintf(key_str
, "DE/%d", (int)pid
);
2721 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2727 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
2728 char **domain_name
, char **name
,
2729 enum lsa_SidType
*type
)
2731 struct winbindd_domain
*domain
;
2732 struct winbind_cache
*cache
;
2733 struct cache_entry
*centry
= NULL
;
2737 domain
= find_lookup_domain_from_sid(sid
);
2738 if (domain
== NULL
) {
2742 cache
= get_cache(domain
);
2744 if (cache
->tdb
== NULL
) {
2748 centry
= wcache_fetch(cache
, domain
, "SN/%s",
2749 sid_to_fstring(tmp
, sid
));
2750 if (centry
== NULL
) {
2754 if (NT_STATUS_IS_OK(centry
->status
)) {
2755 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2756 *domain_name
= centry_string(centry
, mem_ctx
);
2757 *name
= centry_string(centry
, mem_ctx
);
2760 status
= centry
->status
;
2761 centry_free(centry
);
2762 return NT_STATUS_IS_OK(status
);
2765 bool lookup_cached_name(TALLOC_CTX
*mem_ctx
,
2766 const char *domain_name
,
2769 enum lsa_SidType
*type
)
2771 struct winbindd_domain
*domain
;
2772 struct winbind_cache
*cache
;
2773 struct cache_entry
*centry
= NULL
;
2776 bool original_online_state
;
2778 domain
= find_lookup_domain_from_name(domain_name
);
2779 if (domain
== NULL
) {
2783 cache
= get_cache(domain
);
2785 if (cache
->tdb
== NULL
) {
2789 fstrcpy(uname
, name
);
2792 /* If we are doing a cached logon, temporarily set the domain
2793 offline so the cache won't expire the entry */
2795 original_online_state
= domain
->online
;
2796 domain
->online
= false;
2797 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
2798 domain
->online
= original_online_state
;
2800 if (centry
== NULL
) {
2804 if (NT_STATUS_IS_OK(centry
->status
)) {
2805 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2806 centry_sid(centry
, mem_ctx
, sid
);
2809 status
= centry
->status
;
2810 centry_free(centry
);
2812 return NT_STATUS_IS_OK(status
);
2815 void cache_name2sid(struct winbindd_domain
*domain
,
2816 const char *domain_name
, const char *name
,
2817 enum lsa_SidType type
, const DOM_SID
*sid
)
2819 refresh_sequence_number(domain
, false);
2820 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
2825 * The original idea that this cache only contains centries has
2826 * been blurred - now other stuff gets put in here. Ensure we
2827 * ignore these things on cleanup.
2830 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
2831 TDB_DATA dbuf
, void *state
)
2833 struct cache_entry
*centry
;
2835 if (is_non_centry_key(kbuf
)) {
2839 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
2844 if (!NT_STATUS_IS_OK(centry
->status
)) {
2845 DEBUG(10,("deleting centry %s\n", (const char *)kbuf
.dptr
));
2846 tdb_delete(the_tdb
, kbuf
);
2849 centry_free(centry
);
2853 /* flush the cache */
2854 void wcache_flush_cache(void)
2859 tdb_close(wcache
->tdb
);
2862 if (!winbindd_use_cache()) {
2866 /* when working offline we must not clear the cache on restart */
2867 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"),
2868 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
2869 lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
2870 O_RDWR
|O_CREAT
, 0600);
2873 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2877 tdb_traverse(wcache
->tdb
, traverse_fn_cleanup
, NULL
);
2879 DEBUG(10,("wcache_flush_cache success\n"));
2882 /* Count cached creds */
2884 static int traverse_fn_cached_creds(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2887 int *cred_count
= (int*)state
;
2889 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
2895 NTSTATUS
wcache_count_cached_creds(struct winbindd_domain
*domain
, int *count
)
2897 struct winbind_cache
*cache
= get_cache(domain
);
2902 return NT_STATUS_INTERNAL_DB_ERROR
;
2905 tdb_traverse(cache
->tdb
, traverse_fn_cached_creds
, (void *)count
);
2907 return NT_STATUS_OK
;
2911 struct cred_list
*prev
, *next
;
2916 static struct cred_list
*wcache_cred_list
;
2918 static int traverse_fn_get_credlist(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2921 struct cred_list
*cred
;
2923 if (strncmp((const char *)kbuf
.dptr
, "CRED/", 5) == 0) {
2925 cred
= SMB_MALLOC_P(struct cred_list
);
2927 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2933 /* save a copy of the key */
2935 fstrcpy(cred
->name
, (const char *)kbuf
.dptr
);
2936 DLIST_ADD(wcache_cred_list
, cred
);
2942 NTSTATUS
wcache_remove_oldest_cached_creds(struct winbindd_domain
*domain
, const DOM_SID
*sid
)
2944 struct winbind_cache
*cache
= get_cache(domain
);
2947 struct cred_list
*cred
, *oldest
= NULL
;
2950 return NT_STATUS_INTERNAL_DB_ERROR
;
2953 /* we possibly already have an entry */
2954 if (sid
&& NT_STATUS_IS_OK(wcache_cached_creds_exist(domain
, sid
))) {
2956 fstring key_str
, tmp
;
2958 DEBUG(11,("we already have an entry, deleting that\n"));
2960 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
2962 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2964 return NT_STATUS_OK
;
2967 ret
= tdb_traverse(cache
->tdb
, traverse_fn_get_credlist
, NULL
);
2969 return NT_STATUS_OK
;
2970 } else if ((ret
== -1) || (wcache_cred_list
== NULL
)) {
2971 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
2974 ZERO_STRUCTP(oldest
);
2976 for (cred
= wcache_cred_list
; cred
; cred
= cred
->next
) {
2981 data
= tdb_fetch(cache
->tdb
, string_tdb_data(cred
->name
));
2983 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2985 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
2989 t
= IVAL(data
.dptr
, 0);
2990 SAFE_FREE(data
.dptr
);
2993 oldest
= SMB_MALLOC_P(struct cred_list
);
2994 if (oldest
== NULL
) {
2995 status
= NT_STATUS_NO_MEMORY
;
2999 fstrcpy(oldest
->name
, cred
->name
);
3000 oldest
->created
= t
;
3004 if (t
< oldest
->created
) {
3005 fstrcpy(oldest
->name
, cred
->name
);
3006 oldest
->created
= t
;
3010 if (tdb_delete(cache
->tdb
, string_tdb_data(oldest
->name
)) == 0) {
3011 status
= NT_STATUS_OK
;
3013 status
= NT_STATUS_UNSUCCESSFUL
;
3016 SAFE_FREE(wcache_cred_list
);
3022 /* Change the global online/offline state. */
3023 bool set_global_winbindd_state_offline(void)
3027 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3029 /* Only go offline if someone has created
3030 the key "WINBINDD_OFFLINE" in the cache tdb. */
3032 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3033 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3037 if (!lp_winbind_offline_logon()) {
3038 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3042 if (global_winbindd_offline_state
) {
3043 /* Already offline. */
3047 data
= tdb_fetch_bystring( wcache
->tdb
, "WINBINDD_OFFLINE" );
3049 if (!data
.dptr
|| data
.dsize
!= 4) {
3050 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3051 SAFE_FREE(data
.dptr
);
3054 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3055 global_winbindd_offline_state
= true;
3056 SAFE_FREE(data
.dptr
);
3061 void set_global_winbindd_state_online(void)
3063 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3065 if (!lp_winbind_offline_logon()) {
3066 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3070 if (!global_winbindd_offline_state
) {
3071 /* Already online. */
3074 global_winbindd_offline_state
= false;
3080 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3081 tdb_delete_bystring(wcache
->tdb
, "WINBINDD_OFFLINE");
3084 bool get_global_winbindd_state_offline(void)
3086 return global_winbindd_offline_state
;
3089 /***********************************************************************
3090 Validate functions for all possible cache tdb keys.
3091 ***********************************************************************/
3093 static struct cache_entry
*create_centry_validate(const char *kstr
, TDB_DATA data
,
3094 struct tdb_validation_status
*state
)
3096 struct cache_entry
*centry
;
3098 centry
= SMB_XMALLOC_P(struct cache_entry
);
3099 centry
->data
= (unsigned char *)memdup(data
.dptr
, data
.dsize
);
3100 if (!centry
->data
) {
3104 centry
->len
= data
.dsize
;
3107 if (centry
->len
< 8) {
3108 /* huh? corrupt cache? */
3109 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr
));
3110 centry_free(centry
);
3111 state
->bad_entry
= true;
3112 state
->success
= false;
3116 centry
->status
= NT_STATUS(centry_uint32(centry
));
3117 centry
->sequence_number
= centry_uint32(centry
);
3121 static int validate_seqnum(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3122 struct tdb_validation_status
*state
)
3124 if (dbuf
.dsize
!= 8) {
3125 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3126 keystr
, (unsigned int)dbuf
.dsize
));
3127 state
->bad_entry
= true;
3133 static int validate_ns(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3134 struct tdb_validation_status
*state
)
3136 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3141 (void)centry_uint32(centry
);
3142 if (NT_STATUS_IS_OK(centry
->status
)) {
3144 (void)centry_sid(centry
, mem_ctx
, &sid
);
3147 centry_free(centry
);
3149 if (!(state
->success
)) {
3152 DEBUG(10,("validate_ns: %s ok\n", keystr
));
3156 static int validate_sn(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3157 struct tdb_validation_status
*state
)
3159 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3164 if (NT_STATUS_IS_OK(centry
->status
)) {
3165 (void)centry_uint32(centry
);
3166 (void)centry_string(centry
, mem_ctx
);
3167 (void)centry_string(centry
, mem_ctx
);
3170 centry_free(centry
);
3172 if (!(state
->success
)) {
3175 DEBUG(10,("validate_sn: %s ok\n", keystr
));
3179 static int validate_u(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3180 struct tdb_validation_status
*state
)
3182 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3189 (void)centry_string(centry
, mem_ctx
);
3190 (void)centry_string(centry
, mem_ctx
);
3191 (void)centry_string(centry
, mem_ctx
);
3192 (void)centry_string(centry
, mem_ctx
);
3193 (void)centry_uint32(centry
);
3194 (void)centry_sid(centry
, mem_ctx
, &sid
);
3195 (void)centry_sid(centry
, mem_ctx
, &sid
);
3197 centry_free(centry
);
3199 if (!(state
->success
)) {
3202 DEBUG(10,("validate_u: %s ok\n", keystr
));
3206 static int validate_loc_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3207 struct tdb_validation_status
*state
)
3209 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3215 (void)centry_nttime(centry
);
3216 (void)centry_nttime(centry
);
3217 (void)centry_uint16(centry
);
3219 centry_free(centry
);
3221 if (!(state
->success
)) {
3224 DEBUG(10,("validate_loc_pol: %s ok\n", keystr
));
3228 static int validate_pwd_pol(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3229 struct tdb_validation_status
*state
)
3231 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3237 (void)centry_uint16(centry
);
3238 (void)centry_uint16(centry
);
3239 (void)centry_uint32(centry
);
3240 (void)centry_nttime(centry
);
3241 (void)centry_nttime(centry
);
3243 centry_free(centry
);
3245 if (!(state
->success
)) {
3248 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr
));
3252 static int validate_cred(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3253 struct tdb_validation_status
*state
)
3255 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3261 (void)centry_time(centry
);
3262 (void)centry_hash16(centry
, mem_ctx
);
3264 /* We only have 17 bytes more data in the salted cred case. */
3265 if (centry
->len
- centry
->ofs
== 17) {
3266 (void)centry_hash16(centry
, mem_ctx
);
3269 centry_free(centry
);
3271 if (!(state
->success
)) {
3274 DEBUG(10,("validate_cred: %s ok\n", keystr
));
3278 static int validate_ul(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3279 struct tdb_validation_status
*state
)
3281 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3282 int32 num_entries
, i
;
3288 num_entries
= (int32
)centry_uint32(centry
);
3290 for (i
=0; i
< num_entries
; i
++) {
3292 (void)centry_string(centry
, mem_ctx
);
3293 (void)centry_string(centry
, mem_ctx
);
3294 (void)centry_string(centry
, mem_ctx
);
3295 (void)centry_string(centry
, mem_ctx
);
3296 (void)centry_sid(centry
, mem_ctx
, &sid
);
3297 (void)centry_sid(centry
, mem_ctx
, &sid
);
3300 centry_free(centry
);
3302 if (!(state
->success
)) {
3305 DEBUG(10,("validate_ul: %s ok\n", keystr
));
3309 static int validate_gl(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3310 struct tdb_validation_status
*state
)
3312 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3313 int32 num_entries
, i
;
3319 num_entries
= centry_uint32(centry
);
3321 for (i
=0; i
< num_entries
; i
++) {
3322 (void)centry_string(centry
, mem_ctx
);
3323 (void)centry_string(centry
, mem_ctx
);
3324 (void)centry_uint32(centry
);
3327 centry_free(centry
);
3329 if (!(state
->success
)) {
3332 DEBUG(10,("validate_gl: %s ok\n", keystr
));
3336 static int validate_ug(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3337 struct tdb_validation_status
*state
)
3339 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3340 int32 num_groups
, i
;
3346 num_groups
= centry_uint32(centry
);
3348 for (i
=0; i
< num_groups
; i
++) {
3350 centry_sid(centry
, mem_ctx
, &sid
);
3353 centry_free(centry
);
3355 if (!(state
->success
)) {
3358 DEBUG(10,("validate_ug: %s ok\n", keystr
));
3362 static int validate_ua(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3363 struct tdb_validation_status
*state
)
3365 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3366 int32 num_aliases
, i
;
3372 num_aliases
= centry_uint32(centry
);
3374 for (i
=0; i
< num_aliases
; i
++) {
3375 (void)centry_uint32(centry
);
3378 centry_free(centry
);
3380 if (!(state
->success
)) {
3383 DEBUG(10,("validate_ua: %s ok\n", keystr
));
3387 static int validate_gm(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3388 struct tdb_validation_status
*state
)
3390 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3397 num_names
= centry_uint32(centry
);
3399 for (i
=0; i
< num_names
; i
++) {
3401 centry_sid(centry
, mem_ctx
, &sid
);
3402 (void)centry_string(centry
, mem_ctx
);
3403 (void)centry_uint32(centry
);
3406 centry_free(centry
);
3408 if (!(state
->success
)) {
3411 DEBUG(10,("validate_gm: %s ok\n", keystr
));
3415 static int validate_dr(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3416 struct tdb_validation_status
*state
)
3418 /* Can't say anything about this other than must be nonzero. */
3419 if (dbuf
.dsize
== 0) {
3420 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3422 state
->bad_entry
= true;
3423 state
->success
= false;
3427 DEBUG(10,("validate_dr: %s ok\n", keystr
));
3431 static int validate_de(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3432 struct tdb_validation_status
*state
)
3434 /* Can't say anything about this other than must be nonzero. */
3435 if (dbuf
.dsize
== 0) {
3436 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3438 state
->bad_entry
= true;
3439 state
->success
= false;
3443 DEBUG(10,("validate_de: %s ok\n", keystr
));
3447 static int validate_pwinfo(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3448 TDB_DATA dbuf
, struct tdb_validation_status
*state
)
3450 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3456 (void)centry_string(centry
, mem_ctx
);
3457 (void)centry_string(centry
, mem_ctx
);
3458 (void)centry_string(centry
, mem_ctx
);
3459 (void)centry_uint32(centry
);
3461 centry_free(centry
);
3463 if (!(state
->success
)) {
3466 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3470 static int validate_nss_an(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3472 struct tdb_validation_status
*state
)
3474 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3480 (void)centry_string( centry
, mem_ctx
);
3482 centry_free(centry
);
3484 if (!(state
->success
)) {
3487 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3491 static int validate_nss_na(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3493 struct tdb_validation_status
*state
)
3495 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3501 (void)centry_string( centry
, mem_ctx
);
3503 centry_free(centry
);
3505 if (!(state
->success
)) {
3508 DEBUG(10,("validate_pwinfo: %s ok\n", keystr
));
3512 static int validate_trustdoms(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3513 struct tdb_validation_status
*state
)
3515 struct cache_entry
*centry
= create_centry_validate(keystr
, dbuf
, state
);
3516 int32 num_domains
, i
;
3522 num_domains
= centry_uint32(centry
);
3524 for (i
=0; i
< num_domains
; i
++) {
3526 (void)centry_string(centry
, mem_ctx
);
3527 (void)centry_string(centry
, mem_ctx
);
3528 (void)centry_sid(centry
, mem_ctx
, &sid
);
3531 centry_free(centry
);
3533 if (!(state
->success
)) {
3536 DEBUG(10,("validate_trustdoms: %s ok\n", keystr
));
3540 static int validate_trustdomcache(TALLOC_CTX
*mem_ctx
, const char *keystr
,
3542 struct tdb_validation_status
*state
)
3544 if (dbuf
.dsize
== 0) {
3545 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3546 "key %s (len ==0) ?\n", keystr
));
3547 state
->bad_entry
= true;
3548 state
->success
= false;
3552 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr
));
3553 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3557 static int validate_offline(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3558 struct tdb_validation_status
*state
)
3560 if (dbuf
.dsize
!= 4) {
3561 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3562 keystr
, (unsigned int)dbuf
.dsize
));
3563 state
->bad_entry
= true;
3564 state
->success
= false;
3567 DEBUG(10,("validate_offline: %s ok\n", keystr
));
3571 static int validate_cache_version(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
,
3572 struct tdb_validation_status
*state
)
3574 if (dbuf
.dsize
!= 4) {
3575 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3576 "key %s (len %u != 4) ?\n",
3577 keystr
, (unsigned int)dbuf
.dsize
));
3578 state
->bad_entry
= true;
3579 state
->success
= false;
3583 DEBUG(10, ("validate_cache_version: %s ok\n", keystr
));
3587 /***********************************************************************
3588 A list of all possible cache tdb keys with associated validation
3590 ***********************************************************************/
3592 struct key_val_struct
{
3593 const char *keyname
;
3594 int (*validate_data_fn
)(TALLOC_CTX
*mem_ctx
, const char *keystr
, TDB_DATA dbuf
, struct tdb_validation_status
* state
);
3596 {"SEQNUM/", validate_seqnum
},
3597 {"NS/", validate_ns
},
3598 {"SN/", validate_sn
},
3600 {"LOC_POL/", validate_loc_pol
},
3601 {"PWD_POL/", validate_pwd_pol
},
3602 {"CRED/", validate_cred
},
3603 {"UL/", validate_ul
},
3604 {"GL/", validate_gl
},
3605 {"UG/", validate_ug
},
3606 {"UA", validate_ua
},
3607 {"GM/", validate_gm
},
3608 {"DR/", validate_dr
},
3609 {"DE/", validate_de
},
3610 {"NSS/PWINFO/", validate_pwinfo
},
3611 {"TRUSTDOMS/", validate_trustdoms
},
3612 {"TRUSTDOMCACHE/", validate_trustdomcache
},
3613 {"NSS/NA/", validate_nss_na
},
3614 {"NSS/AN/", validate_nss_an
},
3615 {"WINBINDD_OFFLINE", validate_offline
},
3616 {WINBINDD_CACHE_VERSION_KEYSTR
, validate_cache_version
},
3620 /***********************************************************************
3621 Function to look at every entry in the tdb and validate it as far as
3623 ***********************************************************************/
3625 static int cache_traverse_validate_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
3628 unsigned int max_key_len
= 1024;
3629 struct tdb_validation_status
*v_state
= (struct tdb_validation_status
*)state
;
3631 /* Paranoia check. */
3632 if (strncmp("UA/", (const char *)kbuf
.dptr
, 3) == 0) {
3633 max_key_len
= 1024 * 1024;
3635 if (kbuf
.dsize
> max_key_len
) {
3636 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3638 (unsigned int)kbuf
.dsize
, (unsigned int)max_key_len
));
3642 for (i
= 0; key_val
[i
].keyname
; i
++) {
3643 size_t namelen
= strlen(key_val
[i
].keyname
);
3644 if (kbuf
.dsize
>= namelen
&& (
3645 strncmp(key_val
[i
].keyname
, (const char *)kbuf
.dptr
, namelen
)) == 0) {
3646 TALLOC_CTX
*mem_ctx
;
3650 keystr
= SMB_MALLOC_ARRAY(char, kbuf
.dsize
+1);
3654 memcpy(keystr
, kbuf
.dptr
, kbuf
.dsize
);
3655 keystr
[kbuf
.dsize
] = '\0';
3657 mem_ctx
= talloc_init("validate_ctx");
3663 ret
= key_val
[i
].validate_data_fn(mem_ctx
, keystr
, dbuf
,
3667 talloc_destroy(mem_ctx
);
3672 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3673 dump_data(0, (uint8
*)kbuf
.dptr
, kbuf
.dsize
);
3674 DEBUG(0,("data :\n"));
3675 dump_data(0, (uint8
*)dbuf
.dptr
, dbuf
.dsize
);
3676 v_state
->unknown_key
= true;
3677 v_state
->success
= false;
3678 return 1; /* terminate. */
3681 static void validate_panic(const char *const why
)
3683 DEBUG(0,("validating cache: would panic %s\n", why
));
3684 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3688 /***********************************************************************
3689 Try and validate every entry in the winbindd cache. If we fail here,
3690 delete the cache tdb and return non-zero.
3691 ***********************************************************************/
3693 int winbindd_validate_cache(void)
3696 const char *tdb_path
= lock_path("winbindd_cache.tdb");
3697 TDB_CONTEXT
*tdb
= NULL
;
3699 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3700 smb_panic_fn
= validate_panic
;
3703 tdb
= tdb_open_log(tdb_path
,
3704 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
3705 ( lp_winbind_offline_logon()
3707 : TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
3711 DEBUG(0, ("winbindd_validate_cache: "
3712 "error opening/initializing tdb\n"));
3717 ret
= tdb_validate_and_backup(tdb_path
, cache_traverse_validate_fn
);
3720 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3721 DEBUGADD(10, ("removing tdb %s.\n", tdb_path
));
3726 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3727 smb_panic_fn
= smb_panic
;
3731 /***********************************************************************
3732 Try and validate every entry in the winbindd cache.
3733 ***********************************************************************/
3735 int winbindd_validate_cache_nobackup(void)
3738 const char *tdb_path
= lock_path("winbindd_cache.tdb");
3740 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3741 smb_panic_fn
= validate_panic
;
3744 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
3745 ret
= tdb_validate_open(tdb_path
, cache_traverse_validate_fn
);
3747 ret
= tdb_validate(wcache
->tdb
, cache_traverse_validate_fn
);
3751 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3755 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3757 smb_panic_fn
= smb_panic
;
3761 bool winbindd_cache_validate_and_initialize(void)
3763 close_winbindd_cache();
3765 if (lp_winbind_offline_logon()) {
3766 if (winbindd_validate_cache() < 0) {
3767 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3768 "could be restored.\n"));
3772 return initialize_winbindd_cache();
3775 /*********************************************************************
3776 ********************************************************************/
3778 static bool add_wbdomain_to_tdc_array( struct winbindd_domain
*new_dom
,
3779 struct winbindd_tdc_domain
**domains
,
3780 size_t *num_domains
)
3782 struct winbindd_tdc_domain
*list
= NULL
;
3785 bool set_only
= false;
3787 /* don't allow duplicates */
3792 for ( i
=0; i
< (*num_domains
); i
++ ) {
3793 if ( strequal( new_dom
->name
, list
[i
].domain_name
) ) {
3794 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3805 list
= TALLOC_ARRAY( NULL
, struct winbindd_tdc_domain
, 1 );
3808 list
= TALLOC_REALLOC_ARRAY( *domains
, *domains
,
3809 struct winbindd_tdc_domain
,
3814 ZERO_STRUCT( list
[idx
] );
3820 list
[idx
].domain_name
= talloc_strdup( list
, new_dom
->name
);
3821 list
[idx
].dns_name
= talloc_strdup( list
, new_dom
->alt_name
);
3823 if ( !is_null_sid( &new_dom
->sid
) ) {
3824 sid_copy( &list
[idx
].sid
, &new_dom
->sid
);
3826 sid_copy(&list
[idx
].sid
, &global_sid_NULL
);
3829 if ( new_dom
->domain_flags
!= 0x0 )
3830 list
[idx
].trust_flags
= new_dom
->domain_flags
;
3832 if ( new_dom
->domain_type
!= 0x0 )
3833 list
[idx
].trust_type
= new_dom
->domain_type
;
3835 if ( new_dom
->domain_trust_attribs
!= 0x0 )
3836 list
[idx
].trust_attribs
= new_dom
->domain_trust_attribs
;
3840 *num_domains
= idx
+ 1;
3846 /*********************************************************************
3847 ********************************************************************/
3849 static TDB_DATA
make_tdc_key( const char *domain_name
)
3851 char *keystr
= NULL
;
3852 TDB_DATA key
= { NULL
, 0 };
3854 if ( !domain_name
) {
3855 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3860 if (asprintf( &keystr
, "TRUSTDOMCACHE/%s", domain_name
) == -1) {
3863 key
= string_term_tdb_data(keystr
);
3868 /*********************************************************************
3869 ********************************************************************/
3871 static int pack_tdc_domains( struct winbindd_tdc_domain
*domains
,
3873 unsigned char **buf
)
3875 unsigned char *buffer
= NULL
;
3880 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3888 /* Store the number of array items first */
3889 len
+= tdb_pack( buffer
+len
, buflen
-len
, "d",
3892 /* now pack each domain trust record */
3893 for ( i
=0; i
<num_domains
; i
++ ) {
3898 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3899 domains
[i
].domain_name
,
3900 domains
[i
].dns_name
? domains
[i
].dns_name
: "UNKNOWN" ));
3903 len
+= tdb_pack( buffer
+len
, buflen
-len
, "fffddd",
3904 domains
[i
].domain_name
,
3905 domains
[i
].dns_name
,
3906 sid_to_fstring(tmp
, &domains
[i
].sid
),
3907 domains
[i
].trust_flags
,
3908 domains
[i
].trust_attribs
,
3909 domains
[i
].trust_type
);
3912 if ( buflen
< len
) {
3914 if ( (buffer
= SMB_MALLOC_ARRAY(unsigned char, len
)) == NULL
) {
3915 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3929 /*********************************************************************
3930 ********************************************************************/
3932 static size_t unpack_tdc_domains( unsigned char *buf
, int buflen
,
3933 struct winbindd_tdc_domain
**domains
)
3935 fstring domain_name
, dns_name
, sid_string
;
3936 uint32 type
, attribs
, flags
;
3940 struct winbindd_tdc_domain
*list
= NULL
;
3942 /* get the number of domains */
3943 len
+= tdb_unpack( buf
+len
, buflen
-len
, "d", &num_domains
);
3945 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3949 list
= TALLOC_ARRAY( NULL
, struct winbindd_tdc_domain
, num_domains
);
3951 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3955 for ( i
=0; i
<num_domains
; i
++ ) {
3956 len
+= tdb_unpack( buf
+len
, buflen
-len
, "fffddd",
3965 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3966 TALLOC_FREE( list
);
3970 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3971 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3972 domain_name
, dns_name
, sid_string
,
3973 flags
, attribs
, type
));
3975 list
[i
].domain_name
= talloc_strdup( list
, domain_name
);
3976 list
[i
].dns_name
= talloc_strdup( list
, dns_name
);
3977 if ( !string_to_sid( &(list
[i
].sid
), sid_string
) ) {
3978 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3981 list
[i
].trust_flags
= flags
;
3982 list
[i
].trust_attribs
= attribs
;
3983 list
[i
].trust_type
= type
;
3991 /*********************************************************************
3992 ********************************************************************/
3994 static bool wcache_tdc_store_list( struct winbindd_tdc_domain
*domains
, size_t num_domains
)
3996 TDB_DATA key
= make_tdc_key( lp_workgroup() );
3997 TDB_DATA data
= { NULL
, 0 };
4003 /* See if we were asked to delete the cache entry */
4006 ret
= tdb_delete( wcache
->tdb
, key
);
4010 data
.dsize
= pack_tdc_domains( domains
, num_domains
, &data
.dptr
);
4017 ret
= tdb_store( wcache
->tdb
, key
, data
, 0 );
4020 SAFE_FREE( data
.dptr
);
4021 SAFE_FREE( key
.dptr
);
4023 return ( ret
!= -1 );
4026 /*********************************************************************
4027 ********************************************************************/
4029 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain
**domains
, size_t *num_domains
)
4031 TDB_DATA key
= make_tdc_key( lp_workgroup() );
4032 TDB_DATA data
= { NULL
, 0 };
4040 data
= tdb_fetch( wcache
->tdb
, key
);
4042 SAFE_FREE( key
.dptr
);
4047 *num_domains
= unpack_tdc_domains( data
.dptr
, data
.dsize
, domains
);
4049 SAFE_FREE( data
.dptr
);
4057 /*********************************************************************
4058 ********************************************************************/
4060 bool wcache_tdc_add_domain( struct winbindd_domain
*domain
)
4062 struct winbindd_tdc_domain
*dom_list
= NULL
;
4063 size_t num_domains
= 0;
4066 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4067 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4068 domain
->name
, domain
->alt_name
,
4069 sid_string_dbg(&domain
->sid
),
4070 domain
->domain_flags
,
4071 domain
->domain_trust_attribs
,
4072 domain
->domain_type
));
4074 if ( !init_wcache() ) {
4078 /* fetch the list */
4080 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4082 /* add the new domain */
4084 if ( !add_wbdomain_to_tdc_array( domain
, &dom_list
, &num_domains
) ) {
4088 /* pack the domain */
4090 if ( !wcache_tdc_store_list( dom_list
, num_domains
) ) {
4098 TALLOC_FREE( dom_list
);
4103 /*********************************************************************
4104 ********************************************************************/
4106 struct winbindd_tdc_domain
* wcache_tdc_fetch_domain( TALLOC_CTX
*ctx
, const char *name
)
4108 struct winbindd_tdc_domain
*dom_list
= NULL
;
4109 size_t num_domains
= 0;
4111 struct winbindd_tdc_domain
*d
= NULL
;
4113 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name
));
4115 if ( !init_wcache() ) {
4119 /* fetch the list */
4121 wcache_tdc_fetch_list( &dom_list
, &num_domains
);
4123 for ( i
=0; i
<num_domains
; i
++ ) {
4124 if ( strequal(name
, dom_list
[i
].domain_name
) ||
4125 strequal(name
, dom_list
[i
].dns_name
) )
4127 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4130 d
= TALLOC_P( ctx
, struct winbindd_tdc_domain
);
4134 d
->domain_name
= talloc_strdup( d
, dom_list
[i
].domain_name
);
4135 d
->dns_name
= talloc_strdup( d
, dom_list
[i
].dns_name
);
4136 sid_copy( &d
->sid
, &dom_list
[i
].sid
);
4137 d
->trust_flags
= dom_list
[i
].trust_flags
;
4138 d
->trust_type
= dom_list
[i
].trust_type
;
4139 d
->trust_attribs
= dom_list
[i
].trust_attribs
;
4145 TALLOC_FREE( dom_list
);
4151 /*********************************************************************
4152 ********************************************************************/
4154 void wcache_tdc_clear( void )
4156 if ( !init_wcache() )
4159 wcache_tdc_store_list( NULL
, 0 );
4165 /*********************************************************************
4166 ********************************************************************/
4168 static void wcache_save_user_pwinfo(struct winbindd_domain
*domain
,
4170 const DOM_SID
*user_sid
,
4171 const char *homedir
,
4176 struct cache_entry
*centry
;
4179 if ( (centry
= centry_start(domain
, status
)) == NULL
)
4182 centry_put_string( centry
, homedir
);
4183 centry_put_string( centry
, shell
);
4184 centry_put_string( centry
, gecos
);
4185 centry_put_uint32( centry
, gid
);
4187 centry_end(centry
, "NSS/PWINFO/%s", sid_to_fstring(tmp
, user_sid
) );
4189 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid
) ));
4191 centry_free(centry
);
4194 NTSTATUS
nss_get_info_cached( struct winbindd_domain
*domain
,
4195 const DOM_SID
*user_sid
,
4197 ADS_STRUCT
*ads
, LDAPMessage
*msg
,
4198 char **homedir
, char **shell
, char **gecos
,
4201 struct winbind_cache
*cache
= get_cache(domain
);
4202 struct cache_entry
*centry
= NULL
;
4209 centry
= wcache_fetch(cache
, domain
, "NSS/PWINFO/%s",
4210 sid_to_fstring(tmp
, user_sid
));
4215 *homedir
= centry_string( centry
, ctx
);
4216 *shell
= centry_string( centry
, ctx
);
4217 *gecos
= centry_string( centry
, ctx
);
4218 *p_gid
= centry_uint32( centry
);
4220 centry_free(centry
);
4222 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4223 sid_string_dbg(user_sid
)));
4225 return NT_STATUS_OK
;
4229 nt_status
= nss_get_info( domain
->name
, user_sid
, ctx
, ads
, msg
,
4230 homedir
, shell
, gecos
, p_gid
);
4232 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status
)));
4234 if ( NT_STATUS_IS_OK(nt_status
) ) {
4235 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir
));
4236 DEBUGADD(10, ("\tshell = '%s'\n", *shell
));
4237 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos
));
4238 DEBUGADD(10, ("\tgid = '%u'\n", *p_gid
));
4240 wcache_save_user_pwinfo( domain
, nt_status
, user_sid
,
4241 *homedir
, *shell
, *gecos
, *p_gid
);
4244 if ( NT_STATUS_EQUAL( nt_status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
4245 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4247 set_domain_offline( domain
);
4254 /* the cache backend methods are exposed via this structure */
4255 struct winbindd_methods cache_methods
= {