2 Unix SMB/CIFS implementation.
4 Winbind cache backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Gerald Carter 2003-2007
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10 Copyright (C) Michael Adam 2007
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #define DBGC_CLASS DBGC_WINBIND
32 #define WINBINDD_CACHE_VERSION 1
33 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35 extern struct winbindd_methods reconnect_methods
;
36 extern bool opt_nocache
;
38 extern struct winbindd_methods ads_methods
;
40 extern struct winbindd_methods builtin_passdb_methods
;
43 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
44 * Here are the list of entry types that are *not* stored
45 * as form struct cache_entry in the cache.
48 static const char *non_centry_keys
[] = {
53 WINBINDD_CACHE_VERSION_KEYSTR
,
57 /************************************************************************
58 Is this key a non-centry type ?
59 ************************************************************************/
61 static bool is_non_centry_key(TDB_DATA kbuf
)
65 if (kbuf
.dptr
== NULL
|| kbuf
.dsize
== 0) {
68 for (i
= 0; non_centry_keys
[i
] != NULL
; i
++) {
69 size_t namelen
= strlen(non_centry_keys
[i
]);
70 if (kbuf
.dsize
< namelen
) {
73 if (strncmp(non_centry_keys
[i
], (const char *)kbuf
.dptr
, namelen
) == 0) {
80 /* Global online/offline state - False when online. winbindd starts up online
81 and sets this to true if the first query fails and there's an entry in
82 the cache tdb telling us to stay offline. */
84 static bool global_winbindd_offline_state
;
86 struct winbind_cache
{
92 uint32 sequence_number
;
97 void (*smb_panic_fn
)(const char *const why
) = smb_panic
;
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
101 static struct winbind_cache
*wcache
;
103 void winbindd_check_cache_size(time_t t
)
105 static time_t last_check_time
;
108 if (last_check_time
== (time_t)0)
111 if (t
- last_check_time
< 60 && t
- last_check_time
> 0)
114 if (wcache
== NULL
|| wcache
->tdb
== NULL
) {
115 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
119 if (fstat(tdb_fd(wcache
->tdb
), &st
) == -1) {
120 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno
) ));
124 if (st
.st_size
> WINBINDD_MAX_CACHE_SIZE
) {
125 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
126 (unsigned long)st
.st_size
,
127 (unsigned long)WINBINDD_MAX_CACHE_SIZE
));
128 wcache_flush_cache();
132 /* get the winbind_cache structure */
133 static struct winbind_cache
*get_cache(struct winbindd_domain
*domain
)
135 struct winbind_cache
*ret
= wcache
;
137 /* We have to know what type of domain we are dealing with first. */
139 if (domain
->internal
) {
140 domain
->backend
= &builtin_passdb_methods
;
141 domain
->initialized
= True
;
143 if ( !domain
->initialized
) {
144 init_dc_connection( domain
);
148 OK. listen up becasue I'm only going to say this once.
149 We have the following scenarios to consider
150 (a) trusted AD domains on a Samba DC,
151 (b) trusted AD domains and we are joined to a non-kerberos domain
152 (c) trusted AD domains and we are joined to a kerberos (AD) domain
154 For (a) we can always contact the trusted domain using krb5
155 since we have the domain trust account password
157 For (b) we can only use RPC since we have no way of
158 getting a krb5 ticket in our own domain
160 For (c) we can always use krb5 since we have a kerberos trust
165 if (!domain
->backend
) {
167 struct winbindd_domain
*our_domain
= domain
;
169 /* find our domain first so we can figure out if we
170 are joined to a kerberized domain */
172 if ( !domain
->primary
)
173 our_domain
= find_our_domain();
175 if ((our_domain
->active_directory
|| IS_DC
)
176 && domain
->active_directory
177 && !lp_winbind_rpc_only()) {
178 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain
->name
));
179 domain
->backend
= &ads_methods
;
181 #endif /* HAVE_ADS */
182 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain
->name
));
183 domain
->backend
= &reconnect_methods
;
186 #endif /* HAVE_ADS */
192 ret
= SMB_XMALLOC_P(struct winbind_cache
);
196 wcache_flush_cache();
202 free a centry structure
204 static void centry_free(struct cache_entry
*centry
)
208 SAFE_FREE(centry
->data
);
212 static bool centry_check_bytes(struct cache_entry
*centry
, size_t nbytes
)
214 if (centry
->len
- centry
->ofs
< nbytes
) {
215 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
216 (unsigned int)nbytes
,
217 centry
->len
- centry
->ofs
));
224 pull a uint32 from a cache entry
226 static uint32
centry_uint32(struct cache_entry
*centry
)
230 if (!centry_check_bytes(centry
, 4)) {
231 smb_panic_fn("centry_uint32");
233 ret
= IVAL(centry
->data
, centry
->ofs
);
239 pull a uint16 from a cache entry
241 static uint16
centry_uint16(struct cache_entry
*centry
)
244 if (!centry_check_bytes(centry
, 2)) {
245 smb_panic_fn("centry_uint16");
247 ret
= CVAL(centry
->data
, centry
->ofs
);
253 pull a uint8 from a cache entry
255 static uint8
centry_uint8(struct cache_entry
*centry
)
258 if (!centry_check_bytes(centry
, 1)) {
259 smb_panic_fn("centry_uint8");
261 ret
= CVAL(centry
->data
, centry
->ofs
);
267 pull a NTTIME from a cache entry
269 static NTTIME
centry_nttime(struct cache_entry
*centry
)
272 if (!centry_check_bytes(centry
, 8)) {
273 smb_panic_fn("centry_nttime");
275 ret
= IVAL(centry
->data
, centry
->ofs
);
277 ret
+= (uint64_t)IVAL(centry
->data
, centry
->ofs
) << 32;
283 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
285 static time_t centry_time(struct cache_entry
*centry
)
287 return (time_t)centry_nttime(centry
);
290 /* pull a string from a cache entry, using the supplied
293 static char *centry_string(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
298 len
= centry_uint8(centry
);
301 /* a deliberate NULL string */
305 if (!centry_check_bytes(centry
, (size_t)len
)) {
306 smb_panic_fn("centry_string");
309 ret
= TALLOC_ARRAY(mem_ctx
, char, len
+1);
311 smb_panic_fn("centry_string out of memory\n");
313 memcpy(ret
,centry
->data
+ centry
->ofs
, len
);
319 /* pull a hash16 from a cache entry, using the supplied
322 static char *centry_hash16(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
)
327 len
= centry_uint8(centry
);
330 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
335 if (!centry_check_bytes(centry
, 16)) {
339 ret
= TALLOC_ARRAY(mem_ctx
, char, 16);
341 smb_panic_fn("centry_hash out of memory\n");
343 memcpy(ret
,centry
->data
+ centry
->ofs
, 16);
348 /* pull a sid from a cache entry, using the supplied
351 static bool centry_sid(struct cache_entry
*centry
, TALLOC_CTX
*mem_ctx
, DOM_SID
*sid
)
354 sid_string
= centry_string(centry
, mem_ctx
);
355 if ((sid_string
== NULL
) || (!string_to_sid(sid
, sid_string
))) {
363 pull a NTSTATUS from a cache entry
365 static NTSTATUS
centry_ntstatus(struct cache_entry
*centry
)
369 status
= NT_STATUS(centry_uint32(centry
));
374 /* the server is considered down if it can't give us a sequence number */
375 static bool wcache_server_down(struct winbindd_domain
*domain
)
382 ret
= (domain
->sequence_number
== DOM_SEQUENCE_NONE
);
385 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
390 static NTSTATUS
fetch_cache_seqnum( struct winbindd_domain
*domain
, time_t now
)
397 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398 return NT_STATUS_UNSUCCESSFUL
;
401 fstr_sprintf( key
, "SEQNUM/%s", domain
->name
);
403 data
= tdb_fetch_bystring( wcache
->tdb
, key
);
404 if ( !data
.dptr
|| data
.dsize
!=8 ) {
405 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key
));
406 return NT_STATUS_UNSUCCESSFUL
;
409 domain
->sequence_number
= IVAL(data
.dptr
, 0);
410 domain
->last_seq_check
= IVAL(data
.dptr
, 4);
412 SAFE_FREE(data
.dptr
);
414 /* have we expired? */
416 time_diff
= now
- domain
->last_seq_check
;
417 if ( time_diff
> lp_winbind_cache_time() ) {
418 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
419 domain
->name
, domain
->sequence_number
,
420 (uint32
)domain
->last_seq_check
));
421 return NT_STATUS_UNSUCCESSFUL
;
424 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
425 domain
->name
, domain
->sequence_number
,
426 (uint32
)domain
->last_seq_check
));
431 static NTSTATUS
store_cache_seqnum( struct winbindd_domain
*domain
)
438 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439 return NT_STATUS_UNSUCCESSFUL
;
442 fstr_sprintf( key_str
, "SEQNUM/%s", domain
->name
);
444 SIVAL(buf
, 0, domain
->sequence_number
);
445 SIVAL(buf
, 4, domain
->last_seq_check
);
449 if ( tdb_store_bystring( wcache
->tdb
, key_str
, data
, TDB_REPLACE
) == -1 ) {
450 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str
));
451 return NT_STATUS_UNSUCCESSFUL
;
454 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
455 domain
->name
, domain
->sequence_number
,
456 (uint32
)domain
->last_seq_check
));
462 refresh the domain sequence number. If force is true
463 then always refresh it, no matter how recently we fetched it
466 static void refresh_sequence_number(struct winbindd_domain
*domain
, bool force
)
470 time_t t
= time(NULL
);
471 unsigned cache_time
= lp_winbind_cache_time();
473 if ( IS_DOMAIN_OFFLINE(domain
) ) {
479 #if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
480 /* trying to reconnect is expensive, don't do it too often */
481 if (domain
->sequence_number
== DOM_SEQUENCE_NONE
) {
486 time_diff
= t
- domain
->last_seq_check
;
488 /* see if we have to refetch the domain sequence number */
489 if (!force
&& (time_diff
< cache_time
) &&
490 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
491 NT_STATUS_IS_OK(domain
->last_status
)) {
492 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain
->name
));
496 /* try to get the sequence number from the tdb cache first */
497 /* this will update the timestamp as well */
499 status
= fetch_cache_seqnum( domain
, t
);
500 if (NT_STATUS_IS_OK(status
) &&
501 (domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
502 NT_STATUS_IS_OK(domain
->last_status
)) {
506 /* important! make sure that we know if this is a native
507 mode domain or not. And that we can contact it. */
509 if ( winbindd_can_contact_domain( domain
) ) {
510 status
= domain
->backend
->sequence_number(domain
,
511 &domain
->sequence_number
);
513 /* just use the current time */
514 status
= NT_STATUS_OK
;
515 domain
->sequence_number
= time(NULL
);
519 /* the above call could have set our domain->backend to NULL when
520 * coming from offline to online mode, make sure to reinitialize the
521 * backend - Guenther */
524 if (!NT_STATUS_IS_OK(status
)) {
525 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status
)));
526 domain
->sequence_number
= DOM_SEQUENCE_NONE
;
529 domain
->last_status
= status
;
530 domain
->last_seq_check
= time(NULL
);
532 /* save the new sequence number in the cache */
533 store_cache_seqnum( domain
);
536 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
537 domain
->name
, domain
->sequence_number
));
543 decide if a cache entry has expired
545 static bool centry_expired(struct winbindd_domain
*domain
, const char *keystr
, struct cache_entry
*centry
)
547 /* If we've been told to be offline - stay in that state... */
548 if (lp_winbind_offline_logon() && global_winbindd_offline_state
) {
549 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
550 keystr
, domain
->name
));
554 /* when the domain is offline return the cached entry.
555 * This deals with transient offline states... */
557 if (!domain
->online
) {
558 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
559 keystr
, domain
->name
));
563 /* if the server is OK and our cache entry came from when it was down then
564 the entry is invalid */
565 if ((domain
->sequence_number
!= DOM_SEQUENCE_NONE
) &&
566 (centry
->sequence_number
== DOM_SEQUENCE_NONE
)) {
567 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
568 keystr
, domain
->name
));
572 /* if the server is down or the cache entry is not older than the
573 current sequence number then it is OK */
574 if (wcache_server_down(domain
) ||
575 centry
->sequence_number
== domain
->sequence_number
) {
576 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
577 keystr
, domain
->name
));
581 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
582 keystr
, domain
->name
));
588 static struct cache_entry
*wcache_fetch_raw(char *kstr
)
591 struct cache_entry
*centry
;
594 key
= string_tdb_data(kstr
);
595 data
= tdb_fetch(wcache
->tdb
, key
);
601 centry
= SMB_XMALLOC_P(struct cache_entry
);
602 centry
->data
= (unsigned char *)data
.dptr
;
603 centry
->len
= data
.dsize
;
606 if (centry
->len
< 8) {
607 /* huh? corrupt cache? */
608 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr
));
613 centry
->status
= centry_ntstatus(centry
);
614 centry
->sequence_number
= centry_uint32(centry
);
620 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
621 number and return status
623 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
624 struct winbindd_domain
*domain
,
625 const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
626 static struct cache_entry
*wcache_fetch(struct winbind_cache
*cache
,
627 struct winbindd_domain
*domain
,
628 const char *format
, ...)
632 struct cache_entry
*centry
;
638 refresh_sequence_number(domain
, false);
640 va_start(ap
, format
);
641 smb_xvasprintf(&kstr
, format
, ap
);
644 centry
= wcache_fetch_raw(kstr
);
645 if (centry
== NULL
) {
650 if (centry_expired(domain
, kstr
, centry
)) {
652 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
653 kstr
, domain
->name
));
660 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
661 kstr
, domain
->name
));
667 static void wcache_delete(const char *format
, ...) PRINTF_ATTRIBUTE(1,2);
668 static void wcache_delete(const char *format
, ...)
674 va_start(ap
, format
);
675 smb_xvasprintf(&kstr
, format
, ap
);
678 key
= string_tdb_data(kstr
);
680 tdb_delete(wcache
->tdb
, key
);
685 make sure we have at least len bytes available in a centry
687 static void centry_expand(struct cache_entry
*centry
, uint32 len
)
689 if (centry
->len
- centry
->ofs
>= len
)
692 centry
->data
= SMB_REALLOC_ARRAY(centry
->data
, unsigned char,
695 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry
->len
));
696 smb_panic_fn("out of memory in centry_expand");
701 push a uint32 into a centry
703 static void centry_put_uint32(struct cache_entry
*centry
, uint32 v
)
705 centry_expand(centry
, 4);
706 SIVAL(centry
->data
, centry
->ofs
, v
);
711 push a uint16 into a centry
713 static void centry_put_uint16(struct cache_entry
*centry
, uint16 v
)
715 centry_expand(centry
, 2);
716 SIVAL(centry
->data
, centry
->ofs
, v
);
721 push a uint8 into a centry
723 static void centry_put_uint8(struct cache_entry
*centry
, uint8 v
)
725 centry_expand(centry
, 1);
726 SCVAL(centry
->data
, centry
->ofs
, v
);
731 push a string into a centry
733 static void centry_put_string(struct cache_entry
*centry
, const char *s
)
738 /* null strings are marked as len 0xFFFF */
739 centry_put_uint8(centry
, 0xFF);
744 /* can't handle more than 254 char strings. Truncating is probably best */
746 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len
));
749 centry_put_uint8(centry
, len
);
750 centry_expand(centry
, len
);
751 memcpy(centry
->data
+ centry
->ofs
, s
, len
);
756 push a 16 byte hash into a centry - treat as 16 byte string.
758 static void centry_put_hash16(struct cache_entry
*centry
, const uint8 val
[16])
760 centry_put_uint8(centry
, 16);
761 centry_expand(centry
, 16);
762 memcpy(centry
->data
+ centry
->ofs
, val
, 16);
766 static void centry_put_sid(struct cache_entry
*centry
, const DOM_SID
*sid
)
769 centry_put_string(centry
, sid_to_fstring(sid_string
, sid
));
774 put NTSTATUS into a centry
776 static void centry_put_ntstatus(struct cache_entry
*centry
, NTSTATUS status
)
778 uint32 status_value
= NT_STATUS_V(status
);
779 centry_put_uint32(centry
, status_value
);
784 push a NTTIME into a centry
786 static void centry_put_nttime(struct cache_entry
*centry
, NTTIME nt
)
788 centry_expand(centry
, 8);
789 SIVAL(centry
->data
, centry
->ofs
, nt
& 0xFFFFFFFF);
791 SIVAL(centry
->data
, centry
->ofs
, nt
>> 32);
796 push a time_t into a centry - use a 64 bit size.
797 NTTIME here is being used as a convenient 64-bit size.
799 static void centry_put_time(struct cache_entry
*centry
, time_t t
)
801 NTTIME nt
= (NTTIME
)t
;
802 centry_put_nttime(centry
, nt
);
806 start a centry for output. When finished, call centry_end()
808 struct cache_entry
*centry_start(struct winbindd_domain
*domain
, NTSTATUS status
)
810 struct cache_entry
*centry
;
815 centry
= SMB_XMALLOC_P(struct cache_entry
);
817 centry
->len
= 8192; /* reasonable default */
818 centry
->data
= SMB_XMALLOC_ARRAY(uint8
, centry
->len
);
820 centry
->sequence_number
= domain
->sequence_number
;
821 centry_put_ntstatus(centry
, status
);
822 centry_put_uint32(centry
, centry
->sequence_number
);
827 finish a centry and write it to the tdb
829 static void centry_end(struct cache_entry
*centry
, const char *format
, ...) PRINTF_ATTRIBUTE(2,3);
830 static void centry_end(struct cache_entry
*centry
, const char *format
, ...)
840 va_start(ap
, format
);
841 smb_xvasprintf(&kstr
, format
, ap
);
844 key
= string_tdb_data(kstr
);
845 data
.dptr
= centry
->data
;
846 data
.dsize
= centry
->ofs
;
848 tdb_store(wcache
->tdb
, key
, data
, TDB_REPLACE
);
852 static void wcache_save_name_to_sid(struct winbindd_domain
*domain
,
853 NTSTATUS status
, const char *domain_name
,
854 const char *name
, const DOM_SID
*sid
,
855 enum lsa_SidType type
)
857 struct cache_entry
*centry
;
860 centry
= centry_start(domain
, status
);
863 centry_put_uint32(centry
, type
);
864 centry_put_sid(centry
, sid
);
865 fstrcpy(uname
, name
);
867 centry_end(centry
, "NS/%s/%s", domain_name
, uname
);
868 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name
,
869 uname
, sid_string_dbg(sid
), nt_errstr(status
)));
873 static void wcache_save_sid_to_name(struct winbindd_domain
*domain
, NTSTATUS status
,
874 const DOM_SID
*sid
, const char *domain_name
, const char *name
, enum lsa_SidType type
)
876 struct cache_entry
*centry
;
879 centry
= centry_start(domain
, status
);
883 if (NT_STATUS_IS_OK(status
)) {
884 centry_put_uint32(centry
, type
);
885 centry_put_string(centry
, domain_name
);
886 centry_put_string(centry
, name
);
889 centry_end(centry
, "SN/%s", sid_to_fstring(sid_string
, sid
));
890 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string
,
891 name
, nt_errstr(status
)));
896 static void wcache_save_user(struct winbindd_domain
*domain
, NTSTATUS status
, WINBIND_USERINFO
*info
)
898 struct cache_entry
*centry
;
901 if (is_null_sid(&info
->user_sid
)) {
905 centry
= centry_start(domain
, status
);
908 centry_put_string(centry
, info
->acct_name
);
909 centry_put_string(centry
, info
->full_name
);
910 centry_put_string(centry
, info
->homedir
);
911 centry_put_string(centry
, info
->shell
);
912 centry_put_uint32(centry
, info
->primary_gid
);
913 centry_put_sid(centry
, &info
->user_sid
);
914 centry_put_sid(centry
, &info
->group_sid
);
915 centry_end(centry
, "U/%s", sid_to_fstring(sid_string
,
917 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string
, info
->acct_name
));
921 static void wcache_save_lockout_policy(struct winbindd_domain
*domain
,
923 struct samr_DomInfo12
*lockout_policy
)
925 struct cache_entry
*centry
;
927 centry
= centry_start(domain
, status
);
931 centry_put_nttime(centry
, lockout_policy
->lockout_duration
);
932 centry_put_nttime(centry
, lockout_policy
->lockout_window
);
933 centry_put_uint16(centry
, lockout_policy
->lockout_threshold
);
935 centry_end(centry
, "LOC_POL/%s", domain
->name
);
937 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain
->name
));
944 static void wcache_save_password_policy(struct winbindd_domain
*domain
,
946 struct samr_DomInfo1
*policy
)
948 struct cache_entry
*centry
;
950 centry
= centry_start(domain
, status
);
954 centry_put_uint16(centry
, policy
->min_password_length
);
955 centry_put_uint16(centry
, policy
->password_history_length
);
956 centry_put_uint32(centry
, policy
->password_properties
);
957 centry_put_nttime(centry
, policy
->max_password_age
);
958 centry_put_nttime(centry
, policy
->min_password_age
);
960 centry_end(centry
, "PWD_POL/%s", domain
->name
);
962 DEBUG(10,("wcache_save_password_policy: %s\n", domain
->name
));
967 /***************************************************************************
968 ***************************************************************************/
970 static void wcache_save_username_alias(struct winbindd_domain
*domain
,
972 const char *name
, const char *alias
)
974 struct cache_entry
*centry
;
977 if ( (centry
= centry_start(domain
, status
)) == NULL
)
980 centry_put_string( centry
, alias
);
982 fstrcpy(uname
, name
);
984 centry_end(centry
, "NSS/NA/%s", uname
);
986 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name
, alias
));
991 static void wcache_save_alias_username(struct winbindd_domain
*domain
,
993 const char *alias
, const char *name
)
995 struct cache_entry
*centry
;
998 if ( (centry
= centry_start(domain
, status
)) == NULL
)
1001 centry_put_string( centry
, name
);
1003 fstrcpy(uname
, alias
);
1005 centry_end(centry
, "NSS/AN/%s", uname
);
1007 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias
, name
));
1009 centry_free(centry
);
1012 /***************************************************************************
1013 ***************************************************************************/
1015 NTSTATUS
resolve_username_to_alias( TALLOC_CTX
*mem_ctx
,
1016 struct winbindd_domain
*domain
,
1017 const char *name
, char **alias
)
1019 struct winbind_cache
*cache
= get_cache(domain
);
1020 struct cache_entry
*centry
= NULL
;
1024 if ( domain
->internal
)
1025 return NT_STATUS_NOT_SUPPORTED
;
1030 if ( (upper_name
= SMB_STRDUP(name
)) == NULL
)
1031 return NT_STATUS_NO_MEMORY
;
1032 strupper_m(upper_name
);
1034 centry
= wcache_fetch(cache
, domain
, "NSS/NA/%s", upper_name
);
1036 SAFE_FREE( upper_name
);
1041 status
= centry
->status
;
1043 if (!NT_STATUS_IS_OK(status
)) {
1044 centry_free(centry
);
1048 *alias
= centry_string( centry
, mem_ctx
);
1050 centry_free(centry
);
1052 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1053 name
, *alias
? *alias
: "(none)"));
1055 return (*alias
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1059 /* If its not in cache and we are offline, then fail */
1061 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1062 DEBUG(8,("resolve_username_to_alias: rejecting query "
1063 "in offline mode\n"));
1064 return NT_STATUS_NOT_FOUND
;
1067 status
= nss_map_to_alias( mem_ctx
, domain
->name
, name
, alias
);
1069 if ( NT_STATUS_IS_OK( status
) ) {
1070 wcache_save_username_alias(domain
, status
, name
, *alias
);
1073 if ( NT_STATUS_EQUAL( status
, NT_STATUS_NONE_MAPPED
) ) {
1074 wcache_save_username_alias(domain
, status
, name
, "(NULL)");
1077 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1078 nt_errstr(status
)));
1080 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1081 set_domain_offline( domain
);
1087 /***************************************************************************
1088 ***************************************************************************/
1090 NTSTATUS
resolve_alias_to_username( TALLOC_CTX
*mem_ctx
,
1091 struct winbindd_domain
*domain
,
1092 const char *alias
, char **name
)
1094 struct winbind_cache
*cache
= get_cache(domain
);
1095 struct cache_entry
*centry
= NULL
;
1099 if ( domain
->internal
)
1100 return NT_STATUS_NOT_SUPPORTED
;
1105 if ( (upper_name
= SMB_STRDUP(alias
)) == NULL
)
1106 return NT_STATUS_NO_MEMORY
;
1107 strupper_m(upper_name
);
1109 centry
= wcache_fetch(cache
, domain
, "NSS/AN/%s", upper_name
);
1111 SAFE_FREE( upper_name
);
1116 status
= centry
->status
;
1118 if (!NT_STATUS_IS_OK(status
)) {
1119 centry_free(centry
);
1123 *name
= centry_string( centry
, mem_ctx
);
1125 centry_free(centry
);
1127 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1128 alias
, *name
? *name
: "(none)"));
1130 return (*name
) ? NT_STATUS_OK
: NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1134 /* If its not in cache and we are offline, then fail */
1136 if ( get_global_winbindd_state_offline() || !domain
->online
) {
1137 DEBUG(8,("resolve_alias_to_username: rejecting query "
1138 "in offline mode\n"));
1139 return NT_STATUS_NOT_FOUND
;
1142 /* an alias cannot contain a domain prefix or '@' */
1144 if (strchr(alias
, '\\') || strchr(alias
, '@')) {
1145 DEBUG(10,("resolve_alias_to_username: skipping fully "
1146 "qualified name %s\n", alias
));
1147 return NT_STATUS_OBJECT_NAME_INVALID
;
1150 status
= nss_map_from_alias( mem_ctx
, domain
->name
, alias
, name
);
1152 if ( NT_STATUS_IS_OK( status
) ) {
1153 wcache_save_alias_username( domain
, status
, alias
, *name
);
1156 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
)) {
1157 wcache_save_alias_username(domain
, status
, alias
, "(NULL)");
1160 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1161 nt_errstr(status
)));
1163 if ( NT_STATUS_EQUAL(status
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) ) {
1164 set_domain_offline( domain
);
1170 NTSTATUS
wcache_cached_creds_exist(struct winbindd_domain
*domain
, const DOM_SID
*sid
)
1172 struct winbind_cache
*cache
= get_cache(domain
);
1174 fstring key_str
, tmp
;
1178 return NT_STATUS_INTERNAL_DB_ERROR
;
1181 if (is_null_sid(sid
)) {
1182 return NT_STATUS_INVALID_SID
;
1185 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1186 return NT_STATUS_INVALID_SID
;
1189 fstr_sprintf(key_str
, "CRED/%s", sid_to_fstring(tmp
, sid
));
1191 data
= tdb_fetch(cache
->tdb
, string_tdb_data(key_str
));
1193 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1196 SAFE_FREE(data
.dptr
);
1197 return NT_STATUS_OK
;
1200 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1201 as new salted ones. */
1203 NTSTATUS
wcache_get_creds(struct winbindd_domain
*domain
,
1204 TALLOC_CTX
*mem_ctx
,
1206 const uint8
**cached_nt_pass
,
1207 const uint8
**cached_salt
)
1209 struct winbind_cache
*cache
= get_cache(domain
);
1210 struct cache_entry
*centry
= NULL
;
1217 return NT_STATUS_INTERNAL_DB_ERROR
;
1220 if (is_null_sid(sid
)) {
1221 return NT_STATUS_INVALID_SID
;
1224 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1225 return NT_STATUS_INVALID_SID
;
1228 /* Try and get a salted cred first. If we can't
1229 fall back to an unsalted cred. */
1231 centry
= wcache_fetch(cache
, domain
, "CRED/%s",
1232 sid_to_fstring(tmp
, sid
));
1234 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1235 sid_string_dbg(sid
)));
1236 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1239 t
= centry_time(centry
);
1241 /* In the salted case this isn't actually the nt_hash itself,
1242 but the MD5 of the salt + nt_hash. Let the caller
1243 sort this out. It can tell as we only return the cached_salt
1244 if we are returning a salted cred. */
1246 *cached_nt_pass
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1247 if (*cached_nt_pass
== NULL
) {
1250 sid_to_fstring(sidstr
, sid
);
1252 /* Bad (old) cred cache. Delete and pretend we
1254 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1256 wcache_delete("CRED/%s", sidstr
);
1257 centry_free(centry
);
1258 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
1261 /* We only have 17 bytes more data in the salted cred case. */
1262 if (centry
->len
- centry
->ofs
== 17) {
1263 *cached_salt
= (const uint8
*)centry_hash16(centry
, mem_ctx
);
1265 *cached_salt
= NULL
;
1268 dump_data_pw("cached_nt_pass", *cached_nt_pass
, NT_HASH_LEN
);
1270 dump_data_pw("cached_salt", *cached_salt
, NT_HASH_LEN
);
1273 status
= centry
->status
;
1275 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1276 sid_string_dbg(sid
), nt_errstr(status
) ));
1278 centry_free(centry
);
1282 /* Store creds for a SID - only writes out new salted ones. */
1284 NTSTATUS
wcache_save_creds(struct winbindd_domain
*domain
,
1285 TALLOC_CTX
*mem_ctx
,
1287 const uint8 nt_pass
[NT_HASH_LEN
])
1289 struct cache_entry
*centry
;
1292 uint8 cred_salt
[NT_HASH_LEN
];
1293 uint8 salted_hash
[NT_HASH_LEN
];
1295 if (is_null_sid(sid
)) {
1296 return NT_STATUS_INVALID_SID
;
1299 if (!(sid_peek_rid(sid
, &rid
)) || (rid
== 0)) {
1300 return NT_STATUS_INVALID_SID
;
1303 centry
= centry_start(domain
, NT_STATUS_OK
);
1305 return NT_STATUS_INTERNAL_DB_ERROR
;
1308 dump_data_pw("nt_pass", nt_pass
, NT_HASH_LEN
);
1310 centry_put_time(centry
, time(NULL
));
1312 /* Create a salt and then salt the hash. */
1313 generate_random_buffer(cred_salt
, NT_HASH_LEN
);
1314 E_md5hash(cred_salt
, nt_pass
, salted_hash
);
1316 centry_put_hash16(centry
, salted_hash
);
1317 centry_put_hash16(centry
, cred_salt
);
1318 centry_end(centry
, "CRED/%s", sid_to_fstring(sid_string
, sid
));
1320 DEBUG(10,("wcache_save_creds: %s\n", sid_string
));
1322 centry_free(centry
);
1324 return NT_STATUS_OK
;
1328 /* Query display info. This is the basic user list fn */
1329 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
1330 TALLOC_CTX
*mem_ctx
,
1331 uint32
*num_entries
,
1332 WINBIND_USERINFO
**info
)
1334 struct winbind_cache
*cache
= get_cache(domain
);
1335 struct cache_entry
*centry
= NULL
;
1337 unsigned int i
, retry
;
1342 centry
= wcache_fetch(cache
, domain
, "UL/%s", domain
->name
);
1346 *num_entries
= centry_uint32(centry
);
1348 if (*num_entries
== 0)
1351 (*info
) = TALLOC_ARRAY(mem_ctx
, WINBIND_USERINFO
, *num_entries
);
1353 smb_panic_fn("query_user_list out of memory");
1355 for (i
=0; i
<(*num_entries
); i
++) {
1356 (*info
)[i
].acct_name
= centry_string(centry
, mem_ctx
);
1357 (*info
)[i
].full_name
= centry_string(centry
, mem_ctx
);
1358 (*info
)[i
].homedir
= centry_string(centry
, mem_ctx
);
1359 (*info
)[i
].shell
= centry_string(centry
, mem_ctx
);
1360 centry_sid(centry
, mem_ctx
, &(*info
)[i
].user_sid
);
1361 centry_sid(centry
, mem_ctx
, &(*info
)[i
].group_sid
);
1365 status
= centry
->status
;
1367 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1368 domain
->name
, nt_errstr(status
) ));
1370 centry_free(centry
);
1377 /* Return status value returned by seq number check */
1379 if (!NT_STATUS_IS_OK(domain
->last_status
))
1380 return domain
->last_status
;
1382 /* Put the query_user_list() in a retry loop. There appears to be
1383 * some bug either with Windows 2000 or Samba's handling of large
1384 * rpc replies. This manifests itself as sudden disconnection
1385 * at a random point in the enumeration of a large (60k) user list.
1386 * The retry loop simply tries the operation again. )-: It's not
1387 * pretty but an acceptable workaround until we work out what the
1388 * real problem is. */
1393 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1396 status
= domain
->backend
->query_user_list(domain
, mem_ctx
, num_entries
, info
);
1397 if (!NT_STATUS_IS_OK(status
)) {
1398 DEBUG(3, ("query_user_list: returned 0x%08x, "
1399 "retrying\n", NT_STATUS_V(status
)));
1401 if (NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1402 DEBUG(3, ("query_user_list: flushing "
1403 "connection cache\n"));
1404 invalidate_cm_connection(&domain
->conn
);
1407 } while (NT_STATUS_V(status
) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL
) &&
1411 refresh_sequence_number(domain
, false);
1412 centry
= centry_start(domain
, status
);
1415 centry_put_uint32(centry
, *num_entries
);
1416 for (i
=0; i
<(*num_entries
); i
++) {
1417 centry_put_string(centry
, (*info
)[i
].acct_name
);
1418 centry_put_string(centry
, (*info
)[i
].full_name
);
1419 centry_put_string(centry
, (*info
)[i
].homedir
);
1420 centry_put_string(centry
, (*info
)[i
].shell
);
1421 centry_put_sid(centry
, &(*info
)[i
].user_sid
);
1422 centry_put_sid(centry
, &(*info
)[i
].group_sid
);
1423 if (domain
->backend
&& domain
->backend
->consistent
) {
1424 /* when the backend is consistent we can pre-prime some mappings */
1425 wcache_save_name_to_sid(domain
, NT_STATUS_OK
,
1427 (*info
)[i
].acct_name
,
1428 &(*info
)[i
].user_sid
,
1430 wcache_save_sid_to_name(domain
, NT_STATUS_OK
,
1431 &(*info
)[i
].user_sid
,
1433 (*info
)[i
].acct_name
,
1435 wcache_save_user(domain
, NT_STATUS_OK
, &(*info
)[i
]);
1438 centry_end(centry
, "UL/%s", domain
->name
);
1439 centry_free(centry
);
1445 /* list all domain groups */
1446 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
1447 TALLOC_CTX
*mem_ctx
,
1448 uint32
*num_entries
,
1449 struct acct_info
**info
)
1451 struct winbind_cache
*cache
= get_cache(domain
);
1452 struct cache_entry
*centry
= NULL
;
1459 centry
= wcache_fetch(cache
, domain
, "GL/%s/domain", domain
->name
);
1463 *num_entries
= centry_uint32(centry
);
1465 if (*num_entries
== 0)
1468 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1470 smb_panic_fn("enum_dom_groups out of memory");
1472 for (i
=0; i
<(*num_entries
); i
++) {
1473 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1474 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1475 (*info
)[i
].rid
= centry_uint32(centry
);
1479 status
= centry
->status
;
1481 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1482 domain
->name
, nt_errstr(status
) ));
1484 centry_free(centry
);
1491 /* Return status value returned by seq number check */
1493 if (!NT_STATUS_IS_OK(domain
->last_status
))
1494 return domain
->last_status
;
1496 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1499 status
= domain
->backend
->enum_dom_groups(domain
, mem_ctx
, num_entries
, info
);
1502 refresh_sequence_number(domain
, false);
1503 centry
= centry_start(domain
, status
);
1506 centry_put_uint32(centry
, *num_entries
);
1507 for (i
=0; i
<(*num_entries
); i
++) {
1508 centry_put_string(centry
, (*info
)[i
].acct_name
);
1509 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1510 centry_put_uint32(centry
, (*info
)[i
].rid
);
1512 centry_end(centry
, "GL/%s/domain", domain
->name
);
1513 centry_free(centry
);
1519 /* list all domain groups */
1520 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
1521 TALLOC_CTX
*mem_ctx
,
1522 uint32
*num_entries
,
1523 struct acct_info
**info
)
1525 struct winbind_cache
*cache
= get_cache(domain
);
1526 struct cache_entry
*centry
= NULL
;
1533 centry
= wcache_fetch(cache
, domain
, "GL/%s/local", domain
->name
);
1537 *num_entries
= centry_uint32(centry
);
1539 if (*num_entries
== 0)
1542 (*info
) = TALLOC_ARRAY(mem_ctx
, struct acct_info
, *num_entries
);
1544 smb_panic_fn("enum_dom_groups out of memory");
1546 for (i
=0; i
<(*num_entries
); i
++) {
1547 fstrcpy((*info
)[i
].acct_name
, centry_string(centry
, mem_ctx
));
1548 fstrcpy((*info
)[i
].acct_desc
, centry_string(centry
, mem_ctx
));
1549 (*info
)[i
].rid
= centry_uint32(centry
);
1554 /* If we are returning cached data and the domain controller
1555 is down then we don't know whether the data is up to date
1556 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1559 if (wcache_server_down(domain
)) {
1560 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1561 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1563 status
= centry
->status
;
1565 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1566 domain
->name
, nt_errstr(status
) ));
1568 centry_free(centry
);
1575 /* Return status value returned by seq number check */
1577 if (!NT_STATUS_IS_OK(domain
->last_status
))
1578 return domain
->last_status
;
1580 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1583 status
= domain
->backend
->enum_local_groups(domain
, mem_ctx
, num_entries
, info
);
1586 refresh_sequence_number(domain
, false);
1587 centry
= centry_start(domain
, status
);
1590 centry_put_uint32(centry
, *num_entries
);
1591 for (i
=0; i
<(*num_entries
); i
++) {
1592 centry_put_string(centry
, (*info
)[i
].acct_name
);
1593 centry_put_string(centry
, (*info
)[i
].acct_desc
);
1594 centry_put_uint32(centry
, (*info
)[i
].rid
);
1596 centry_end(centry
, "GL/%s/local", domain
->name
);
1597 centry_free(centry
);
1603 /* convert a single name to a sid in a domain */
1604 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
1605 TALLOC_CTX
*mem_ctx
,
1606 enum winbindd_cmd orig_cmd
,
1607 const char *domain_name
,
1610 enum lsa_SidType
*type
)
1612 struct winbind_cache
*cache
= get_cache(domain
);
1613 struct cache_entry
*centry
= NULL
;
1620 fstrcpy(uname
, name
);
1622 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
1626 status
= centry
->status
;
1627 if (NT_STATUS_IS_OK(status
)) {
1628 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1629 centry_sid(centry
, mem_ctx
, sid
);
1632 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1633 domain
->name
, nt_errstr(status
) ));
1635 centry_free(centry
);
1641 /* If the seq number check indicated that there is a problem
1642 * with this DC, then return that status... except for
1643 * access_denied. This is special because the dc may be in
1644 * "restrict anonymous = 1" mode, in which case it will deny
1645 * most unauthenticated operations, but *will* allow the LSA
1646 * name-to-sid that we try as a fallback. */
1648 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1649 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1650 return domain
->last_status
;
1652 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1655 status
= domain
->backend
->name_to_sid(domain
, mem_ctx
, orig_cmd
,
1656 domain_name
, name
, sid
, type
);
1659 refresh_sequence_number(domain
, false);
1661 if (domain
->online
&&
1662 (NT_STATUS_IS_OK(status
) || NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))) {
1663 wcache_save_name_to_sid(domain
, status
, domain_name
, name
, sid
, *type
);
1665 /* Only save the reverse mapping if this was not a UPN */
1666 if (!strchr(name
, '@')) {
1667 strupper_m(CONST_DISCARD(char *,domain_name
));
1668 strlower_m(CONST_DISCARD(char *,name
));
1669 wcache_save_sid_to_name(domain
, status
, sid
, domain_name
, name
, *type
);
1676 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1678 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
1679 TALLOC_CTX
*mem_ctx
,
1683 enum lsa_SidType
*type
)
1685 struct winbind_cache
*cache
= get_cache(domain
);
1686 struct cache_entry
*centry
= NULL
;
1693 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1694 sid_to_fstring(sid_string
, sid
));
1698 status
= centry
->status
;
1699 if (NT_STATUS_IS_OK(status
)) {
1700 *type
= (enum lsa_SidType
)centry_uint32(centry
);
1701 *domain_name
= centry_string(centry
, mem_ctx
);
1702 *name
= centry_string(centry
, mem_ctx
);
1705 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1706 domain
->name
, nt_errstr(status
) ));
1708 centry_free(centry
);
1713 *domain_name
= NULL
;
1715 /* If the seq number check indicated that there is a problem
1716 * with this DC, then return that status... except for
1717 * access_denied. This is special because the dc may be in
1718 * "restrict anonymous = 1" mode, in which case it will deny
1719 * most unauthenticated operations, but *will* allow the LSA
1720 * sid-to-name that we try as a fallback. */
1722 if (!(NT_STATUS_IS_OK(domain
->last_status
)
1723 || NT_STATUS_EQUAL(domain
->last_status
, NT_STATUS_ACCESS_DENIED
)))
1724 return domain
->last_status
;
1726 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1729 status
= domain
->backend
->sid_to_name(domain
, mem_ctx
, sid
, domain_name
, name
, type
);
1732 refresh_sequence_number(domain
, false);
1733 wcache_save_sid_to_name(domain
, status
, sid
, *domain_name
, *name
, *type
);
1735 /* We can't save the name to sid mapping here, as with sid history a
1736 * later name2sid would give the wrong sid. */
1741 static NTSTATUS
rids_to_names(struct winbindd_domain
*domain
,
1742 TALLOC_CTX
*mem_ctx
,
1743 const DOM_SID
*domain_sid
,
1748 enum lsa_SidType
**types
)
1750 struct winbind_cache
*cache
= get_cache(domain
);
1752 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
1756 *domain_name
= NULL
;
1764 if (num_rids
== 0) {
1765 return NT_STATUS_OK
;
1768 *names
= TALLOC_ARRAY(mem_ctx
, char *, num_rids
);
1769 *types
= TALLOC_ARRAY(mem_ctx
, enum lsa_SidType
, num_rids
);
1771 if ((*names
== NULL
) || (*types
== NULL
)) {
1772 result
= NT_STATUS_NO_MEMORY
;
1776 have_mapped
= have_unmapped
= false;
1778 for (i
=0; i
<num_rids
; i
++) {
1780 struct cache_entry
*centry
;
1783 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1784 result
= NT_STATUS_INTERNAL_ERROR
;
1788 centry
= wcache_fetch(cache
, domain
, "SN/%s",
1789 sid_to_fstring(tmp
, &sid
));
1794 (*types
)[i
] = SID_NAME_UNKNOWN
;
1795 (*names
)[i
] = talloc_strdup(*names
, "");
1797 if (NT_STATUS_IS_OK(centry
->status
)) {
1800 (*types
)[i
] = (enum lsa_SidType
)centry_uint32(centry
);
1802 dom
= centry_string(centry
, mem_ctx
);
1803 if (*domain_name
== NULL
) {
1809 (*names
)[i
] = centry_string(centry
, *names
);
1811 } else if (NT_STATUS_EQUAL(centry
->status
, NT_STATUS_NONE_MAPPED
)) {
1812 have_unmapped
= true;
1815 /* something's definitely wrong */
1816 result
= centry
->status
;
1820 centry_free(centry
);
1824 return NT_STATUS_NONE_MAPPED
;
1826 if (!have_unmapped
) {
1827 return NT_STATUS_OK
;
1829 return STATUS_SOME_UNMAPPED
;
1833 TALLOC_FREE(*names
);
1834 TALLOC_FREE(*types
);
1836 result
= domain
->backend
->rids_to_names(domain
, mem_ctx
, domain_sid
,
1837 rids
, num_rids
, domain_name
,
1841 None of the queried rids has been found so save all negative entries
1843 if (NT_STATUS_EQUAL(result
, NT_STATUS_NONE_MAPPED
)) {
1844 for (i
= 0; i
< num_rids
; i
++) {
1846 const char *name
= "";
1847 const enum lsa_SidType type
= SID_NAME_UNKNOWN
;
1848 NTSTATUS status
= NT_STATUS_NONE_MAPPED
;
1850 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1851 return NT_STATUS_INTERNAL_ERROR
;
1854 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1862 Some or all of the queried rids have been found.
1864 if (!NT_STATUS_IS_OK(result
) &&
1865 !NT_STATUS_EQUAL(result
, STATUS_SOME_UNMAPPED
)) {
1869 refresh_sequence_number(domain
, false);
1871 for (i
=0; i
<num_rids
; i
++) {
1875 if (!sid_compose(&sid
, domain_sid
, rids
[i
])) {
1876 result
= NT_STATUS_INTERNAL_ERROR
;
1880 status
= (*types
)[i
] == SID_NAME_UNKNOWN
?
1881 NT_STATUS_NONE_MAPPED
: NT_STATUS_OK
;
1883 wcache_save_sid_to_name(domain
, status
, &sid
, *domain_name
,
1884 (*names
)[i
], (*types
)[i
]);
1891 TALLOC_FREE(*names
);
1892 TALLOC_FREE(*types
);
1896 /* Lookup user information from a rid */
1897 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
1898 TALLOC_CTX
*mem_ctx
,
1899 const DOM_SID
*user_sid
,
1900 WINBIND_USERINFO
*info
)
1902 struct winbind_cache
*cache
= get_cache(domain
);
1903 struct cache_entry
*centry
= NULL
;
1910 centry
= wcache_fetch(cache
, domain
, "U/%s",
1911 sid_to_fstring(tmp
, user_sid
));
1913 /* If we have an access denied cache entry and a cached info3 in the
1914 samlogon cache then do a query. This will force the rpc back end
1915 to return the info3 data. */
1917 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1918 netsamlogon_cache_have(user_sid
)) {
1919 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1920 domain
->last_status
= NT_STATUS_OK
;
1921 centry_free(centry
);
1928 /* if status is not ok then this is a negative hit
1929 and the rest of the data doesn't matter */
1930 status
= centry
->status
;
1931 if (NT_STATUS_IS_OK(status
)) {
1932 info
->acct_name
= centry_string(centry
, mem_ctx
);
1933 info
->full_name
= centry_string(centry
, mem_ctx
);
1934 info
->homedir
= centry_string(centry
, mem_ctx
);
1935 info
->shell
= centry_string(centry
, mem_ctx
);
1936 info
->primary_gid
= centry_uint32(centry
);
1937 centry_sid(centry
, mem_ctx
, &info
->user_sid
);
1938 centry_sid(centry
, mem_ctx
, &info
->group_sid
);
1941 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1942 domain
->name
, nt_errstr(status
) ));
1944 centry_free(centry
);
1950 /* Return status value returned by seq number check */
1952 if (!NT_STATUS_IS_OK(domain
->last_status
))
1953 return domain
->last_status
;
1955 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1958 status
= domain
->backend
->query_user(domain
, mem_ctx
, user_sid
, info
);
1961 refresh_sequence_number(domain
, false);
1962 wcache_save_user(domain
, status
, info
);
1968 /* Lookup groups a user is a member of. */
1969 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
1970 TALLOC_CTX
*mem_ctx
,
1971 const DOM_SID
*user_sid
,
1972 uint32
*num_groups
, DOM_SID
**user_gids
)
1974 struct winbind_cache
*cache
= get_cache(domain
);
1975 struct cache_entry
*centry
= NULL
;
1983 centry
= wcache_fetch(cache
, domain
, "UG/%s",
1984 sid_to_fstring(sid_string
, user_sid
));
1986 /* If we have an access denied cache entry and a cached info3 in the
1987 samlogon cache then do a query. This will force the rpc back end
1988 to return the info3 data. */
1990 if (NT_STATUS_V(domain
->last_status
) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED
) &&
1991 netsamlogon_cache_have(user_sid
)) {
1992 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1993 domain
->last_status
= NT_STATUS_OK
;
1994 centry_free(centry
);
2001 *num_groups
= centry_uint32(centry
);
2003 if (*num_groups
== 0)
2006 (*user_gids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_groups
);
2007 if (! (*user_gids
)) {
2008 smb_panic_fn("lookup_usergroups out of memory");
2010 for (i
=0; i
<(*num_groups
); i
++) {
2011 centry_sid(centry
, mem_ctx
, &(*user_gids
)[i
]);
2015 status
= centry
->status
;
2017 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2018 domain
->name
, nt_errstr(status
) ));
2020 centry_free(centry
);
2025 (*user_gids
) = NULL
;
2027 /* Return status value returned by seq number check */
2029 if (!NT_STATUS_IS_OK(domain
->last_status
))
2030 return domain
->last_status
;
2032 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2035 status
= domain
->backend
->lookup_usergroups(domain
, mem_ctx
, user_sid
, num_groups
, user_gids
);
2037 if ( NT_STATUS_EQUAL(status
, NT_STATUS_SYNCHRONIZATION_REQUIRED
) )
2041 refresh_sequence_number(domain
, false);
2042 centry
= centry_start(domain
, status
);
2046 centry_put_uint32(centry
, *num_groups
);
2047 for (i
=0; i
<(*num_groups
); i
++) {
2048 centry_put_sid(centry
, &(*user_gids
)[i
]);
2051 centry_end(centry
, "UG/%s", sid_to_fstring(sid_string
, user_sid
));
2052 centry_free(centry
);
2058 static NTSTATUS
lookup_useraliases(struct winbindd_domain
*domain
,
2059 TALLOC_CTX
*mem_ctx
,
2060 uint32 num_sids
, const DOM_SID
*sids
,
2061 uint32
*num_aliases
, uint32
**alias_rids
)
2063 struct winbind_cache
*cache
= get_cache(domain
);
2064 struct cache_entry
*centry
= NULL
;
2066 char *sidlist
= talloc_strdup(mem_ctx
, "");
2072 if (num_sids
== 0) {
2075 return NT_STATUS_OK
;
2078 /* We need to cache indexed by the whole list of SIDs, the aliases
2079 * resulting might come from any of the SIDs. */
2081 for (i
=0; i
<num_sids
; i
++) {
2083 sidlist
= talloc_asprintf(mem_ctx
, "%s/%s", sidlist
,
2084 sid_to_fstring(tmp
, &sids
[i
]));
2085 if (sidlist
== NULL
)
2086 return NT_STATUS_NO_MEMORY
;
2089 centry
= wcache_fetch(cache
, domain
, "UA%s", sidlist
);
2094 *num_aliases
= centry_uint32(centry
);
2098 (*alias_rids
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_aliases
);
2100 if ((*alias_rids
) == NULL
) {
2101 centry_free(centry
);
2102 return NT_STATUS_NO_MEMORY
;
2105 (*alias_rids
) = NULL
;
2108 for (i
=0; i
<(*num_aliases
); i
++)
2109 (*alias_rids
)[i
] = centry_uint32(centry
);
2111 status
= centry
->status
;
2113 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2114 "status %s\n", domain
->name
, nt_errstr(status
)));
2116 centry_free(centry
);
2121 (*alias_rids
) = NULL
;
2123 if (!NT_STATUS_IS_OK(domain
->last_status
))
2124 return domain
->last_status
;
2126 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2127 "for domain %s\n", domain
->name
));
2129 status
= domain
->backend
->lookup_useraliases(domain
, mem_ctx
,
2131 num_aliases
, alias_rids
);
2134 refresh_sequence_number(domain
, false);
2135 centry
= centry_start(domain
, status
);
2138 centry_put_uint32(centry
, *num_aliases
);
2139 for (i
=0; i
<(*num_aliases
); i
++)
2140 centry_put_uint32(centry
, (*alias_rids
)[i
]);
2141 centry_end(centry
, "UA%s", sidlist
);
2142 centry_free(centry
);
2149 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
2150 TALLOC_CTX
*mem_ctx
,
2151 const DOM_SID
*group_sid
, uint32
*num_names
,
2152 DOM_SID
**sid_mem
, char ***names
,
2153 uint32
**name_types
)
2155 struct winbind_cache
*cache
= get_cache(domain
);
2156 struct cache_entry
*centry
= NULL
;
2164 centry
= wcache_fetch(cache
, domain
, "GM/%s",
2165 sid_to_fstring(sid_string
, group_sid
));
2169 *num_names
= centry_uint32(centry
);
2171 if (*num_names
== 0)
2174 (*sid_mem
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_names
);
2175 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_names
);
2176 (*name_types
) = TALLOC_ARRAY(mem_ctx
, uint32
, *num_names
);
2178 if (! (*sid_mem
) || ! (*names
) || ! (*name_types
)) {
2179 smb_panic_fn("lookup_groupmem out of memory");
2182 for (i
=0; i
<(*num_names
); i
++) {
2183 centry_sid(centry
, mem_ctx
, &(*sid_mem
)[i
]);
2184 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2185 (*name_types
)[i
] = centry_uint32(centry
);
2189 status
= centry
->status
;
2191 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2192 domain
->name
, nt_errstr(status
)));
2194 centry_free(centry
);
2201 (*name_types
) = NULL
;
2203 /* Return status value returned by seq number check */
2205 if (!NT_STATUS_IS_OK(domain
->last_status
))
2206 return domain
->last_status
;
2208 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2211 status
= domain
->backend
->lookup_groupmem(domain
, mem_ctx
, group_sid
, num_names
,
2212 sid_mem
, names
, name_types
);
2215 refresh_sequence_number(domain
, false);
2216 centry
= centry_start(domain
, status
);
2219 centry_put_uint32(centry
, *num_names
);
2220 for (i
=0; i
<(*num_names
); i
++) {
2221 centry_put_sid(centry
, &(*sid_mem
)[i
]);
2222 centry_put_string(centry
, (*names
)[i
]);
2223 centry_put_uint32(centry
, (*name_types
)[i
]);
2225 centry_end(centry
, "GM/%s", sid_to_fstring(sid_string
, group_sid
));
2226 centry_free(centry
);
2232 /* find the sequence number for a domain */
2233 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
2235 refresh_sequence_number(domain
, false);
2237 *seq
= domain
->sequence_number
;
2239 return NT_STATUS_OK
;
2242 /* enumerate trusted domains
2243 * (we need to have the list of trustdoms in the cache when we go offline) -
2245 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
2246 TALLOC_CTX
*mem_ctx
,
2247 uint32
*num_domains
,
2252 struct winbind_cache
*cache
= get_cache(domain
);
2253 struct cache_entry
*centry
= NULL
;
2260 centry
= wcache_fetch(cache
, domain
, "TRUSTDOMS/%s", domain
->name
);
2266 *num_domains
= centry_uint32(centry
);
2269 (*names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2270 (*alt_names
) = TALLOC_ARRAY(mem_ctx
, char *, *num_domains
);
2271 (*dom_sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, *num_domains
);
2273 if (! (*dom_sids
) || ! (*names
) || ! (*alt_names
)) {
2274 smb_panic_fn("trusted_domains out of memory");
2278 (*alt_names
) = NULL
;
2282 for (i
=0; i
<(*num_domains
); i
++) {
2283 (*names
)[i
] = centry_string(centry
, mem_ctx
);
2284 (*alt_names
)[i
] = centry_string(centry
, mem_ctx
);
2285 if (!centry_sid(centry
, mem_ctx
, &(*dom_sids
)[i
])) {
2286 sid_copy(&(*dom_sids
)[i
], &global_sid_NULL
);
2290 status
= centry
->status
;
2292 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2293 domain
->name
, *num_domains
, nt_errstr(status
) ));
2295 centry_free(centry
);
2302 (*alt_names
) = NULL
;
2304 /* Return status value returned by seq number check */
2306 if (!NT_STATUS_IS_OK(domain
->last_status
))
2307 return domain
->last_status
;
2309 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2312 status
= domain
->backend
->trusted_domains(domain
, mem_ctx
, num_domains
,
2313 names
, alt_names
, dom_sids
);
2315 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2316 * so that the generic centry handling still applies correctly -
2319 if (!NT_STATUS_IS_ERR(status
)) {
2320 status
= NT_STATUS_OK
;
2324 #if 0 /* Disabled as we want the trust dom list to be managed by
2325 the main parent and always to make the query. --jerry */
2328 refresh_sequence_number(domain
, false);
2330 centry
= centry_start(domain
, status
);
2334 centry_put_uint32(centry
, *num_domains
);
2336 for (i
=0; i
<(*num_domains
); i
++) {
2337 centry_put_string(centry
, (*names
)[i
]);
2338 centry_put_string(centry
, (*alt_names
)[i
]);
2339 centry_put_sid(centry
, &(*dom_sids
)[i
]);
2342 centry_end(centry
, "TRUSTDOMS/%s", domain
->name
);
2344 centry_free(centry
);
2352 /* get lockout policy */
2353 static NTSTATUS
lockout_policy(struct winbindd_domain
*domain
,
2354 TALLOC_CTX
*mem_ctx
,
2355 struct samr_DomInfo12
*policy
)
2357 struct winbind_cache
*cache
= get_cache(domain
);
2358 struct cache_entry
*centry
= NULL
;
2364 centry
= wcache_fetch(cache
, domain
, "LOC_POL/%s", domain
->name
);
2369 policy
->lockout_duration
= centry_nttime(centry
);
2370 policy
->lockout_window
= centry_nttime(centry
);
2371 policy
->lockout_threshold
= centry_uint16(centry
);
2373 status
= centry
->status
;
2375 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2376 domain
->name
, nt_errstr(status
) ));
2378 centry_free(centry
);
2382 ZERO_STRUCTP(policy
);
2384 /* Return status value returned by seq number check */
2386 if (!NT_STATUS_IS_OK(domain
->last_status
))
2387 return domain
->last_status
;
2389 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2392 status
= domain
->backend
->lockout_policy(domain
, mem_ctx
, policy
);
2395 refresh_sequence_number(domain
, false);
2396 wcache_save_lockout_policy(domain
, status
, policy
);
2401 /* get password policy */
2402 static NTSTATUS
password_policy(struct winbindd_domain
*domain
,
2403 TALLOC_CTX
*mem_ctx
,
2404 struct samr_DomInfo1
*policy
)
2406 struct winbind_cache
*cache
= get_cache(domain
);
2407 struct cache_entry
*centry
= NULL
;
2413 centry
= wcache_fetch(cache
, domain
, "PWD_POL/%s", domain
->name
);
2418 policy
->min_password_length
= centry_uint16(centry
);
2419 policy
->password_history_length
= centry_uint16(centry
);
2420 policy
->password_properties
= centry_uint32(centry
);
2421 policy
->max_password_age
= centry_nttime(centry
);
2422 policy
->min_password_age
= centry_nttime(centry
);
2424 status
= centry
->status
;
2426 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2427 domain
->name
, nt_errstr(status
) ));
2429 centry_free(centry
);
2433 ZERO_STRUCTP(policy
);
2435 /* Return status value returned by seq number check */
2437 if (!NT_STATUS_IS_OK(domain
->last_status
))
2438 return domain
->last_status
;
2440 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2443 status
= domain
->backend
->password_policy(domain
, mem_ctx
, policy
);
2446 refresh_sequence_number(domain
, false);
2447 if (NT_STATUS_IS_OK(status
)) {
2448 wcache_save_password_policy(domain
, status
, policy
);
2455 /* Invalidate cached user and group lists coherently */
2457 static int traverse_fn(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
, TDB_DATA dbuf
,
2460 if (strncmp((const char *)kbuf
.dptr
, "UL/", 3) == 0 ||
2461 strncmp((const char *)kbuf
.dptr
, "GL/", 3) == 0)
2462 tdb_delete(the_tdb
, kbuf
);
2467 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2469 void wcache_invalidate_samlogon(struct winbindd_domain
*domain
,
2470 struct netr_SamInfo3
*info3
)
2473 fstring key_str
, sid_string
;
2474 struct winbind_cache
*cache
;
2476 /* dont clear cached U/SID and UG/SID entries when we want to logon
2479 if (lp_winbind_offline_logon()) {
2486 cache
= get_cache(domain
);
2492 sid_copy(&sid
, info3
->base
.domain_sid
);
2493 sid_append_rid(&sid
, info3
->base
.rid
);
2495 /* Clear U/SID cache entry */
2496 fstr_sprintf(key_str
, "U/%s", sid_to_fstring(sid_string
, &sid
));
2497 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2498 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2500 /* Clear UG/SID cache entry */
2501 fstr_sprintf(key_str
, "UG/%s", sid_to_fstring(sid_string
, &sid
));
2502 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str
));
2503 tdb_delete(cache
->tdb
, string_tdb_data(key_str
));
2505 /* Samba/winbindd never needs this. */
2506 netsamlogon_clear_cached_user(info3
);
2509 bool wcache_invalidate_cache(void)
2511 struct winbindd_domain
*domain
;
2513 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
2514 struct winbind_cache
*cache
= get_cache(domain
);
2516 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2517 "entries for %s\n", domain
->name
));
2520 tdb_traverse(cache
->tdb
, traverse_fn
, NULL
);
2529 bool init_wcache(void)
2531 if (wcache
== NULL
) {
2532 wcache
= SMB_XMALLOC_P(struct winbind_cache
);
2533 ZERO_STRUCTP(wcache
);
2536 if (wcache
->tdb
!= NULL
)
2539 /* when working offline we must not clear the cache on restart */
2540 wcache
->tdb
= tdb_open_log(lock_path("winbindd_cache.tdb"),
2541 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE
,
2542 lp_winbind_offline_logon() ? TDB_DEFAULT
: (TDB_DEFAULT
| TDB_CLEAR_IF_FIRST
),
2543 O_RDWR
|O_CREAT
, 0600);
2545 if (wcache
->tdb
== NULL
) {
2546 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2553 /************************************************************************
2554 This is called by the parent to initialize the cache file.
2555 We don't need sophisticated locking here as we know we're the
2557 ************************************************************************/
2559 bool initialize_winbindd_cache(void)
2561 bool cache_bad
= true;
2564 if (!init_wcache()) {
2565 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2569 /* Check version number. */
2570 if (tdb_fetch_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, &vers
) &&
2571 vers
== WINBINDD_CACHE_VERSION
) {
2576 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2577 "and re-creating with version number %d\n",
2578 WINBINDD_CACHE_VERSION
));
2580 tdb_close(wcache
->tdb
);
2583 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2584 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2585 lock_path("winbindd_cache.tdb"),
2589 if (!init_wcache()) {
2590 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2591 "init_wcache failed.\n"));
2595 /* Write the version. */
2596 if (!tdb_store_uint32(wcache
->tdb
, WINBINDD_CACHE_VERSION_KEYSTR
, WINBINDD_CACHE_VERSION
)) {
2597 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2598 tdb_errorstr(wcache
->tdb
) ));
2603 tdb_close(wcache
->tdb
);
2608 void close_winbindd_cache(void)
2614 tdb_close(wcache
->tdb
);
2619 void cache_store_response(pid_t pid
, struct winbindd_response
*response
)
2626 DEBUG(10, ("Storing response for pid %d, len %d\n",
2627 pid
, response
->length
));
2629 fstr_sprintf(key_str
, "DR/%d", pid
);
2630 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2631 make_tdb_data((uint8
*)response
, sizeof(*response
)),
2635 if (response
->length
== sizeof(*response
))
2638 /* There's extra data */
2640 DEBUG(10, ("Storing extra data: len=%d\n",
2641 (int)(response
->length
- sizeof(*response
))));
2643 fstr_sprintf(key_str
, "DE/%d", pid
);
2644 if (tdb_store(wcache
->tdb
, string_tdb_data(key_str
),
2645 make_tdb_data((uint8
*)response
->extra_data
.data
,
2646 response
->length
- sizeof(*response
)),
2650 /* We could not store the extra data, make sure the tdb does not
2651 * contain a main record with wrong dangling extra data */
2653 fstr_sprintf(key_str
, "DR/%d", pid
);
2654 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2659 bool cache_retrieve_response(pid_t pid
, struct winbindd_response
* response
)
2667 DEBUG(10, ("Retrieving response for pid %d\n", pid
));
2669 fstr_sprintf(key_str
, "DR/%d", pid
);
2670 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2672 if (data
.dptr
== NULL
)
2675 if (data
.dsize
!= sizeof(*response
))
2678 memcpy(response
, data
.dptr
, data
.dsize
);
2679 SAFE_FREE(data
.dptr
);
2681 if (response
->length
== sizeof(*response
)) {
2682 response
->extra_data
.data
= NULL
;
2686 /* There's extra data */
2688 DEBUG(10, ("Retrieving extra data length=%d\n",
2689 (int)(response
->length
- sizeof(*response
))));
2691 fstr_sprintf(key_str
, "DE/%d", pid
);
2692 data
= tdb_fetch(wcache
->tdb
, string_tdb_data(key_str
));
2694 if (data
.dptr
== NULL
) {
2695 DEBUG(0, ("Did not find extra data\n"));
2699 if (data
.dsize
!= (response
->length
- sizeof(*response
))) {
2700 DEBUG(0, ("Invalid extra data length: %d\n", (int)data
.dsize
));
2701 SAFE_FREE(data
.dptr
);
2705 dump_data(11, (uint8
*)data
.dptr
, data
.dsize
);
2707 response
->extra_data
.data
= data
.dptr
;
2711 void cache_cleanup_response(pid_t pid
)
2718 fstr_sprintf(key_str
, "DR/%d", pid
);
2719 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2721 fstr_sprintf(key_str
, "DE/%d", pid
);
2722 tdb_delete(wcache
->tdb
, string_tdb_data(key_str
));
2728 bool lookup_cached_sid(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
2729 char **domain_name
, char **name
,
2730 enum lsa_SidType
*type
)
2732 struct winbindd_domain
*domain
;
2733 struct winbind_cache
*cache
;
2734 struct cache_entry
*centry
= NULL
;
2738 domain
= find_lookup_domain_from_sid(sid
);
2739 if (domain
== NULL
) {
2743 cache
= get_cache(domain
);
2745 if (cache
->tdb
== NULL
) {
2749 centry
= wcache_fetch(cache
, domain
, "SN/%s",
2750 sid_to_fstring(tmp
, sid
));
2751 if (centry
== NULL
) {
2755 if (NT_STATUS_IS_OK(centry
->status
)) {
2756 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2757 *domain_name
= centry_string(centry
, mem_ctx
);
2758 *name
= centry_string(centry
, mem_ctx
);
2761 status
= centry
->status
;
2762 centry_free(centry
);
2763 return NT_STATUS_IS_OK(status
);
2766 bool lookup_cached_name(TALLOC_CTX
*mem_ctx
,
2767 const char *domain_name
,
2770 enum lsa_SidType
*type
)
2772 struct winbindd_domain
*domain
;
2773 struct winbind_cache
*cache
;
2774 struct cache_entry
*centry
= NULL
;
2777 bool original_online_state
;
2779 domain
= find_lookup_domain_from_name(domain_name
);
2780 if (domain
== NULL
) {
2784 cache
= get_cache(domain
);
2786 if (cache
->tdb
== NULL
) {
2790 fstrcpy(uname
, name
);
2793 /* If we are doing a cached logon, temporarily set the domain
2794 offline so the cache won't expire the entry */
2796 original_online_state
= domain
->online
;
2797 domain
->online
= false;
2798 centry
= wcache_fetch(cache
, domain
, "NS/%s/%s", domain_name
, uname
);
2799 domain
->online
= original_online_state
;
2801 if (centry
== NULL
) {
2805 if (NT_STATUS_IS_OK(centry
->status
)) {
2806 *type
= (enum lsa_SidType
)centry_uint32(centry
);
2807 centry_sid(centry
, mem_ctx
, sid
);
2810 status
= centry
->status
;
2811 centry_free(centry
);
2813 return NT_STATUS_IS_OK(status
);
2816 void cache_name2sid(struct winbindd_domain
*domain
,
2817 const char *domain_name
, const char *name
,
2818 enum lsa_SidType type
, const DOM_SID
*sid
)
2820 refresh_sequence_number(domain
, false);
2821 wcache_save_name_to_sid(domain
, NT_STATUS_OK
, domain_name
, name
,
2826 * The original idea that this cache only contains centries has
2827 * been blurred - now other stuff gets put in here. Ensure we
2828 * ignore these things on cleanup.
2831 static int traverse_fn_cleanup(TDB_CONTEXT
*the_tdb
, TDB_DATA kbuf
,
2832 TDB_DATA dbuf
, void *state
)
2834 struct cache_entry
*centry
;
2836 if (is_non_centry_key(kbuf
)) {
2840 centry
= wcache_fetch_raw((char *)kbuf
.dptr
);
2845 if (!NT_STATUS_IS_OK(centry
->status
)) {
2846 DEBUG(10,("deleting centry %s\n", (const char *)kbuf
.dptr
));
2847 tdb_delete(the_tdb
, kbuf
);
2850 centry_free(centry
);
2854 /* flush the cache */
2855 void wcache_flush_cache(void)
2860 tdb_close(wcache
->tdb
);
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
= {