source: trunk/server/source3/winbindd/winbindd_cache.c

Last change on this file was 862, checked in by Silvan Scherrer, 11 years ago

Samba Server: update trunk to 3.6.23

File size: 119.8 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Winbind cache backend functions
5
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
11
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.
16
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.
21
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/>.
24*/
25
26#include "includes.h"
27#include "system/filesys.h"
28#include "winbindd.h"
29#include "tdb_validate.h"
30#include "../libcli/auth/libcli_auth.h"
31#include "../librpc/gen_ndr/ndr_wbint.h"
32#include "ads.h"
33#include "nss_info.h"
34#include "../libcli/security/security.h"
35#include "passdb/machine_sid.h"
36#include "util_tdb.h"
37
38#undef DBGC_CLASS
39#define DBGC_CLASS DBGC_WINBIND
40
41#define WINBINDD_CACHE_VER1 1 /* initial db version */
42#define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
43
44#define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
46
47extern struct winbindd_methods reconnect_methods;
48#ifdef HAVE_ADS
49extern struct winbindd_methods ads_methods;
50#endif
51extern struct winbindd_methods builtin_passdb_methods;
52extern struct winbindd_methods sam_passdb_methods;
53
54/*
55 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56 * Here are the list of entry types that are *not* stored
57 * as form struct cache_entry in the cache.
58 */
59
60static const char *non_centry_keys[] = {
61 "SEQNUM/",
62 "DR/",
63 "DE/",
64 "WINBINDD_OFFLINE",
65 WINBINDD_CACHE_VERSION_KEYSTR,
66 NULL
67};
68
69/************************************************************************
70 Is this key a non-centry type ?
71************************************************************************/
72
73static bool is_non_centry_key(TDB_DATA kbuf)
74{
75 int i;
76
77 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
78 return false;
79 }
80 for (i = 0; non_centry_keys[i] != NULL; i++) {
81 size_t namelen = strlen(non_centry_keys[i]);
82 if (kbuf.dsize < namelen) {
83 continue;
84 }
85 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
86 return true;
87 }
88 }
89 return false;
90}
91
92/* Global online/offline state - False when online. winbindd starts up online
93 and sets this to true if the first query fails and there's an entry in
94 the cache tdb telling us to stay offline. */
95
96static bool global_winbindd_offline_state;
97
98struct winbind_cache {
99 TDB_CONTEXT *tdb;
100};
101
102struct cache_entry {
103 NTSTATUS status;
104 uint32 sequence_number;
105 uint64_t timeout;
106 uint8 *data;
107 uint32 len, ofs;
108};
109
110void (*smb_panic_fn)(const char *const why) = smb_panic;
111
112#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
113
114static struct winbind_cache *wcache;
115
116/* get the winbind_cache structure */
117static struct winbind_cache *get_cache(struct winbindd_domain *domain)
118{
119 struct winbind_cache *ret = wcache;
120
121 /* We have to know what type of domain we are dealing with first. */
122
123 if (domain->internal) {
124 domain->backend = &builtin_passdb_methods;
125 domain->initialized = True;
126 }
127
128 if (strequal(domain->name, get_global_sam_name()) &&
129 sid_check_is_domain(&domain->sid)) {
130 domain->backend = &sam_passdb_methods;
131 domain->initialized = True;
132 }
133
134 if ( !domain->initialized ) {
135 init_dc_connection( domain );
136 }
137
138 /*
139 OK. listen up becasue I'm only going to say this once.
140 We have the following scenarios to consider
141 (a) trusted AD domains on a Samba DC,
142 (b) trusted AD domains and we are joined to a non-kerberos domain
143 (c) trusted AD domains and we are joined to a kerberos (AD) domain
144
145 For (a) we can always contact the trusted domain using krb5
146 since we have the domain trust account password
147
148 For (b) we can only use RPC since we have no way of
149 getting a krb5 ticket in our own domain
150
151 For (c) we can always use krb5 since we have a kerberos trust
152
153 --jerry
154 */
155
156 if (!domain->backend) {
157#ifdef HAVE_ADS
158 struct winbindd_domain *our_domain = domain;
159
160 /* find our domain first so we can figure out if we
161 are joined to a kerberized domain */
162
163 if ( !domain->primary )
164 our_domain = find_our_domain();
165
166 if ((our_domain->active_directory || IS_DC)
167 && domain->active_directory
168 && !lp_winbind_rpc_only()) {
169 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
170 domain->backend = &ads_methods;
171 } else {
172#endif /* HAVE_ADS */
173 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
174 domain->backend = &reconnect_methods;
175#ifdef HAVE_ADS
176 }
177#endif /* HAVE_ADS */
178 }
179
180 if (ret)
181 return ret;
182
183 ret = SMB_XMALLOC_P(struct winbind_cache);
184 ZERO_STRUCTP(ret);
185
186 wcache = ret;
187 wcache_flush_cache();
188
189 return ret;
190}
191
192/*
193 free a centry structure
194*/
195static void centry_free(struct cache_entry *centry)
196{
197 if (!centry)
198 return;
199 SAFE_FREE(centry->data);
200 free(centry);
201}
202
203static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
204{
205 if (centry->len - centry->ofs < nbytes) {
206 DEBUG(0,("centry corruption? needed %u bytes, have %d\n",
207 (unsigned int)nbytes,
208 centry->len - centry->ofs));
209 return false;
210 }
211 return true;
212}
213
214/*
215 pull a uint64_t from a cache entry
216*/
217static uint64_t centry_uint64_t(struct cache_entry *centry)
218{
219 uint64_t ret;
220
221 if (!centry_check_bytes(centry, 8)) {
222 smb_panic_fn("centry_uint64_t");
223 }
224 ret = BVAL(centry->data, centry->ofs);
225 centry->ofs += 8;
226 return ret;
227}
228
229/*
230 pull a uint32 from a cache entry
231*/
232static uint32 centry_uint32(struct cache_entry *centry)
233{
234 uint32 ret;
235
236 if (!centry_check_bytes(centry, 4)) {
237 smb_panic_fn("centry_uint32");
238 }
239 ret = IVAL(centry->data, centry->ofs);
240 centry->ofs += 4;
241 return ret;
242}
243
244/*
245 pull a uint16 from a cache entry
246*/
247static uint16 centry_uint16(struct cache_entry *centry)
248{
249 uint16 ret;
250 if (!centry_check_bytes(centry, 2)) {
251 smb_panic_fn("centry_uint16");
252 }
253 ret = SVAL(centry->data, centry->ofs);
254 centry->ofs += 2;
255 return ret;
256}
257
258/*
259 pull a uint8 from a cache entry
260*/
261static uint8 centry_uint8(struct cache_entry *centry)
262{
263 uint8 ret;
264 if (!centry_check_bytes(centry, 1)) {
265 smb_panic_fn("centry_uint8");
266 }
267 ret = CVAL(centry->data, centry->ofs);
268 centry->ofs += 1;
269 return ret;
270}
271
272/*
273 pull a NTTIME from a cache entry
274*/
275static NTTIME centry_nttime(struct cache_entry *centry)
276{
277 NTTIME ret;
278 if (!centry_check_bytes(centry, 8)) {
279 smb_panic_fn("centry_nttime");
280 }
281 ret = IVAL(centry->data, centry->ofs);
282 centry->ofs += 4;
283 ret += (uint64)IVAL(centry->data, centry->ofs) << 32;
284 centry->ofs += 4;
285 return ret;
286}
287
288/*
289 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
290*/
291static time_t centry_time(struct cache_entry *centry)
292{
293 return (time_t)centry_nttime(centry);
294}
295
296/* pull a string from a cache entry, using the supplied
297 talloc context
298*/
299static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
300{
301 uint32 len;
302 char *ret;
303
304 len = centry_uint8(centry);
305
306 if (len == 0xFF) {
307 /* a deliberate NULL string */
308 return NULL;
309 }
310
311 if (!centry_check_bytes(centry, (size_t)len)) {
312 smb_panic_fn("centry_string");
313 }
314
315 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
316 if (!ret) {
317 smb_panic_fn("centry_string out of memory\n");
318 }
319 memcpy(ret,centry->data + centry->ofs, len);
320 ret[len] = 0;
321 centry->ofs += len;
322 return ret;
323}
324
325/* pull a hash16 from a cache entry, using the supplied
326 talloc context
327*/
328static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
329{
330 uint32 len;
331 char *ret;
332
333 len = centry_uint8(centry);
334
335 if (len != 16) {
336 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
337 len ));
338 return NULL;
339 }
340
341 if (!centry_check_bytes(centry, 16)) {
342 return NULL;
343 }
344
345 ret = TALLOC_ARRAY(mem_ctx, char, 16);
346 if (!ret) {
347 smb_panic_fn("centry_hash out of memory\n");
348 }
349 memcpy(ret,centry->data + centry->ofs, 16);
350 centry->ofs += 16;
351 return ret;
352}
353
354/* pull a sid from a cache entry, using the supplied
355 talloc context
356*/
357static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
358{
359 char *sid_string;
360 bool ret;
361
362 sid_string = centry_string(centry, talloc_tos());
363 if (sid_string == NULL) {
364 return false;
365 }
366 ret = string_to_sid(sid, sid_string);
367 TALLOC_FREE(sid_string);
368 return ret;
369}
370
371
372/*
373 pull a NTSTATUS from a cache entry
374*/
375static NTSTATUS centry_ntstatus(struct cache_entry *centry)
376{
377 NTSTATUS status;
378
379 status = NT_STATUS(centry_uint32(centry));
380 return status;
381}
382
383
384/* the server is considered down if it can't give us a sequence number */
385static bool wcache_server_down(struct winbindd_domain *domain)
386{
387 bool ret;
388
389 if (!wcache->tdb)
390 return false;
391
392 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
393
394 if (ret)
395 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
396 domain->name ));
397 return ret;
398}
399
400static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
401 uint32_t *last_seq_check)
402{
403 char *key;
404 TDB_DATA data;
405
406 if (wcache->tdb == NULL) {
407 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
408 return false;
409 }
410
411 key = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
412 if (key == NULL) {
413 DEBUG(10, ("talloc failed\n"));
414 return false;
415 }
416
417 data = tdb_fetch_bystring(wcache->tdb, key);
418 TALLOC_FREE(key);
419
420 if (data.dptr == NULL) {
421 DEBUG(10, ("wcache_fetch_seqnum: %s not found\n",
422 domain_name));
423 return false;
424 }
425 if (data.dsize != 8) {
426 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
427 (int)data.dsize));
428 SAFE_FREE(data.dptr);
429 return false;
430 }
431
432 *seqnum = IVAL(data.dptr, 0);
433 *last_seq_check = IVAL(data.dptr, 4);
434 SAFE_FREE(data.dptr);
435
436 return true;
437}
438
439static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
440{
441 uint32 last_check, time_diff;
442
443 if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
444 &last_check)) {
445 return NT_STATUS_UNSUCCESSFUL;
446 }
447 domain->last_seq_check = last_check;
448
449 /* have we expired? */
450
451 time_diff = now - domain->last_seq_check;
452 if ( time_diff > lp_winbind_cache_time() ) {
453 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
454 domain->name, domain->sequence_number,
455 (uint32)domain->last_seq_check));
456 return NT_STATUS_UNSUCCESSFUL;
457 }
458
459 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
460 domain->name, domain->sequence_number,
461 (uint32)domain->last_seq_check));
462
463 return NT_STATUS_OK;
464}
465
466bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
467 time_t last_seq_check)
468{
469 char *key_str;
470 uint8_t buf[8];
471 int ret;
472
473 if (wcache->tdb == NULL) {
474 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
475 return false;
476 }
477
478 key_str = talloc_asprintf(talloc_tos(), "SEQNUM/%s", domain_name);
479 if (key_str == NULL) {
480 DEBUG(10, ("talloc_asprintf failed\n"));
481 return false;
482 }
483
484 SIVAL(buf, 0, seqnum);
485 SIVAL(buf, 4, last_seq_check);
486
487 ret = tdb_store_bystring(wcache->tdb, key_str,
488 make_tdb_data(buf, sizeof(buf)), TDB_REPLACE);
489 TALLOC_FREE(key_str);
490 if (ret == -1) {
491 DEBUG(10, ("tdb_store_bystring failed: %s\n",
492 tdb_errorstr(wcache->tdb)));
493 TALLOC_FREE(key_str);
494 return false;
495 }
496
497 DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
498 domain_name, seqnum, (unsigned)last_seq_check));
499
500 return true;
501}
502
503static bool store_cache_seqnum( struct winbindd_domain *domain )
504{
505 return wcache_store_seqnum(domain->name, domain->sequence_number,
506 domain->last_seq_check);
507}
508
509/*
510 refresh the domain sequence number. If force is true
511 then always refresh it, no matter how recently we fetched it
512*/
513
514static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
515{
516 NTSTATUS status;
517 unsigned time_diff;
518 time_t t = time(NULL);
519 unsigned cache_time = lp_winbind_cache_time();
520
521 if (is_domain_offline(domain)) {
522 return;
523 }
524
525 get_cache( domain );
526
527#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
528 /* trying to reconnect is expensive, don't do it too often */
529 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
530 cache_time *= 8;
531 }
532#endif
533
534 time_diff = t - domain->last_seq_check;
535
536 /* see if we have to refetch the domain sequence number */
537 if (!force && (time_diff < cache_time) &&
538 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
539 NT_STATUS_IS_OK(domain->last_status)) {
540 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
541 goto done;
542 }
543
544 /* try to get the sequence number from the tdb cache first */
545 /* this will update the timestamp as well */
546
547 status = fetch_cache_seqnum( domain, t );
548 if (NT_STATUS_IS_OK(status) &&
549 (domain->sequence_number != DOM_SEQUENCE_NONE) &&
550 NT_STATUS_IS_OK(domain->last_status)) {
551 goto done;
552 }
553
554 /* important! make sure that we know if this is a native
555 mode domain or not. And that we can contact it. */
556
557 if ( winbindd_can_contact_domain( domain ) ) {
558 status = domain->backend->sequence_number(domain,
559 &domain->sequence_number);
560 } else {
561 /* just use the current time */
562 status = NT_STATUS_OK;
563 domain->sequence_number = time(NULL);
564 }
565
566
567 /* the above call could have set our domain->backend to NULL when
568 * coming from offline to online mode, make sure to reinitialize the
569 * backend - Guenther */
570 get_cache( domain );
571
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
574 domain->sequence_number = DOM_SEQUENCE_NONE;
575 }
576
577 domain->last_status = status;
578 domain->last_seq_check = time(NULL);
579
580 /* save the new sequence number in the cache */
581 store_cache_seqnum( domain );
582
583done:
584 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
585 domain->name, domain->sequence_number));
586
587 return;
588}
589
590/*
591 decide if a cache entry has expired
592*/
593static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
594{
595 /* If we've been told to be offline - stay in that state... */
596 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
597 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
598 keystr, domain->name ));
599 return false;
600 }
601
602 /* when the domain is offline return the cached entry.
603 * This deals with transient offline states... */
604
605 if (!domain->online) {
606 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
607 keystr, domain->name ));
608 return false;
609 }
610
611 /* if the server is OK and our cache entry came from when it was down then
612 the entry is invalid */
613 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
614 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
615 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
616 keystr, domain->name ));
617 return true;
618 }
619
620 /* if the server is down or the cache entry is not older than the
621 current sequence number or it did not timeout then it is OK */
622 if (wcache_server_down(domain)
623 || ((centry->sequence_number == domain->sequence_number)
624 && (centry->timeout > time(NULL)))) {
625 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
626 keystr, domain->name ));
627 return false;
628 }
629
630 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
631 keystr, domain->name ));
632
633 /* it's expired */
634 return true;
635}
636
637static struct cache_entry *wcache_fetch_raw(char *kstr)
638{
639 TDB_DATA data;
640 struct cache_entry *centry;
641 TDB_DATA key;
642
643 key = string_tdb_data(kstr);
644 data = tdb_fetch(wcache->tdb, key);
645 if (!data.dptr) {
646 /* a cache miss */
647 return NULL;
648 }
649
650 centry = SMB_XMALLOC_P(struct cache_entry);
651 centry->data = (unsigned char *)data.dptr;
652 centry->len = data.dsize;
653 centry->ofs = 0;
654
655 if (centry->len < 16) {
656 /* huh? corrupt cache? */
657 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
658 "(len < 16)?\n", kstr));
659 centry_free(centry);
660 return NULL;
661 }
662
663 centry->status = centry_ntstatus(centry);
664 centry->sequence_number = centry_uint32(centry);
665 centry->timeout = centry_uint64_t(centry);
666
667 return centry;
668}
669
670static bool is_my_own_sam_domain(struct winbindd_domain *domain)
671{
672 if (strequal(domain->name, get_global_sam_name()) &&
673 sid_check_is_domain(&domain->sid)) {
674 return true;
675 }
676
677 return false;
678}
679
680static bool is_builtin_domain(struct winbindd_domain *domain)
681{
682 if (strequal(domain->name, "BUILTIN") &&
683 sid_check_is_builtin(&domain->sid)) {
684 return true;
685 }
686
687 return false;
688}
689
690/*
691 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
692 number and return status
693*/
694static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
695 struct winbindd_domain *domain,
696 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
697static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
698 struct winbindd_domain *domain,
699 const char *format, ...)
700{
701 va_list ap;
702 char *kstr;
703 struct cache_entry *centry;
704
705 if (!winbindd_use_cache() ||
706 is_my_own_sam_domain(domain) ||
707 is_builtin_domain(domain)) {
708 return NULL;
709 }
710
711 refresh_sequence_number(domain, false);
712
713 va_start(ap, format);
714 smb_xvasprintf(&kstr, format, ap);
715 va_end(ap);
716
717 centry = wcache_fetch_raw(kstr);
718 if (centry == NULL) {
719 free(kstr);
720 return NULL;
721 }
722
723 if (centry_expired(domain, kstr, centry)) {
724
725 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
726 kstr, domain->name ));
727
728 centry_free(centry);
729 free(kstr);
730 return NULL;
731 }
732
733 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
734 kstr, domain->name ));
735
736 free(kstr);
737 return centry;
738}
739
740static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
741static void wcache_delete(const char *format, ...)
742{
743 va_list ap;
744 char *kstr;
745 TDB_DATA key;
746
747 va_start(ap, format);
748 smb_xvasprintf(&kstr, format, ap);
749 va_end(ap);
750
751 key = string_tdb_data(kstr);
752
753 tdb_delete(wcache->tdb, key);
754 free(kstr);
755}
756
757/*
758 make sure we have at least len bytes available in a centry
759*/
760static void centry_expand(struct cache_entry *centry, uint32 len)
761{
762 if (centry->len - centry->ofs >= len)
763 return;
764 centry->len *= 2;
765 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
766 centry->len);
767 if (!centry->data) {
768 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
769 smb_panic_fn("out of memory in centry_expand");
770 }
771}
772
773/*
774 push a uint64_t into a centry
775*/
776static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
777{
778 centry_expand(centry, 8);
779 SBVAL(centry->data, centry->ofs, v);
780 centry->ofs += 8;
781}
782
783/*
784 push a uint32 into a centry
785*/
786static void centry_put_uint32(struct cache_entry *centry, uint32 v)
787{
788 centry_expand(centry, 4);
789 SIVAL(centry->data, centry->ofs, v);
790 centry->ofs += 4;
791}
792
793/*
794 push a uint16 into a centry
795*/
796static void centry_put_uint16(struct cache_entry *centry, uint16 v)
797{
798 centry_expand(centry, 2);
799 SSVAL(centry->data, centry->ofs, v);
800 centry->ofs += 2;
801}
802
803/*
804 push a uint8 into a centry
805*/
806static void centry_put_uint8(struct cache_entry *centry, uint8 v)
807{
808 centry_expand(centry, 1);
809 SCVAL(centry->data, centry->ofs, v);
810 centry->ofs += 1;
811}
812
813/*
814 push a string into a centry
815 */
816static void centry_put_string(struct cache_entry *centry, const char *s)
817{
818 int len;
819
820 if (!s) {
821 /* null strings are marked as len 0xFFFF */
822 centry_put_uint8(centry, 0xFF);
823 return;
824 }
825
826 len = strlen(s);
827 /* can't handle more than 254 char strings. Truncating is probably best */
828 if (len > 254) {
829 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
830 len = 254;
831 }
832 centry_put_uint8(centry, len);
833 centry_expand(centry, len);
834 memcpy(centry->data + centry->ofs, s, len);
835 centry->ofs += len;
836}
837
838/*
839 push a 16 byte hash into a centry - treat as 16 byte string.
840 */
841static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
842{
843 centry_put_uint8(centry, 16);
844 centry_expand(centry, 16);
845 memcpy(centry->data + centry->ofs, val, 16);
846 centry->ofs += 16;
847}
848
849static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
850{
851 fstring sid_string;
852 centry_put_string(centry, sid_to_fstring(sid_string, sid));
853}
854
855
856/*
857 put NTSTATUS into a centry
858*/
859static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
860{
861 uint32 status_value = NT_STATUS_V(status);
862 centry_put_uint32(centry, status_value);
863}
864
865
866/*
867 push a NTTIME into a centry
868*/
869static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
870{
871 centry_expand(centry, 8);
872 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
873 centry->ofs += 4;
874 SIVAL(centry->data, centry->ofs, nt >> 32);
875 centry->ofs += 4;
876}
877
878/*
879 push a time_t into a centry - use a 64 bit size.
880 NTTIME here is being used as a convenient 64-bit size.
881*/
882static void centry_put_time(struct cache_entry *centry, time_t t)
883{
884 NTTIME nt = (NTTIME)t;
885 centry_put_nttime(centry, nt);
886}
887
888/*
889 start a centry for output. When finished, call centry_end()
890*/
891struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
892{
893 struct cache_entry *centry;
894
895 if (!wcache->tdb)
896 return NULL;
897
898 centry = SMB_XMALLOC_P(struct cache_entry);
899
900 centry->len = 8192; /* reasonable default */
901 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
902 centry->ofs = 0;
903 centry->sequence_number = domain->sequence_number;
904 centry->timeout = lp_winbind_cache_time() + time(NULL);
905 centry_put_ntstatus(centry, status);
906 centry_put_uint32(centry, centry->sequence_number);
907 centry_put_uint64_t(centry, centry->timeout);
908 return centry;
909}
910
911/*
912 finish a centry and write it to the tdb
913*/
914static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
915static void centry_end(struct cache_entry *centry, const char *format, ...)
916{
917 va_list ap;
918 char *kstr;
919 TDB_DATA key, data;
920
921 if (!winbindd_use_cache()) {
922 return;
923 }
924
925 va_start(ap, format);
926 smb_xvasprintf(&kstr, format, ap);
927 va_end(ap);
928
929 key = string_tdb_data(kstr);
930 data.dptr = centry->data;
931 data.dsize = centry->ofs;
932
933 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
934 free(kstr);
935}
936
937static void wcache_save_name_to_sid(struct winbindd_domain *domain,
938 NTSTATUS status, const char *domain_name,
939 const char *name, const struct dom_sid *sid,
940 enum lsa_SidType type)
941{
942 struct cache_entry *centry;
943 fstring uname;
944
945 centry = centry_start(domain, status);
946 if (!centry)
947 return;
948
949 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
950 struct winbindd_domain *mydomain =
951 find_domain_from_sid_noinit(sid);
952 if (mydomain != NULL) {
953 domain_name = mydomain->name;
954 }
955 }
956
957 centry_put_uint32(centry, type);
958 centry_put_sid(centry, sid);
959 fstrcpy(uname, name);
960 strupper_m(uname);
961 centry_end(centry, "NS/%s/%s", domain_name, uname);
962 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
963 uname, sid_string_dbg(sid), nt_errstr(status)));
964 centry_free(centry);
965}
966
967static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
968 const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
969{
970 struct cache_entry *centry;
971 fstring sid_string;
972
973 centry = centry_start(domain, status);
974 if (!centry)
975 return;
976
977 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
978 struct winbindd_domain *mydomain =
979 find_domain_from_sid_noinit(sid);
980 if (mydomain != NULL) {
981 domain_name = mydomain->name;
982 }
983 }
984
985 if (NT_STATUS_IS_OK(status)) {
986 centry_put_uint32(centry, type);
987 centry_put_string(centry, domain_name);
988 centry_put_string(centry, name);
989 }
990
991 centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
992 DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
993 name, nt_errstr(status)));
994 centry_free(centry);
995}
996
997
998static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
999 struct wbint_userinfo *info)
1000{
1001 struct cache_entry *centry;
1002 fstring sid_string;
1003
1004 if (is_null_sid(&info->user_sid)) {
1005 return;
1006 }
1007
1008 centry = centry_start(domain, status);
1009 if (!centry)
1010 return;
1011 centry_put_string(centry, info->acct_name);
1012 centry_put_string(centry, info->full_name);
1013 centry_put_string(centry, info->homedir);
1014 centry_put_string(centry, info->shell);
1015 centry_put_uint32(centry, info->primary_gid);
1016 centry_put_sid(centry, &info->user_sid);
1017 centry_put_sid(centry, &info->group_sid);
1018 centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1019 &info->user_sid));
1020 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1021 centry_free(centry);
1022}
1023
1024static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1025 NTSTATUS status,
1026 struct samr_DomInfo12 *lockout_policy)
1027{
1028 struct cache_entry *centry;
1029
1030 centry = centry_start(domain, status);
1031 if (!centry)
1032 return;
1033
1034 centry_put_nttime(centry, lockout_policy->lockout_duration);
1035 centry_put_nttime(centry, lockout_policy->lockout_window);
1036 centry_put_uint16(centry, lockout_policy->lockout_threshold);
1037
1038 centry_end(centry, "LOC_POL/%s", domain->name);
1039
1040 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1041
1042 centry_free(centry);
1043}
1044
1045
1046
1047static void wcache_save_password_policy(struct winbindd_domain *domain,
1048 NTSTATUS status,
1049 struct samr_DomInfo1 *policy)
1050{
1051 struct cache_entry *centry;
1052
1053 centry = centry_start(domain, status);
1054 if (!centry)
1055 return;
1056
1057 centry_put_uint16(centry, policy->min_password_length);
1058 centry_put_uint16(centry, policy->password_history_length);
1059 centry_put_uint32(centry, policy->password_properties);
1060 centry_put_nttime(centry, policy->max_password_age);
1061 centry_put_nttime(centry, policy->min_password_age);
1062
1063 centry_end(centry, "PWD_POL/%s", domain->name);
1064
1065 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1066
1067 centry_free(centry);
1068}
1069
1070/***************************************************************************
1071 ***************************************************************************/
1072
1073static void wcache_save_username_alias(struct winbindd_domain *domain,
1074 NTSTATUS status,
1075 const char *name, const char *alias)
1076{
1077 struct cache_entry *centry;
1078 fstring uname;
1079
1080 if ( (centry = centry_start(domain, status)) == NULL )
1081 return;
1082
1083 centry_put_string( centry, alias );
1084
1085 fstrcpy(uname, name);
1086 strupper_m(uname);
1087 centry_end(centry, "NSS/NA/%s", uname);
1088
1089 DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1090
1091 centry_free(centry);
1092}
1093
1094static void wcache_save_alias_username(struct winbindd_domain *domain,
1095 NTSTATUS status,
1096 const char *alias, const char *name)
1097{
1098 struct cache_entry *centry;
1099 fstring uname;
1100
1101 if ( (centry = centry_start(domain, status)) == NULL )
1102 return;
1103
1104 centry_put_string( centry, name );
1105
1106 fstrcpy(uname, alias);
1107 strupper_m(uname);
1108 centry_end(centry, "NSS/AN/%s", uname);
1109
1110 DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1111
1112 centry_free(centry);
1113}
1114
1115/***************************************************************************
1116 ***************************************************************************/
1117
1118NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1119 struct winbindd_domain *domain,
1120 const char *name, char **alias )
1121{
1122 struct winbind_cache *cache = get_cache(domain);
1123 struct cache_entry *centry = NULL;
1124 NTSTATUS status;
1125 char *upper_name;
1126
1127 if ( domain->internal )
1128 return NT_STATUS_NOT_SUPPORTED;
1129
1130 if (!cache->tdb)
1131 goto do_query;
1132
1133 if ( (upper_name = SMB_STRDUP(name)) == NULL )
1134 return NT_STATUS_NO_MEMORY;
1135 strupper_m(upper_name);
1136
1137 centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1138
1139 SAFE_FREE( upper_name );
1140
1141 if (!centry)
1142 goto do_query;
1143
1144 status = centry->status;
1145
1146 if (!NT_STATUS_IS_OK(status)) {
1147 centry_free(centry);
1148 return status;
1149 }
1150
1151 *alias = centry_string( centry, mem_ctx );
1152
1153 centry_free(centry);
1154
1155 DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1156 name, *alias ? *alias : "(none)"));
1157
1158 return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1159
1160do_query:
1161
1162 /* If its not in cache and we are offline, then fail */
1163
1164 if ( get_global_winbindd_state_offline() || !domain->online ) {
1165 DEBUG(8,("resolve_username_to_alias: rejecting query "
1166 "in offline mode\n"));
1167 return NT_STATUS_NOT_FOUND;
1168 }
1169
1170 status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1171
1172 if ( NT_STATUS_IS_OK( status ) ) {
1173 wcache_save_username_alias(domain, status, name, *alias);
1174 }
1175
1176 if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1177 wcache_save_username_alias(domain, status, name, "(NULL)");
1178 }
1179
1180 DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1181 nt_errstr(status)));
1182
1183 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1184 set_domain_offline( domain );
1185 }
1186
1187 return status;
1188}
1189
1190/***************************************************************************
1191 ***************************************************************************/
1192
1193NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1194 struct winbindd_domain *domain,
1195 const char *alias, char **name )
1196{
1197 struct winbind_cache *cache = get_cache(domain);
1198 struct cache_entry *centry = NULL;
1199 NTSTATUS status;
1200 char *upper_name;
1201
1202 if ( domain->internal )
1203 return NT_STATUS_NOT_SUPPORTED;
1204
1205 if (!cache->tdb)
1206 goto do_query;
1207
1208 if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1209 return NT_STATUS_NO_MEMORY;
1210 strupper_m(upper_name);
1211
1212 centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1213
1214 SAFE_FREE( upper_name );
1215
1216 if (!centry)
1217 goto do_query;
1218
1219 status = centry->status;
1220
1221 if (!NT_STATUS_IS_OK(status)) {
1222 centry_free(centry);
1223 return status;
1224 }
1225
1226 *name = centry_string( centry, mem_ctx );
1227
1228 centry_free(centry);
1229
1230 DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1231 alias, *name ? *name : "(none)"));
1232
1233 return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1234
1235do_query:
1236
1237 /* If its not in cache and we are offline, then fail */
1238
1239 if ( get_global_winbindd_state_offline() || !domain->online ) {
1240 DEBUG(8,("resolve_alias_to_username: rejecting query "
1241 "in offline mode\n"));
1242 return NT_STATUS_NOT_FOUND;
1243 }
1244
1245 /* an alias cannot contain a domain prefix or '@' */
1246
1247 if (strchr(alias, '\\') || strchr(alias, '@')) {
1248 DEBUG(10,("resolve_alias_to_username: skipping fully "
1249 "qualified name %s\n", alias));
1250 return NT_STATUS_OBJECT_NAME_INVALID;
1251 }
1252
1253 status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1254
1255 if ( NT_STATUS_IS_OK( status ) ) {
1256 wcache_save_alias_username( domain, status, alias, *name );
1257 }
1258
1259 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1260 wcache_save_alias_username(domain, status, alias, "(NULL)");
1261 }
1262
1263 DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1264 nt_errstr(status)));
1265
1266 if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1267 set_domain_offline( domain );
1268 }
1269
1270 return status;
1271}
1272
1273NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1274{
1275 struct winbind_cache *cache = get_cache(domain);
1276 TDB_DATA data;
1277 fstring key_str, tmp;
1278 uint32 rid;
1279
1280 if (!cache->tdb) {
1281 return NT_STATUS_INTERNAL_DB_ERROR;
1282 }
1283
1284 if (is_null_sid(sid)) {
1285 return NT_STATUS_INVALID_SID;
1286 }
1287
1288 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1289 return NT_STATUS_INVALID_SID;
1290 }
1291
1292 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1293
1294 data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1295 if (!data.dptr) {
1296 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1297 }
1298
1299 SAFE_FREE(data.dptr);
1300 return NT_STATUS_OK;
1301}
1302
1303/* Lookup creds for a SID - copes with old (unsalted) creds as well
1304 as new salted ones. */
1305
1306NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
1307 TALLOC_CTX *mem_ctx,
1308 const struct dom_sid *sid,
1309 const uint8 **cached_nt_pass,
1310 const uint8 **cached_salt)
1311{
1312 struct winbind_cache *cache = get_cache(domain);
1313 struct cache_entry *centry = NULL;
1314 NTSTATUS status;
1315 time_t t;
1316 uint32 rid;
1317 fstring tmp;
1318
1319 if (!cache->tdb) {
1320 return NT_STATUS_INTERNAL_DB_ERROR;
1321 }
1322
1323 if (is_null_sid(sid)) {
1324 return NT_STATUS_INVALID_SID;
1325 }
1326
1327 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1328 return NT_STATUS_INVALID_SID;
1329 }
1330
1331 /* Try and get a salted cred first. If we can't
1332 fall back to an unsalted cred. */
1333
1334 centry = wcache_fetch(cache, domain, "CRED/%s",
1335 sid_to_fstring(tmp, sid));
1336 if (!centry) {
1337 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
1338 sid_string_dbg(sid)));
1339 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1340 }
1341
1342 t = centry_time(centry);
1343
1344 /* In the salted case this isn't actually the nt_hash itself,
1345 but the MD5 of the salt + nt_hash. Let the caller
1346 sort this out. It can tell as we only return the cached_salt
1347 if we are returning a salted cred. */
1348
1349 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1350 if (*cached_nt_pass == NULL) {
1351 fstring sidstr;
1352
1353 sid_to_fstring(sidstr, sid);
1354
1355 /* Bad (old) cred cache. Delete and pretend we
1356 don't have it. */
1357 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
1358 sidstr));
1359 wcache_delete("CRED/%s", sidstr);
1360 centry_free(centry);
1361 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1362 }
1363
1364 /* We only have 17 bytes more data in the salted cred case. */
1365 if (centry->len - centry->ofs == 17) {
1366 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1367 } else {
1368 *cached_salt = NULL;
1369 }
1370
1371 dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1372 if (*cached_salt) {
1373 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1374 }
1375
1376 status = centry->status;
1377
1378 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1379 sid_string_dbg(sid), nt_errstr(status) ));
1380
1381 centry_free(centry);
1382 return status;
1383}
1384
1385/* Store creds for a SID - only writes out new salted ones. */
1386
1387NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1388 const struct dom_sid *sid,
1389 const uint8 nt_pass[NT_HASH_LEN])
1390{
1391 struct cache_entry *centry;
1392 fstring sid_string;
1393 uint32 rid;
1394 uint8 cred_salt[NT_HASH_LEN];
1395 uint8 salted_hash[NT_HASH_LEN];
1396
1397 if (is_null_sid(sid)) {
1398 return NT_STATUS_INVALID_SID;
1399 }
1400
1401 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1402 return NT_STATUS_INVALID_SID;
1403 }
1404
1405 centry = centry_start(domain, NT_STATUS_OK);
1406 if (!centry) {
1407 return NT_STATUS_INTERNAL_DB_ERROR;
1408 }
1409
1410 dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1411
1412 centry_put_time(centry, time(NULL));
1413
1414 /* Create a salt and then salt the hash. */
1415 generate_random_buffer(cred_salt, NT_HASH_LEN);
1416 E_md5hash(cred_salt, nt_pass, salted_hash);
1417
1418 centry_put_hash16(centry, salted_hash);
1419 centry_put_hash16(centry, cred_salt);
1420 centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1421
1422 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1423
1424 centry_free(centry);
1425
1426 return NT_STATUS_OK;
1427}
1428
1429
1430/* Query display info. This is the basic user list fn */
1431static NTSTATUS query_user_list(struct winbindd_domain *domain,
1432 TALLOC_CTX *mem_ctx,
1433 uint32 *num_entries,
1434 struct wbint_userinfo **info)
1435{
1436 struct winbind_cache *cache = get_cache(domain);
1437 struct cache_entry *centry = NULL;
1438 NTSTATUS status;
1439 unsigned int i, retry;
1440 bool old_status = domain->online;
1441
1442 if (!cache->tdb)
1443 goto do_query;
1444
1445 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1446 if (!centry)
1447 goto do_query;
1448
1449do_fetch_cache:
1450 *num_entries = centry_uint32(centry);
1451
1452 if (*num_entries == 0)
1453 goto do_cached;
1454
1455 (*info) = TALLOC_ARRAY(mem_ctx, struct wbint_userinfo, *num_entries);
1456 if (! (*info)) {
1457 smb_panic_fn("query_user_list out of memory");
1458 }
1459 for (i=0; i<(*num_entries); i++) {
1460 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1461 (*info)[i].full_name = centry_string(centry, mem_ctx);
1462 (*info)[i].homedir = centry_string(centry, mem_ctx);
1463 (*info)[i].shell = centry_string(centry, mem_ctx);
1464 centry_sid(centry, &(*info)[i].user_sid);
1465 centry_sid(centry, &(*info)[i].group_sid);
1466 }
1467
1468do_cached:
1469 status = centry->status;
1470
1471 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1472 domain->name, nt_errstr(status) ));
1473
1474 centry_free(centry);
1475 return status;
1476
1477do_query:
1478 *num_entries = 0;
1479 *info = NULL;
1480
1481 /* Return status value returned by seq number check */
1482
1483 if (!NT_STATUS_IS_OK(domain->last_status))
1484 return domain->last_status;
1485
1486 /* Put the query_user_list() in a retry loop. There appears to be
1487 * some bug either with Windows 2000 or Samba's handling of large
1488 * rpc replies. This manifests itself as sudden disconnection
1489 * at a random point in the enumeration of a large (60k) user list.
1490 * The retry loop simply tries the operation again. )-: It's not
1491 * pretty but an acceptable workaround until we work out what the
1492 * real problem is. */
1493
1494 retry = 0;
1495 do {
1496
1497 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1498 domain->name ));
1499
1500 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1501 if (!NT_STATUS_IS_OK(status)) {
1502 DEBUG(3, ("query_user_list: returned 0x%08x, "
1503 "retrying\n", NT_STATUS_V(status)));
1504 }
1505 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1506 DEBUG(3, ("query_user_list: flushing "
1507 "connection cache\n"));
1508 invalidate_cm_connection(&domain->conn);
1509 }
1510 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1511 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1512 if (!domain->internal && old_status) {
1513 set_domain_offline(domain);
1514 }
1515 /* store partial response. */
1516 if (*num_entries > 0) {
1517 /*
1518 * humm, what about the status used for cache?
1519 * Should it be NT_STATUS_OK?
1520 */
1521 break;
1522 }
1523 /*
1524 * domain is offline now, and there is no user entries,
1525 * try to fetch from cache again.
1526 */
1527 if (cache->tdb && !domain->online && !domain->internal && old_status) {
1528 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1529 /* partial response... */
1530 if (!centry) {
1531 goto skip_save;
1532 } else {
1533 goto do_fetch_cache;
1534 }
1535 } else {
1536 goto skip_save;
1537 }
1538 }
1539
1540 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1541 (retry++ < 5));
1542
1543 /* and save it */
1544 refresh_sequence_number(domain, false);
1545 if (!NT_STATUS_IS_OK(status)) {
1546 return status;
1547 }
1548 centry = centry_start(domain, status);
1549 if (!centry)
1550 goto skip_save;
1551 centry_put_uint32(centry, *num_entries);
1552 for (i=0; i<(*num_entries); i++) {
1553 centry_put_string(centry, (*info)[i].acct_name);
1554 centry_put_string(centry, (*info)[i].full_name);
1555 centry_put_string(centry, (*info)[i].homedir);
1556 centry_put_string(centry, (*info)[i].shell);
1557 centry_put_sid(centry, &(*info)[i].user_sid);
1558 centry_put_sid(centry, &(*info)[i].group_sid);
1559 if (domain->backend && domain->backend->consistent) {
1560 /* when the backend is consistent we can pre-prime some mappings */
1561 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1562 domain->name,
1563 (*info)[i].acct_name,
1564 &(*info)[i].user_sid,
1565 SID_NAME_USER);
1566 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1567 &(*info)[i].user_sid,
1568 domain->name,
1569 (*info)[i].acct_name,
1570 SID_NAME_USER);
1571 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1572 }
1573 }
1574 centry_end(centry, "UL/%s", domain->name);
1575 centry_free(centry);
1576
1577skip_save:
1578 return status;
1579}
1580
1581/* list all domain groups */
1582static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1583 TALLOC_CTX *mem_ctx,
1584 uint32 *num_entries,
1585 struct wb_acct_info **info)
1586{
1587 struct winbind_cache *cache = get_cache(domain);
1588 struct cache_entry *centry = NULL;
1589 NTSTATUS status;
1590 unsigned int i;
1591 bool old_status;
1592
1593 old_status = domain->online;
1594 if (!cache->tdb)
1595 goto do_query;
1596
1597 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1598 if (!centry)
1599 goto do_query;
1600
1601do_fetch_cache:
1602 *num_entries = centry_uint32(centry);
1603
1604 if (*num_entries == 0)
1605 goto do_cached;
1606
1607 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1608 if (! (*info)) {
1609 smb_panic_fn("enum_dom_groups out of memory");
1610 }
1611 for (i=0; i<(*num_entries); i++) {
1612 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1613 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1614 (*info)[i].rid = centry_uint32(centry);
1615 }
1616
1617do_cached:
1618 status = centry->status;
1619
1620 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1621 domain->name, nt_errstr(status) ));
1622
1623 centry_free(centry);
1624 return status;
1625
1626do_query:
1627 *num_entries = 0;
1628 *info = NULL;
1629
1630 /* Return status value returned by seq number check */
1631
1632 if (!NT_STATUS_IS_OK(domain->last_status))
1633 return domain->last_status;
1634
1635 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1636 domain->name ));
1637
1638 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1639
1640 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1641 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1642 if (!domain->internal && old_status) {
1643 set_domain_offline(domain);
1644 }
1645 if (cache->tdb &&
1646 !domain->online &&
1647 !domain->internal &&
1648 old_status) {
1649 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1650 if (centry) {
1651 goto do_fetch_cache;
1652 }
1653 }
1654 }
1655 /* and save it */
1656 refresh_sequence_number(domain, false);
1657 if (!NT_STATUS_IS_OK(status)) {
1658 return status;
1659 }
1660 centry = centry_start(domain, status);
1661 if (!centry)
1662 goto skip_save;
1663 centry_put_uint32(centry, *num_entries);
1664 for (i=0; i<(*num_entries); i++) {
1665 centry_put_string(centry, (*info)[i].acct_name);
1666 centry_put_string(centry, (*info)[i].acct_desc);
1667 centry_put_uint32(centry, (*info)[i].rid);
1668 }
1669 centry_end(centry, "GL/%s/domain", domain->name);
1670 centry_free(centry);
1671
1672skip_save:
1673 return status;
1674}
1675
1676/* list all domain groups */
1677static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1678 TALLOC_CTX *mem_ctx,
1679 uint32 *num_entries,
1680 struct wb_acct_info **info)
1681{
1682 struct winbind_cache *cache = get_cache(domain);
1683 struct cache_entry *centry = NULL;
1684 NTSTATUS status;
1685 unsigned int i;
1686 bool old_status;
1687
1688 old_status = domain->online;
1689 if (!cache->tdb)
1690 goto do_query;
1691
1692 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1693 if (!centry)
1694 goto do_query;
1695
1696do_fetch_cache:
1697 *num_entries = centry_uint32(centry);
1698
1699 if (*num_entries == 0)
1700 goto do_cached;
1701
1702 (*info) = TALLOC_ARRAY(mem_ctx, struct wb_acct_info, *num_entries);
1703 if (! (*info)) {
1704 smb_panic_fn("enum_dom_groups out of memory");
1705 }
1706 for (i=0; i<(*num_entries); i++) {
1707 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1708 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1709 (*info)[i].rid = centry_uint32(centry);
1710 }
1711
1712do_cached:
1713
1714 /* If we are returning cached data and the domain controller
1715 is down then we don't know whether the data is up to date
1716 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1717 indicate this. */
1718
1719 if (wcache_server_down(domain)) {
1720 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1721 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1722 } else
1723 status = centry->status;
1724
1725 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1726 domain->name, nt_errstr(status) ));
1727
1728 centry_free(centry);
1729 return status;
1730
1731do_query:
1732 *num_entries = 0;
1733 *info = NULL;
1734
1735 /* Return status value returned by seq number check */
1736
1737 if (!NT_STATUS_IS_OK(domain->last_status))
1738 return domain->last_status;
1739
1740 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1741 domain->name ));
1742
1743 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1744
1745 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1746 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1747 if (!domain->internal && old_status) {
1748 set_domain_offline(domain);
1749 }
1750 if (cache->tdb &&
1751 !domain->internal &&
1752 !domain->online &&
1753 old_status) {
1754 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1755 if (centry) {
1756 goto do_fetch_cache;
1757 }
1758 }
1759 }
1760 /* and save it */
1761 refresh_sequence_number(domain, false);
1762 if (!NT_STATUS_IS_OK(status)) {
1763 return status;
1764 }
1765 centry = centry_start(domain, status);
1766 if (!centry)
1767 goto skip_save;
1768 centry_put_uint32(centry, *num_entries);
1769 for (i=0; i<(*num_entries); i++) {
1770 centry_put_string(centry, (*info)[i].acct_name);
1771 centry_put_string(centry, (*info)[i].acct_desc);
1772 centry_put_uint32(centry, (*info)[i].rid);
1773 }
1774 centry_end(centry, "GL/%s/local", domain->name);
1775 centry_free(centry);
1776
1777skip_save:
1778 return status;
1779}
1780
1781NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1782 const char *domain_name,
1783 const char *name,
1784 struct dom_sid *sid,
1785 enum lsa_SidType *type)
1786{
1787 struct winbind_cache *cache = get_cache(domain);
1788 struct cache_entry *centry;
1789 NTSTATUS status;
1790 char *uname;
1791
1792 if (cache->tdb == NULL) {
1793 return NT_STATUS_NOT_FOUND;
1794 }
1795
1796 uname = talloc_strdup_upper(talloc_tos(), name);
1797 if (uname == NULL) {
1798 return NT_STATUS_NO_MEMORY;
1799 }
1800
1801 if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1802 domain_name = domain->name;
1803 }
1804
1805 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1806 TALLOC_FREE(uname);
1807 if (centry == NULL) {
1808 return NT_STATUS_NOT_FOUND;
1809 }
1810
1811 status = centry->status;
1812 if (NT_STATUS_IS_OK(status)) {
1813 *type = (enum lsa_SidType)centry_uint32(centry);
1814 centry_sid(centry, sid);
1815 }
1816
1817 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1818 "%s\n", domain->name, nt_errstr(status) ));
1819
1820 centry_free(centry);
1821 return status;
1822}
1823
1824/* convert a single name to a sid in a domain */
1825static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1826 TALLOC_CTX *mem_ctx,
1827 const char *domain_name,
1828 const char *name,
1829 uint32_t flags,
1830 struct dom_sid *sid,
1831 enum lsa_SidType *type)
1832{
1833 NTSTATUS status;
1834 bool old_status;
1835
1836 old_status = domain->online;
1837
1838 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1839 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1840 return status;
1841 }
1842
1843 ZERO_STRUCTP(sid);
1844
1845 /* If the seq number check indicated that there is a problem
1846 * with this DC, then return that status... except for
1847 * access_denied. This is special because the dc may be in
1848 * "restrict anonymous = 1" mode, in which case it will deny
1849 * most unauthenticated operations, but *will* allow the LSA
1850 * name-to-sid that we try as a fallback. */
1851
1852 if (!(NT_STATUS_IS_OK(domain->last_status)
1853 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1854 return domain->last_status;
1855
1856 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1857 domain->name ));
1858
1859 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1860 name, flags, sid, type);
1861
1862 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1863 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1864 if (!domain->internal && old_status) {
1865 set_domain_offline(domain);
1866 }
1867 if (!domain->internal &&
1868 !domain->online &&
1869 old_status) {
1870 NTSTATUS cache_status;
1871 cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1872 return cache_status;
1873 }
1874 }
1875 /* and save it */
1876 refresh_sequence_number(domain, false);
1877
1878 if (domain->online &&
1879 (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1880 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1881
1882 /* Only save the reverse mapping if this was not a UPN */
1883 if (!strchr(name, '@')) {
1884 strupper_m(CONST_DISCARD(char *,domain_name));
1885 strlower_m(CONST_DISCARD(char *,name));
1886 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1887 }
1888 }
1889
1890 return status;
1891}
1892
1893NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1894 const struct dom_sid *sid,
1895 TALLOC_CTX *mem_ctx,
1896 char **domain_name,
1897 char **name,
1898 enum lsa_SidType *type)
1899{
1900 struct winbind_cache *cache = get_cache(domain);
1901 struct cache_entry *centry;
1902 char *sid_string;
1903 NTSTATUS status;
1904
1905 if (cache->tdb == NULL) {
1906 return NT_STATUS_NOT_FOUND;
1907 }
1908
1909 sid_string = sid_string_tos(sid);
1910 if (sid_string == NULL) {
1911 return NT_STATUS_NO_MEMORY;
1912 }
1913
1914 centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1915 TALLOC_FREE(sid_string);
1916 if (centry == NULL) {
1917 return NT_STATUS_NOT_FOUND;
1918 }
1919
1920 if (NT_STATUS_IS_OK(centry->status)) {
1921 *type = (enum lsa_SidType)centry_uint32(centry);
1922 *domain_name = centry_string(centry, mem_ctx);
1923 *name = centry_string(centry, mem_ctx);
1924 }
1925
1926 status = centry->status;
1927 centry_free(centry);
1928
1929 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1930 "%s\n", domain->name, nt_errstr(status) ));
1931
1932 return status;
1933}
1934
1935/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1936 given */
1937static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1938 TALLOC_CTX *mem_ctx,
1939 const struct dom_sid *sid,
1940 char **domain_name,
1941 char **name,
1942 enum lsa_SidType *type)
1943{
1944 NTSTATUS status;
1945 bool old_status;
1946
1947 old_status = domain->online;
1948 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1949 type);
1950 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1951 return status;
1952 }
1953
1954 *name = NULL;
1955 *domain_name = NULL;
1956
1957 /* If the seq number check indicated that there is a problem
1958 * with this DC, then return that status... except for
1959 * access_denied. This is special because the dc may be in
1960 * "restrict anonymous = 1" mode, in which case it will deny
1961 * most unauthenticated operations, but *will* allow the LSA
1962 * sid-to-name that we try as a fallback. */
1963
1964 if (!(NT_STATUS_IS_OK(domain->last_status)
1965 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1966 return domain->last_status;
1967
1968 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1969 domain->name ));
1970
1971 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1972
1973 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1974 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1975 if (!domain->internal && old_status) {
1976 set_domain_offline(domain);
1977 }
1978 if (!domain->internal &&
1979 !domain->online &&
1980 old_status) {
1981 NTSTATUS cache_status;
1982 cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
1983 domain_name, name, type);
1984 return cache_status;
1985 }
1986 }
1987 /* and save it */
1988 refresh_sequence_number(domain, false);
1989 if (!NT_STATUS_IS_OK(status)) {
1990 return status;
1991 }
1992 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1993
1994 /* We can't save the name to sid mapping here, as with sid history a
1995 * later name2sid would give the wrong sid. */
1996
1997 return status;
1998}
1999
2000static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2001 TALLOC_CTX *mem_ctx,
2002 const struct dom_sid *domain_sid,
2003 uint32 *rids,
2004 size_t num_rids,
2005 char **domain_name,
2006 char ***names,
2007 enum lsa_SidType **types)
2008{
2009 struct winbind_cache *cache = get_cache(domain);
2010 size_t i;
2011 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2012 bool have_mapped;
2013 bool have_unmapped;
2014 bool old_status;
2015
2016 old_status = domain->online;
2017 *domain_name = NULL;
2018 *names = NULL;
2019 *types = NULL;
2020
2021 if (!cache->tdb) {
2022 goto do_query;
2023 }
2024
2025 if (num_rids == 0) {
2026 return NT_STATUS_OK;
2027 }
2028
2029 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
2030 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
2031
2032 if ((*names == NULL) || (*types == NULL)) {
2033 result = NT_STATUS_NO_MEMORY;
2034 goto error;
2035 }
2036
2037 have_mapped = have_unmapped = false;
2038
2039 for (i=0; i<num_rids; i++) {
2040 struct dom_sid sid;
2041 struct cache_entry *centry;
2042 fstring tmp;
2043
2044 if (!sid_compose(&sid, domain_sid, rids[i])) {
2045 result = NT_STATUS_INTERNAL_ERROR;
2046 goto error;
2047 }
2048
2049 centry = wcache_fetch(cache, domain, "SN/%s",
2050 sid_to_fstring(tmp, &sid));
2051 if (!centry) {
2052 goto do_query;
2053 }
2054
2055 (*types)[i] = SID_NAME_UNKNOWN;
2056 (*names)[i] = talloc_strdup(*names, "");
2057
2058 if (NT_STATUS_IS_OK(centry->status)) {
2059 char *dom;
2060 have_mapped = true;
2061 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2062
2063 dom = centry_string(centry, mem_ctx);
2064 if (*domain_name == NULL) {
2065 *domain_name = dom;
2066 } else {
2067 talloc_free(dom);
2068 }
2069
2070 (*names)[i] = centry_string(centry, *names);
2071
2072 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2073 || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2074 have_unmapped = true;
2075
2076 } else {
2077 /* something's definitely wrong */
2078 result = centry->status;
2079 goto error;
2080 }
2081
2082 centry_free(centry);
2083 }
2084
2085 if (!have_mapped) {
2086 return NT_STATUS_NONE_MAPPED;
2087 }
2088 if (!have_unmapped) {
2089 return NT_STATUS_OK;
2090 }
2091 return STATUS_SOME_UNMAPPED;
2092
2093 do_query:
2094
2095 TALLOC_FREE(*names);
2096 TALLOC_FREE(*types);
2097
2098 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2099 rids, num_rids, domain_name,
2100 names, types);
2101
2102 if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2103 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2104 if (!domain->internal && old_status) {
2105 set_domain_offline(domain);
2106 }
2107 if (cache->tdb &&
2108 !domain->internal &&
2109 !domain->online &&
2110 old_status) {
2111 have_mapped = have_unmapped = false;
2112
2113 for (i=0; i<num_rids; i++) {
2114 struct dom_sid sid;
2115 struct cache_entry *centry;
2116 fstring tmp;
2117
2118 if (!sid_compose(&sid, domain_sid, rids[i])) {
2119 result = NT_STATUS_INTERNAL_ERROR;
2120 goto error;
2121 }
2122
2123 centry = wcache_fetch(cache, domain, "SN/%s",
2124 sid_to_fstring(tmp, &sid));
2125 if (!centry) {
2126 (*types)[i] = SID_NAME_UNKNOWN;
2127 (*names)[i] = talloc_strdup(*names, "");
2128 continue;
2129 }
2130
2131 (*types)[i] = SID_NAME_UNKNOWN;
2132 (*names)[i] = talloc_strdup(*names, "");
2133
2134 if (NT_STATUS_IS_OK(centry->status)) {
2135 char *dom;
2136 have_mapped = true;
2137 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2138
2139 dom = centry_string(centry, mem_ctx);
2140 if (*domain_name == NULL) {
2141 *domain_name = dom;
2142 } else {
2143 talloc_free(dom);
2144 }
2145
2146 (*names)[i] = centry_string(centry, *names);
2147
2148 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2149 have_unmapped = true;
2150
2151 } else {
2152 /* something's definitely wrong */
2153 result = centry->status;
2154 centry_free(centry);
2155 goto error;
2156 }
2157
2158 centry_free(centry);
2159 }
2160
2161 if (!have_mapped) {
2162 return NT_STATUS_NONE_MAPPED;
2163 }
2164 if (!have_unmapped) {
2165 return NT_STATUS_OK;
2166 }
2167 return STATUS_SOME_UNMAPPED;
2168 }
2169 }
2170 /*
2171 None of the queried rids has been found so save all negative entries
2172 */
2173 if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2174 for (i = 0; i < num_rids; i++) {
2175 struct dom_sid sid;
2176 const char *name = "";
2177 const enum lsa_SidType type = SID_NAME_UNKNOWN;
2178 NTSTATUS status = NT_STATUS_NONE_MAPPED;
2179
2180 if (!sid_compose(&sid, domain_sid, rids[i])) {
2181 return NT_STATUS_INTERNAL_ERROR;
2182 }
2183
2184 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2185 name, type);
2186 }
2187
2188 return result;
2189 }
2190
2191 /*
2192 Some or all of the queried rids have been found.
2193 */
2194 if (!NT_STATUS_IS_OK(result) &&
2195 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2196 return result;
2197 }
2198
2199 refresh_sequence_number(domain, false);
2200
2201 for (i=0; i<num_rids; i++) {
2202 struct dom_sid sid;
2203 NTSTATUS status;
2204
2205 if (!sid_compose(&sid, domain_sid, rids[i])) {
2206 result = NT_STATUS_INTERNAL_ERROR;
2207 goto error;
2208 }
2209
2210 status = (*types)[i] == SID_NAME_UNKNOWN ?
2211 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2212
2213 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2214 (*names)[i], (*types)[i]);
2215 }
2216
2217 return result;
2218
2219 error:
2220 TALLOC_FREE(*names);
2221 TALLOC_FREE(*types);
2222 return result;
2223}
2224
2225NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2226 TALLOC_CTX *mem_ctx,
2227 const struct dom_sid *user_sid,
2228 struct wbint_userinfo *info)
2229{
2230 struct winbind_cache *cache = get_cache(domain);
2231 struct cache_entry *centry = NULL;
2232 NTSTATUS status;
2233 char *sid_string;
2234
2235 if (cache->tdb == NULL) {
2236 return NT_STATUS_NOT_FOUND;
2237 }
2238
2239 sid_string = sid_string_tos(user_sid);
2240 if (sid_string == NULL) {
2241 return NT_STATUS_NO_MEMORY;
2242 }
2243
2244 centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2245 TALLOC_FREE(sid_string);
2246 if (centry == NULL) {
2247 return NT_STATUS_NOT_FOUND;
2248 }
2249
2250 /*
2251 * If we have an access denied cache entry and a cached info3
2252 * in the samlogon cache then do a query. This will force the
2253 * rpc back end to return the info3 data.
2254 */
2255
2256 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2257 netsamlogon_cache_have(user_sid)) {
2258 DEBUG(10, ("query_user: cached access denied and have cached "
2259 "info3\n"));
2260 domain->last_status = NT_STATUS_OK;
2261 centry_free(centry);
2262 return NT_STATUS_NOT_FOUND;
2263 }
2264
2265 /* if status is not ok then this is a negative hit
2266 and the rest of the data doesn't matter */
2267 status = centry->status;
2268 if (NT_STATUS_IS_OK(status)) {
2269 info->acct_name = centry_string(centry, mem_ctx);
2270 info->full_name = centry_string(centry, mem_ctx);
2271 info->homedir = centry_string(centry, mem_ctx);
2272 info->shell = centry_string(centry, mem_ctx);
2273 info->primary_gid = centry_uint32(centry);
2274 centry_sid(centry, &info->user_sid);
2275 centry_sid(centry, &info->group_sid);
2276 }
2277
2278 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2279 "%s\n", domain->name, nt_errstr(status) ));
2280
2281 centry_free(centry);
2282 return status;
2283}
2284
2285/* Lookup user information from a rid */
2286static NTSTATUS query_user(struct winbindd_domain *domain,
2287 TALLOC_CTX *mem_ctx,
2288 const struct dom_sid *user_sid,
2289 struct wbint_userinfo *info)
2290{
2291 NTSTATUS status;
2292 bool old_status;
2293
2294 old_status = domain->online;
2295 status = wcache_query_user(domain, mem_ctx, user_sid, info);
2296 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2297 return status;
2298 }
2299
2300 ZERO_STRUCTP(info);
2301
2302 /* Return status value returned by seq number check */
2303
2304 if (!NT_STATUS_IS_OK(domain->last_status))
2305 return domain->last_status;
2306
2307 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2308 domain->name ));
2309
2310 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2311
2312 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2313 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2314 if (!domain->internal && old_status) {
2315 set_domain_offline(domain);
2316 }
2317 if (!domain->internal &&
2318 !domain->online &&
2319 old_status) {
2320 NTSTATUS cache_status;
2321 cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2322 return cache_status;
2323 }
2324 }
2325 /* and save it */
2326 refresh_sequence_number(domain, false);
2327 if (!NT_STATUS_IS_OK(status)) {
2328 return status;
2329 }
2330 wcache_save_user(domain, status, info);
2331
2332 return status;
2333}
2334
2335NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2336 TALLOC_CTX *mem_ctx,
2337 const struct dom_sid *user_sid,
2338 uint32_t *pnum_sids,
2339 struct dom_sid **psids)
2340{
2341 struct winbind_cache *cache = get_cache(domain);
2342 struct cache_entry *centry = NULL;
2343 NTSTATUS status;
2344 uint32_t i, num_sids;
2345 struct dom_sid *sids;
2346 fstring sid_string;
2347
2348 if (cache->tdb == NULL) {
2349 return NT_STATUS_NOT_FOUND;
2350 }
2351
2352 centry = wcache_fetch(cache, domain, "UG/%s",
2353 sid_to_fstring(sid_string, user_sid));
2354 if (centry == NULL) {
2355 return NT_STATUS_NOT_FOUND;
2356 }
2357
2358 /* If we have an access denied cache entry and a cached info3 in the
2359 samlogon cache then do a query. This will force the rpc back end
2360 to return the info3 data. */
2361
2362 if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2363 && netsamlogon_cache_have(user_sid)) {
2364 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2365 "cached info3\n"));
2366 domain->last_status = NT_STATUS_OK;
2367 centry_free(centry);
2368 return NT_STATUS_NOT_FOUND;
2369 }
2370
2371 num_sids = centry_uint32(centry);
2372 sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2373 if (sids == NULL) {
2374 centry_free(centry);
2375 return NT_STATUS_NO_MEMORY;
2376 }
2377
2378 for (i=0; i<num_sids; i++) {
2379 centry_sid(centry, &sids[i]);
2380 }
2381
2382 status = centry->status;
2383
2384 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2385 "status: %s\n", domain->name, nt_errstr(status)));
2386
2387 centry_free(centry);
2388
2389 *pnum_sids = num_sids;
2390 *psids = sids;
2391 return status;
2392}
2393
2394/* Lookup groups a user is a member of. */
2395static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2396 TALLOC_CTX *mem_ctx,
2397 const struct dom_sid *user_sid,
2398 uint32 *num_groups, struct dom_sid **user_gids)
2399{
2400 struct cache_entry *centry = NULL;
2401 NTSTATUS status;
2402 unsigned int i;
2403 fstring sid_string;
2404 bool old_status;
2405
2406 old_status = domain->online;
2407 status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2408 num_groups, user_gids);
2409 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2410 return status;
2411 }
2412
2413 (*num_groups) = 0;
2414 (*user_gids) = NULL;
2415
2416 /* Return status value returned by seq number check */
2417
2418 if (!NT_STATUS_IS_OK(domain->last_status))
2419 return domain->last_status;
2420
2421 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2422 domain->name ));
2423
2424 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2425
2426 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2427 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2428 if (!domain->internal && old_status) {
2429 set_domain_offline(domain);
2430 }
2431 if (!domain->internal &&
2432 !domain->online &&
2433 old_status) {
2434 NTSTATUS cache_status;
2435 cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2436 num_groups, user_gids);
2437 return cache_status;
2438 }
2439 }
2440 if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2441 goto skip_save;
2442
2443 /* and save it */
2444 refresh_sequence_number(domain, false);
2445 if (!NT_STATUS_IS_OK(status)) {
2446 return status;
2447 }
2448 centry = centry_start(domain, status);
2449 if (!centry)
2450 goto skip_save;
2451
2452 centry_put_uint32(centry, *num_groups);
2453 for (i=0; i<(*num_groups); i++) {
2454 centry_put_sid(centry, &(*user_gids)[i]);
2455 }
2456
2457 centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2458 centry_free(centry);
2459
2460skip_save:
2461 return status;
2462}
2463
2464static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2465 const struct dom_sid *sids)
2466{
2467 uint32_t i;
2468 char *sidlist;
2469
2470 sidlist = talloc_strdup(mem_ctx, "");
2471 if (sidlist == NULL) {
2472 return NULL;
2473 }
2474 for (i=0; i<num_sids; i++) {
2475 fstring tmp;
2476 sidlist = talloc_asprintf_append_buffer(
2477 sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2478 if (sidlist == NULL) {
2479 return NULL;
2480 }
2481 }
2482 return sidlist;
2483}
2484
2485NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2486 TALLOC_CTX *mem_ctx, uint32_t num_sids,
2487 const struct dom_sid *sids,
2488 uint32_t *pnum_aliases, uint32_t **paliases)
2489{
2490 struct winbind_cache *cache = get_cache(domain);
2491 struct cache_entry *centry = NULL;
2492 uint32_t num_aliases;
2493 uint32_t *aliases;
2494 NTSTATUS status;
2495 char *sidlist;
2496 int i;
2497
2498 if (cache->tdb == NULL) {
2499 return NT_STATUS_NOT_FOUND;
2500 }
2501
2502 if (num_sids == 0) {
2503 *pnum_aliases = 0;
2504 *paliases = NULL;
2505 return NT_STATUS_OK;
2506 }
2507
2508 /* We need to cache indexed by the whole list of SIDs, the aliases
2509 * resulting might come from any of the SIDs. */
2510
2511 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2512 if (sidlist == NULL) {
2513 return NT_STATUS_NO_MEMORY;
2514 }
2515
2516 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2517 TALLOC_FREE(sidlist);
2518 if (centry == NULL) {
2519 return NT_STATUS_NOT_FOUND;
2520 }
2521
2522 num_aliases = centry_uint32(centry);
2523 aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2524 if (aliases == NULL) {
2525 centry_free(centry);
2526 return NT_STATUS_NO_MEMORY;
2527 }
2528
2529 for (i=0; i<num_aliases; i++) {
2530 aliases[i] = centry_uint32(centry);
2531 }
2532
2533 status = centry->status;
2534
2535 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2536 "status %s\n", domain->name, nt_errstr(status)));
2537
2538 centry_free(centry);
2539
2540 *pnum_aliases = num_aliases;
2541 *paliases = aliases;
2542
2543 return status;
2544}
2545
2546static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2547 TALLOC_CTX *mem_ctx,
2548 uint32 num_sids, const struct dom_sid *sids,
2549 uint32 *num_aliases, uint32 **alias_rids)
2550{
2551 struct cache_entry *centry = NULL;
2552 NTSTATUS status;
2553 char *sidlist;
2554 int i;
2555 bool old_status;
2556
2557 old_status = domain->online;
2558 status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2559 num_aliases, alias_rids);
2560 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2561 return status;
2562 }
2563
2564 (*num_aliases) = 0;
2565 (*alias_rids) = NULL;
2566
2567 if (!NT_STATUS_IS_OK(domain->last_status))
2568 return domain->last_status;
2569
2570 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2571 "for domain %s\n", domain->name ));
2572
2573 sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2574 if (sidlist == NULL) {
2575 return NT_STATUS_NO_MEMORY;
2576 }
2577
2578 status = domain->backend->lookup_useraliases(domain, mem_ctx,
2579 num_sids, sids,
2580 num_aliases, alias_rids);
2581
2582 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2583 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2584 if (!domain->internal && old_status) {
2585 set_domain_offline(domain);
2586 }
2587 if (!domain->internal &&
2588 !domain->online &&
2589 old_status) {
2590 NTSTATUS cache_status;
2591 cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2592 sids, num_aliases, alias_rids);
2593 return cache_status;
2594 }
2595 }
2596 /* and save it */
2597 refresh_sequence_number(domain, false);
2598 if (!NT_STATUS_IS_OK(status)) {
2599 return status;
2600 }
2601 centry = centry_start(domain, status);
2602 if (!centry)
2603 goto skip_save;
2604 centry_put_uint32(centry, *num_aliases);
2605 for (i=0; i<(*num_aliases); i++)
2606 centry_put_uint32(centry, (*alias_rids)[i]);
2607 centry_end(centry, "UA%s", sidlist);
2608 centry_free(centry);
2609
2610 skip_save:
2611 return status;
2612}
2613
2614NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2615 TALLOC_CTX *mem_ctx,
2616 const struct dom_sid *group_sid,
2617 uint32_t *num_names,
2618 struct dom_sid **sid_mem, char ***names,
2619 uint32_t **name_types)
2620{
2621 struct winbind_cache *cache = get_cache(domain);
2622 struct cache_entry *centry = NULL;
2623 NTSTATUS status;
2624 unsigned int i;
2625 char *sid_string;
2626
2627 if (cache->tdb == NULL) {
2628 return NT_STATUS_NOT_FOUND;
2629 }
2630
2631 sid_string = sid_string_tos(group_sid);
2632 if (sid_string == NULL) {
2633 return NT_STATUS_NO_MEMORY;
2634 }
2635
2636 centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2637 TALLOC_FREE(sid_string);
2638 if (centry == NULL) {
2639 return NT_STATUS_NOT_FOUND;
2640 }
2641
2642 *sid_mem = NULL;
2643 *names = NULL;
2644 *name_types = NULL;
2645
2646 *num_names = centry_uint32(centry);
2647 if (*num_names == 0) {
2648 centry_free(centry);
2649 return NT_STATUS_OK;
2650 }
2651
2652 *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2653 *names = talloc_array(mem_ctx, char *, *num_names);
2654 *name_types = talloc_array(mem_ctx, uint32, *num_names);
2655
2656 if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2657 TALLOC_FREE(*sid_mem);
2658 TALLOC_FREE(*names);
2659 TALLOC_FREE(*name_types);
2660 centry_free(centry);
2661 return NT_STATUS_NO_MEMORY;
2662 }
2663
2664 for (i=0; i<(*num_names); i++) {
2665 centry_sid(centry, &(*sid_mem)[i]);
2666 (*names)[i] = centry_string(centry, mem_ctx);
2667 (*name_types)[i] = centry_uint32(centry);
2668 }
2669
2670 status = centry->status;
2671
2672 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2673 "status: %s\n", domain->name, nt_errstr(status)));
2674
2675 centry_free(centry);
2676 return status;
2677}
2678
2679static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2680 TALLOC_CTX *mem_ctx,
2681 const struct dom_sid *group_sid,
2682 enum lsa_SidType type,
2683 uint32 *num_names,
2684 struct dom_sid **sid_mem, char ***names,
2685 uint32 **name_types)
2686{
2687 struct cache_entry *centry = NULL;
2688 NTSTATUS status;
2689 unsigned int i;
2690 fstring sid_string;
2691 bool old_status;
2692
2693 old_status = domain->online;
2694 status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2695 sid_mem, names, name_types);
2696 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2697 return status;
2698 }
2699
2700 (*num_names) = 0;
2701 (*sid_mem) = NULL;
2702 (*names) = NULL;
2703 (*name_types) = NULL;
2704
2705 /* Return status value returned by seq number check */
2706
2707 if (!NT_STATUS_IS_OK(domain->last_status))
2708 return domain->last_status;
2709
2710 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2711 domain->name ));
2712
2713 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2714 type, num_names,
2715 sid_mem, names, name_types);
2716
2717 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2718 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2719 if (!domain->internal && old_status) {
2720 set_domain_offline(domain);
2721 }
2722 if (!domain->internal &&
2723 !domain->online &&
2724 old_status) {
2725 NTSTATUS cache_status;
2726 cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2727 num_names, sid_mem, names,
2728 name_types);
2729 return cache_status;
2730 }
2731 }
2732 /* and save it */
2733 refresh_sequence_number(domain, false);
2734 if (!NT_STATUS_IS_OK(status)) {
2735 return status;
2736 }
2737 centry = centry_start(domain, status);
2738 if (!centry)
2739 goto skip_save;
2740 centry_put_uint32(centry, *num_names);
2741 for (i=0; i<(*num_names); i++) {
2742 centry_put_sid(centry, &(*sid_mem)[i]);
2743 centry_put_string(centry, (*names)[i]);
2744 centry_put_uint32(centry, (*name_types)[i]);
2745 }
2746 centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2747 centry_free(centry);
2748
2749skip_save:
2750 return status;
2751}
2752
2753/* find the sequence number for a domain */
2754static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2755{
2756 refresh_sequence_number(domain, false);
2757
2758 *seq = domain->sequence_number;
2759
2760 return NT_STATUS_OK;
2761}
2762
2763/* enumerate trusted domains
2764 * (we need to have the list of trustdoms in the cache when we go offline) -
2765 * Guenther */
2766static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2767 TALLOC_CTX *mem_ctx,
2768 struct netr_DomainTrustList *trusts)
2769{
2770 NTSTATUS status;
2771 struct winbind_cache *cache;
2772 struct winbindd_tdc_domain *dom_list = NULL;
2773 size_t num_domains = 0;
2774 bool retval = false;
2775 int i;
2776 bool old_status;
2777
2778 old_status = domain->online;
2779 trusts->count = 0;
2780 trusts->array = NULL;
2781
2782 cache = get_cache(domain);
2783 if (!cache || !cache->tdb) {
2784 goto do_query;
2785 }
2786
2787 if (domain->online) {
2788 goto do_query;
2789 }
2790
2791 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2792 if (!retval || !num_domains || !dom_list) {
2793 TALLOC_FREE(dom_list);
2794 goto do_query;
2795 }
2796
2797do_fetch_cache:
2798 trusts->array = TALLOC_ZERO_ARRAY(mem_ctx, struct netr_DomainTrust, num_domains);
2799 if (!trusts->array) {
2800 TALLOC_FREE(dom_list);
2801 return NT_STATUS_NO_MEMORY;
2802 }
2803
2804 for (i = 0; i < num_domains; i++) {
2805 struct netr_DomainTrust *trust;
2806 struct dom_sid *sid;
2807 struct winbindd_domain *dom;
2808
2809 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2810 if (dom && dom->internal) {
2811 continue;
2812 }
2813
2814 trust = &trusts->array[trusts->count];
2815 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2816 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2817 sid = talloc(trusts->array, struct dom_sid);
2818 if (!trust->netbios_name || !trust->dns_name ||
2819 !sid) {
2820 TALLOC_FREE(dom_list);
2821 TALLOC_FREE(trusts->array);
2822 return NT_STATUS_NO_MEMORY;
2823 }
2824
2825 trust->trust_flags = dom_list[i].trust_flags;
2826 trust->trust_attributes = dom_list[i].trust_attribs;
2827 trust->trust_type = dom_list[i].trust_type;
2828 sid_copy(sid, &dom_list[i].sid);
2829 trust->sid = sid;
2830 trusts->count++;
2831 }
2832
2833 TALLOC_FREE(dom_list);
2834 return NT_STATUS_OK;
2835
2836do_query:
2837 /* Return status value returned by seq number check */
2838
2839 if (!NT_STATUS_IS_OK(domain->last_status))
2840 return domain->last_status;
2841
2842 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2843 domain->name ));
2844
2845 status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2846
2847 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2848 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2849 if (!domain->internal && old_status) {
2850 set_domain_offline(domain);
2851 }
2852 if (!domain->internal &&
2853 !domain->online &&
2854 old_status) {
2855 retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2856 if (retval && num_domains && dom_list) {
2857 TALLOC_FREE(trusts->array);
2858 trusts->count = 0;
2859 goto do_fetch_cache;
2860 }
2861 }
2862 }
2863 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2864 * so that the generic centry handling still applies correctly -
2865 * Guenther*/
2866
2867 if (!NT_STATUS_IS_ERR(status)) {
2868 status = NT_STATUS_OK;
2869 }
2870 return status;
2871}
2872
2873/* get lockout policy */
2874static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2875 TALLOC_CTX *mem_ctx,
2876 struct samr_DomInfo12 *policy)
2877{
2878 struct winbind_cache *cache = get_cache(domain);
2879 struct cache_entry *centry = NULL;
2880 NTSTATUS status;
2881 bool old_status;
2882
2883 old_status = domain->online;
2884 if (!cache->tdb)
2885 goto do_query;
2886
2887 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2888
2889 if (!centry)
2890 goto do_query;
2891
2892do_fetch_cache:
2893 policy->lockout_duration = centry_nttime(centry);
2894 policy->lockout_window = centry_nttime(centry);
2895 policy->lockout_threshold = centry_uint16(centry);
2896
2897 status = centry->status;
2898
2899 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2900 domain->name, nt_errstr(status) ));
2901
2902 centry_free(centry);
2903 return status;
2904
2905do_query:
2906 ZERO_STRUCTP(policy);
2907
2908 /* Return status value returned by seq number check */
2909
2910 if (!NT_STATUS_IS_OK(domain->last_status))
2911 return domain->last_status;
2912
2913 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2914 domain->name ));
2915
2916 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2917
2918 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2919 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2920 if (!domain->internal && old_status) {
2921 set_domain_offline(domain);
2922 }
2923 if (cache->tdb &&
2924 !domain->internal &&
2925 !domain->online &&
2926 old_status) {
2927 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2928 if (centry) {
2929 goto do_fetch_cache;
2930 }
2931 }
2932 }
2933 /* and save it */
2934 refresh_sequence_number(domain, false);
2935 if (!NT_STATUS_IS_OK(status)) {
2936 return status;
2937 }
2938 wcache_save_lockout_policy(domain, status, policy);
2939
2940 return status;
2941}
2942
2943/* get password policy */
2944static NTSTATUS password_policy(struct winbindd_domain *domain,
2945 TALLOC_CTX *mem_ctx,
2946 struct samr_DomInfo1 *policy)
2947{
2948 struct winbind_cache *cache = get_cache(domain);
2949 struct cache_entry *centry = NULL;
2950 NTSTATUS status;
2951 bool old_status;
2952
2953 old_status = domain->online;
2954 if (!cache->tdb)
2955 goto do_query;
2956
2957 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2958
2959 if (!centry)
2960 goto do_query;
2961
2962do_fetch_cache:
2963 policy->min_password_length = centry_uint16(centry);
2964 policy->password_history_length = centry_uint16(centry);
2965 policy->password_properties = centry_uint32(centry);
2966 policy->max_password_age = centry_nttime(centry);
2967 policy->min_password_age = centry_nttime(centry);
2968
2969 status = centry->status;
2970
2971 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2972 domain->name, nt_errstr(status) ));
2973
2974 centry_free(centry);
2975 return status;
2976
2977do_query:
2978 ZERO_STRUCTP(policy);
2979
2980 /* Return status value returned by seq number check */
2981
2982 if (!NT_STATUS_IS_OK(domain->last_status))
2983 return domain->last_status;
2984
2985 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2986 domain->name ));
2987
2988 status = domain->backend->password_policy(domain, mem_ctx, policy);
2989
2990 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2991 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2992 if (!domain->internal && old_status) {
2993 set_domain_offline(domain);
2994 }
2995 if (cache->tdb &&
2996 !domain->internal &&
2997 !domain->online &&
2998 old_status) {
2999 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3000 if (centry) {
3001 goto do_fetch_cache;
3002 }
3003 }
3004 }
3005 /* and save it */
3006 refresh_sequence_number(domain, false);
3007 if (!NT_STATUS_IS_OK(status)) {
3008 return status;
3009 }
3010 wcache_save_password_policy(domain, status, policy);
3011
3012 return status;
3013}
3014
3015
3016/* Invalidate cached user and group lists coherently */
3017
3018static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3019 void *state)
3020{
3021 if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3022 strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3023 tdb_delete(the_tdb, kbuf);
3024
3025 return 0;
3026}
3027
3028/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3029
3030void wcache_invalidate_samlogon(struct winbindd_domain *domain,
3031 const struct dom_sid *sid)
3032{
3033 fstring key_str, sid_string;
3034 struct winbind_cache *cache;
3035
3036 /* dont clear cached U/SID and UG/SID entries when we want to logon
3037 * offline - gd */
3038
3039 if (lp_winbind_offline_logon()) {
3040 return;
3041 }
3042
3043 if (!domain)
3044 return;
3045
3046 cache = get_cache(domain);
3047
3048 if (!cache->tdb) {
3049 return;
3050 }
3051
3052 /* Clear U/SID cache entry */
3053 fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, sid));
3054 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3055 tdb_delete(cache->tdb, string_tdb_data(key_str));
3056
3057 /* Clear UG/SID cache entry */
3058 fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, sid));
3059 DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
3060 tdb_delete(cache->tdb, string_tdb_data(key_str));
3061
3062 /* Samba/winbindd never needs this. */
3063 netsamlogon_clear_cached_user(sid);
3064}
3065
3066bool wcache_invalidate_cache(void)
3067{
3068 struct winbindd_domain *domain;
3069
3070 for (domain = domain_list(); domain; domain = domain->next) {
3071 struct winbind_cache *cache = get_cache(domain);
3072
3073 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3074 "entries for %s\n", domain->name));
3075 if (cache) {
3076 if (cache->tdb) {
3077 tdb_traverse(cache->tdb, traverse_fn, NULL);
3078 } else {
3079 return false;
3080 }
3081 }
3082 }
3083 return true;
3084}
3085
3086bool wcache_invalidate_cache_noinit(void)
3087{
3088 struct winbindd_domain *domain;
3089
3090 for (domain = domain_list(); domain; domain = domain->next) {
3091 struct winbind_cache *cache;
3092
3093 /* Skip uninitialized domains. */
3094 if (!domain->initialized && !domain->internal) {
3095 continue;
3096 }
3097
3098 cache = get_cache(domain);
3099
3100 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
3101 "entries for %s\n", domain->name));
3102 if (cache) {
3103 if (cache->tdb) {
3104 tdb_traverse(cache->tdb, traverse_fn, NULL);
3105 /*
3106 * Flushing cache has nothing to with domains.
3107 * return here if we successfully flushed once.
3108 * To avoid unnecessary traversing the cache.
3109 */
3110 return true;
3111 } else {
3112 return false;
3113 }
3114 }
3115 }
3116 return true;
3117}
3118
3119bool init_wcache(void)
3120{
3121 if (wcache == NULL) {
3122 wcache = SMB_XMALLOC_P(struct winbind_cache);
3123 ZERO_STRUCTP(wcache);
3124 }
3125
3126 if (wcache->tdb != NULL)
3127 return true;
3128
3129 /* when working offline we must not clear the cache on restart */
3130 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3131 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3132 TDB_INCOMPATIBLE_HASH |
3133 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3134 O_RDWR|O_CREAT, 0600);
3135
3136 if (wcache->tdb == NULL) {
3137 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3138 return false;
3139 }
3140
3141 return true;
3142}
3143
3144/************************************************************************
3145 This is called by the parent to initialize the cache file.
3146 We don't need sophisticated locking here as we know we're the
3147 only opener.
3148************************************************************************/
3149
3150bool initialize_winbindd_cache(void)
3151{
3152 bool cache_bad = true;
3153 uint32 vers;
3154
3155 if (!init_wcache()) {
3156 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
3157 return false;
3158 }
3159
3160 /* Check version number. */
3161 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
3162 vers == WINBINDD_CACHE_VERSION) {
3163 cache_bad = false;
3164 }
3165
3166 if (cache_bad) {
3167 DEBUG(0,("initialize_winbindd_cache: clearing cache "
3168 "and re-creating with version number %d\n",
3169 WINBINDD_CACHE_VERSION ));
3170
3171 tdb_close(wcache->tdb);
3172 wcache->tdb = NULL;
3173
3174 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
3175 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
3176 cache_path("winbindd_cache.tdb"),
3177 strerror(errno) ));
3178 return false;
3179 }
3180 if (!init_wcache()) {
3181 DEBUG(0,("initialize_winbindd_cache: re-initialization "
3182 "init_wcache failed.\n"));
3183 return false;
3184 }
3185
3186 /* Write the version. */
3187 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
3188 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
3189 tdb_errorstr(wcache->tdb) ));
3190 return false;
3191 }
3192 }
3193
3194 tdb_close(wcache->tdb);
3195 wcache->tdb = NULL;
3196 return true;
3197}
3198
3199void close_winbindd_cache(void)
3200{
3201 if (!wcache) {
3202 return;
3203 }
3204 if (wcache->tdb) {
3205 tdb_close(wcache->tdb);
3206 wcache->tdb = NULL;
3207 }
3208}
3209
3210bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
3211 char **domain_name, char **name,
3212 enum lsa_SidType *type)
3213{
3214 struct winbindd_domain *domain;
3215 NTSTATUS status;
3216
3217 domain = find_lookup_domain_from_sid(sid);
3218 if (domain == NULL) {
3219 return false;
3220 }
3221 status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
3222 type);
3223 return NT_STATUS_IS_OK(status);
3224}
3225
3226bool lookup_cached_name(const char *domain_name,
3227 const char *name,
3228 struct dom_sid *sid,
3229 enum lsa_SidType *type)
3230{
3231 struct winbindd_domain *domain;
3232 NTSTATUS status;
3233 bool original_online_state;
3234
3235 domain = find_lookup_domain_from_name(domain_name);
3236 if (domain == NULL) {
3237 return false;
3238 }
3239
3240 /* If we are doing a cached logon, temporarily set the domain
3241 offline so the cache won't expire the entry */
3242
3243 original_online_state = domain->online;
3244 domain->online = false;
3245 status = wcache_name_to_sid(domain, domain_name, name, sid, type);
3246 domain->online = original_online_state;
3247
3248 return NT_STATUS_IS_OK(status);
3249}
3250
3251void cache_name2sid(struct winbindd_domain *domain,
3252 const char *domain_name, const char *name,
3253 enum lsa_SidType type, const struct dom_sid *sid)
3254{
3255 refresh_sequence_number(domain, false);
3256 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
3257 sid, type);
3258}
3259
3260/*
3261 * The original idea that this cache only contains centries has
3262 * been blurred - now other stuff gets put in here. Ensure we
3263 * ignore these things on cleanup.
3264 */
3265
3266static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
3267 TDB_DATA dbuf, void *state)
3268{
3269 struct cache_entry *centry;
3270
3271 if (is_non_centry_key(kbuf)) {
3272 return 0;
3273 }
3274
3275 centry = wcache_fetch_raw((char *)kbuf.dptr);
3276 if (!centry) {
3277 return 0;
3278 }
3279
3280 if (!NT_STATUS_IS_OK(centry->status)) {
3281 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
3282 tdb_delete(the_tdb, kbuf);
3283 }
3284
3285 centry_free(centry);
3286 return 0;
3287}
3288
3289/* flush the cache */
3290void wcache_flush_cache(void)
3291{
3292 if (!wcache)
3293 return;
3294 if (wcache->tdb) {
3295 tdb_close(wcache->tdb);
3296 wcache->tdb = NULL;
3297 }
3298 if (!winbindd_use_cache()) {
3299 return;
3300 }
3301
3302 /* when working offline we must not clear the cache on restart */
3303 wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
3304 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3305 TDB_INCOMPATIBLE_HASH |
3306 (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
3307 O_RDWR|O_CREAT, 0600);
3308
3309 if (!wcache->tdb) {
3310 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
3311 return;
3312 }
3313
3314 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
3315
3316 DEBUG(10,("wcache_flush_cache success\n"));
3317}
3318
3319/* Count cached creds */
3320
3321static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3322 void *state)
3323{
3324 int *cred_count = (int*)state;
3325
3326 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3327 (*cred_count)++;
3328 }
3329 return 0;
3330}
3331
3332NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
3333{
3334 struct winbind_cache *cache = get_cache(domain);
3335
3336 *count = 0;
3337
3338 if (!cache->tdb) {
3339 return NT_STATUS_INTERNAL_DB_ERROR;
3340 }
3341
3342 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
3343
3344 return NT_STATUS_OK;
3345}
3346
3347struct cred_list {
3348 struct cred_list *prev, *next;
3349 TDB_DATA key;
3350 fstring name;
3351 time_t created;
3352};
3353static struct cred_list *wcache_cred_list;
3354
3355static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
3356 void *state)
3357{
3358 struct cred_list *cred;
3359
3360 if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
3361
3362 cred = SMB_MALLOC_P(struct cred_list);
3363 if (cred == NULL) {
3364 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
3365 return -1;
3366 }
3367
3368 ZERO_STRUCTP(cred);
3369
3370 /* save a copy of the key */
3371
3372 fstrcpy(cred->name, (const char *)kbuf.dptr);
3373 DLIST_ADD(wcache_cred_list, cred);
3374 }
3375
3376 return 0;
3377}
3378
3379NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
3380{
3381 struct winbind_cache *cache = get_cache(domain);
3382 NTSTATUS status;
3383 int ret;
3384 struct cred_list *cred, *oldest = NULL;
3385
3386 if (!cache->tdb) {
3387 return NT_STATUS_INTERNAL_DB_ERROR;
3388 }
3389
3390 /* we possibly already have an entry */
3391 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
3392
3393 fstring key_str, tmp;
3394
3395 DEBUG(11,("we already have an entry, deleting that\n"));
3396
3397 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
3398
3399 tdb_delete(cache->tdb, string_tdb_data(key_str));
3400
3401 return NT_STATUS_OK;
3402 }
3403
3404 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
3405 if (ret == 0) {
3406 return NT_STATUS_OK;
3407 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
3408 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3409 }
3410
3411 ZERO_STRUCTP(oldest);
3412
3413 for (cred = wcache_cred_list; cred; cred = cred->next) {
3414
3415 TDB_DATA data;
3416 time_t t;
3417
3418 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
3419 if (!data.dptr) {
3420 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
3421 cred->name));
3422 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3423 goto done;
3424 }
3425
3426 t = IVAL(data.dptr, 0);
3427 SAFE_FREE(data.dptr);
3428
3429 if (!oldest) {
3430 oldest = SMB_MALLOC_P(struct cred_list);
3431 if (oldest == NULL) {
3432 status = NT_STATUS_NO_MEMORY;
3433 goto done;
3434 }
3435
3436 fstrcpy(oldest->name, cred->name);
3437 oldest->created = t;
3438 continue;
3439 }
3440
3441 if (t < oldest->created) {
3442 fstrcpy(oldest->name, cred->name);
3443 oldest->created = t;
3444 }
3445 }
3446
3447 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3448 status = NT_STATUS_OK;
3449 } else {
3450 status = NT_STATUS_UNSUCCESSFUL;
3451 }
3452done:
3453 SAFE_FREE(wcache_cred_list);
3454 SAFE_FREE(oldest);
3455
3456 return status;
3457}
3458
3459/* Change the global online/offline state. */
3460bool set_global_winbindd_state_offline(void)
3461{
3462 TDB_DATA data;
3463
3464 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3465
3466 /* Only go offline if someone has created
3467 the key "WINBINDD_OFFLINE" in the cache tdb. */
3468
3469 if (wcache == NULL || wcache->tdb == NULL) {
3470 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3471 return false;
3472 }
3473
3474 if (!lp_winbind_offline_logon()) {
3475 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3476 return false;
3477 }
3478
3479 if (global_winbindd_offline_state) {
3480 /* Already offline. */
3481 return true;
3482 }
3483
3484 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3485
3486 if (!data.dptr || data.dsize != 4) {
3487 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3488 SAFE_FREE(data.dptr);
3489 return false;
3490 } else {
3491 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3492 global_winbindd_offline_state = true;
3493 SAFE_FREE(data.dptr);
3494 return true;
3495 }
3496}
3497
3498void set_global_winbindd_state_online(void)
3499{
3500 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3501
3502 if (!lp_winbind_offline_logon()) {
3503 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3504 return;
3505 }
3506
3507 if (!global_winbindd_offline_state) {
3508 /* Already online. */
3509 return;
3510 }
3511 global_winbindd_offline_state = false;
3512
3513 if (!wcache->tdb) {
3514 return;
3515 }
3516
3517 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3518 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3519}
3520
3521bool get_global_winbindd_state_offline(void)
3522{
3523 return global_winbindd_offline_state;
3524}
3525
3526/***********************************************************************
3527 Validate functions for all possible cache tdb keys.
3528***********************************************************************/
3529
3530static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
3531 struct tdb_validation_status *state)
3532{
3533 struct cache_entry *centry;
3534
3535 centry = SMB_XMALLOC_P(struct cache_entry);
3536 centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3537 if (!centry->data) {
3538 SAFE_FREE(centry);
3539 return NULL;
3540 }
3541 centry->len = data.dsize;
3542 centry->ofs = 0;
3543
3544 if (centry->len < 16) {
3545 /* huh? corrupt cache? */
3546 DEBUG(0,("create_centry_validate: Corrupt cache for key %s "
3547 "(len < 16) ?\n", kstr));
3548 centry_free(centry);
3549 state->bad_entry = true;
3550 state->success = false;
3551 return NULL;
3552 }
3553
3554 centry->status = NT_STATUS(centry_uint32(centry));
3555 centry->sequence_number = centry_uint32(centry);
3556 centry->timeout = centry_uint64_t(centry);
3557 return centry;
3558}
3559
3560static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3561 struct tdb_validation_status *state)
3562{
3563 if (dbuf.dsize != 8) {
3564 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3565 keystr, (unsigned int)dbuf.dsize ));
3566 state->bad_entry = true;
3567 return 1;
3568 }
3569 return 0;
3570}
3571
3572static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3573 struct tdb_validation_status *state)
3574{
3575 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3576 if (!centry) {
3577 return 1;
3578 }
3579
3580 (void)centry_uint32(centry);
3581 if (NT_STATUS_IS_OK(centry->status)) {
3582 struct dom_sid sid;
3583 (void)centry_sid(centry, &sid);
3584 }
3585
3586 centry_free(centry);
3587
3588 if (!(state->success)) {
3589 return 1;
3590 }
3591 DEBUG(10,("validate_ns: %s ok\n", keystr));
3592 return 0;
3593}
3594
3595static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3596 struct tdb_validation_status *state)
3597{
3598 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3599 if (!centry) {
3600 return 1;
3601 }
3602
3603 if (NT_STATUS_IS_OK(centry->status)) {
3604 (void)centry_uint32(centry);
3605 (void)centry_string(centry, mem_ctx);
3606 (void)centry_string(centry, mem_ctx);
3607 }
3608
3609 centry_free(centry);
3610
3611 if (!(state->success)) {
3612 return 1;
3613 }
3614 DEBUG(10,("validate_sn: %s ok\n", keystr));
3615 return 0;
3616}
3617
3618static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3619 struct tdb_validation_status *state)
3620{
3621 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3622 struct dom_sid sid;
3623
3624 if (!centry) {
3625 return 1;
3626 }
3627
3628 (void)centry_string(centry, mem_ctx);
3629 (void)centry_string(centry, mem_ctx);
3630 (void)centry_string(centry, mem_ctx);
3631 (void)centry_string(centry, mem_ctx);
3632 (void)centry_uint32(centry);
3633 (void)centry_sid(centry, &sid);
3634 (void)centry_sid(centry, &sid);
3635
3636 centry_free(centry);
3637
3638 if (!(state->success)) {
3639 return 1;
3640 }
3641 DEBUG(10,("validate_u: %s ok\n", keystr));
3642 return 0;
3643}
3644
3645static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3646 struct tdb_validation_status *state)
3647{
3648 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3649
3650 if (!centry) {
3651 return 1;
3652 }
3653
3654 (void)centry_nttime(centry);
3655 (void)centry_nttime(centry);
3656 (void)centry_uint16(centry);
3657
3658 centry_free(centry);
3659
3660 if (!(state->success)) {
3661 return 1;
3662 }
3663 DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3664 return 0;
3665}
3666
3667static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3668 struct tdb_validation_status *state)
3669{
3670 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3671
3672 if (!centry) {
3673 return 1;
3674 }
3675
3676 (void)centry_uint16(centry);
3677 (void)centry_uint16(centry);
3678 (void)centry_uint32(centry);
3679 (void)centry_nttime(centry);
3680 (void)centry_nttime(centry);
3681
3682 centry_free(centry);
3683
3684 if (!(state->success)) {
3685 return 1;
3686 }
3687 DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3688 return 0;
3689}
3690
3691static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3692 struct tdb_validation_status *state)
3693{
3694 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3695
3696 if (!centry) {
3697 return 1;
3698 }
3699
3700 (void)centry_time(centry);
3701 (void)centry_hash16(centry, mem_ctx);
3702
3703 /* We only have 17 bytes more data in the salted cred case. */
3704 if (centry->len - centry->ofs == 17) {
3705 (void)centry_hash16(centry, mem_ctx);
3706 }
3707
3708 centry_free(centry);
3709
3710 if (!(state->success)) {
3711 return 1;
3712 }
3713 DEBUG(10,("validate_cred: %s ok\n", keystr));
3714 return 0;
3715}
3716
3717static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3718 struct tdb_validation_status *state)
3719{
3720 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3721 int32 num_entries, i;
3722
3723 if (!centry) {
3724 return 1;
3725 }
3726
3727 num_entries = (int32)centry_uint32(centry);
3728
3729 for (i=0; i< num_entries; i++) {
3730 struct dom_sid sid;
3731 (void)centry_string(centry, mem_ctx);
3732 (void)centry_string(centry, mem_ctx);
3733 (void)centry_string(centry, mem_ctx);
3734 (void)centry_string(centry, mem_ctx);
3735 (void)centry_sid(centry, &sid);
3736 (void)centry_sid(centry, &sid);
3737 }
3738
3739 centry_free(centry);
3740
3741 if (!(state->success)) {
3742 return 1;
3743 }
3744 DEBUG(10,("validate_ul: %s ok\n", keystr));
3745 return 0;
3746}
3747
3748static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3749 struct tdb_validation_status *state)
3750{
3751 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3752 int32 num_entries, i;
3753
3754 if (!centry) {
3755 return 1;
3756 }
3757
3758 num_entries = centry_uint32(centry);
3759
3760 for (i=0; i< num_entries; i++) {
3761 (void)centry_string(centry, mem_ctx);
3762 (void)centry_string(centry, mem_ctx);
3763 (void)centry_uint32(centry);
3764 }
3765
3766 centry_free(centry);
3767
3768 if (!(state->success)) {
3769 return 1;
3770 }
3771 DEBUG(10,("validate_gl: %s ok\n", keystr));
3772 return 0;
3773}
3774
3775static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3776 struct tdb_validation_status *state)
3777{
3778 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3779 int32 num_groups, i;
3780
3781 if (!centry) {
3782 return 1;
3783 }
3784
3785 num_groups = centry_uint32(centry);
3786
3787 for (i=0; i< num_groups; i++) {
3788 struct dom_sid sid;
3789 centry_sid(centry, &sid);
3790 }
3791
3792 centry_free(centry);
3793
3794 if (!(state->success)) {
3795 return 1;
3796 }
3797 DEBUG(10,("validate_ug: %s ok\n", keystr));
3798 return 0;
3799}
3800
3801static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3802 struct tdb_validation_status *state)
3803{
3804 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3805 int32 num_aliases, i;
3806
3807 if (!centry) {
3808 return 1;
3809 }
3810
3811 num_aliases = centry_uint32(centry);
3812
3813 for (i=0; i < num_aliases; i++) {
3814 (void)centry_uint32(centry);
3815 }
3816
3817 centry_free(centry);
3818
3819 if (!(state->success)) {
3820 return 1;
3821 }
3822 DEBUG(10,("validate_ua: %s ok\n", keystr));
3823 return 0;
3824}
3825
3826static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3827 struct tdb_validation_status *state)
3828{
3829 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3830 int32 num_names, i;
3831
3832 if (!centry) {
3833 return 1;
3834 }
3835
3836 num_names = centry_uint32(centry);
3837
3838 for (i=0; i< num_names; i++) {
3839 struct dom_sid sid;
3840 centry_sid(centry, &sid);
3841 (void)centry_string(centry, mem_ctx);
3842 (void)centry_uint32(centry);
3843 }
3844
3845 centry_free(centry);
3846
3847 if (!(state->success)) {
3848 return 1;
3849 }
3850 DEBUG(10,("validate_gm: %s ok\n", keystr));
3851 return 0;
3852}
3853
3854static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3855 struct tdb_validation_status *state)
3856{
3857 /* Can't say anything about this other than must be nonzero. */
3858 if (dbuf.dsize == 0) {
3859 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3860 keystr));
3861 state->bad_entry = true;
3862 state->success = false;
3863 return 1;
3864 }
3865
3866 DEBUG(10,("validate_dr: %s ok\n", keystr));
3867 return 0;
3868}
3869
3870static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3871 struct tdb_validation_status *state)
3872{
3873 /* Can't say anything about this other than must be nonzero. */
3874 if (dbuf.dsize == 0) {
3875 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3876 keystr));
3877 state->bad_entry = true;
3878 state->success = false;
3879 return 1;
3880 }
3881
3882 DEBUG(10,("validate_de: %s ok\n", keystr));
3883 return 0;
3884}
3885
3886static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3887 TDB_DATA dbuf, struct tdb_validation_status *state)
3888{
3889 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3890
3891 if (!centry) {
3892 return 1;
3893 }
3894
3895 (void)centry_string(centry, mem_ctx);
3896 (void)centry_string(centry, mem_ctx);
3897 (void)centry_string(centry, mem_ctx);
3898 (void)centry_uint32(centry);
3899
3900 centry_free(centry);
3901
3902 if (!(state->success)) {
3903 return 1;
3904 }
3905 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3906 return 0;
3907}
3908
3909static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3910 TDB_DATA dbuf,
3911 struct tdb_validation_status *state)
3912{
3913 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3914
3915 if (!centry) {
3916 return 1;
3917 }
3918
3919 (void)centry_string( centry, mem_ctx );
3920
3921 centry_free(centry);
3922
3923 if (!(state->success)) {
3924 return 1;
3925 }
3926 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3927 return 0;
3928}
3929
3930static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3931 TDB_DATA dbuf,
3932 struct tdb_validation_status *state)
3933{
3934 struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3935
3936 if (!centry) {
3937 return 1;
3938 }
3939
3940 (void)centry_string( centry, mem_ctx );
3941
3942 centry_free(centry);
3943
3944 if (!(state->success)) {
3945 return 1;
3946 }
3947 DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3948 return 0;
3949}
3950
3951static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
3952 TDB_DATA dbuf,
3953 struct tdb_validation_status *state)
3954{
3955 if (dbuf.dsize == 0) {
3956 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3957 "key %s (len ==0) ?\n", keystr));
3958 state->bad_entry = true;
3959 state->success = false;
3960 return 1;
3961 }
3962
3963 DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
3964 DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
3965 return 0;
3966}
3967
3968static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3969 struct tdb_validation_status *state)
3970{
3971 if (dbuf.dsize != 4) {
3972 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3973 keystr, (unsigned int)dbuf.dsize ));
3974 state->bad_entry = true;
3975 state->success = false;
3976 return 1;
3977 }
3978 DEBUG(10,("validate_offline: %s ok\n", keystr));
3979 return 0;
3980}
3981
3982static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3983 struct tdb_validation_status *state)
3984{
3985 /*
3986 * Ignore validation for now. The proper way to do this is with a
3987 * checksum. Just pure parsing does not really catch much.
3988 */
3989 return 0;
3990}
3991
3992static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3993 struct tdb_validation_status *state)
3994{
3995 if (dbuf.dsize != 4) {
3996 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3997 "key %s (len %u != 4) ?\n",
3998 keystr, (unsigned int)dbuf.dsize));
3999 state->bad_entry = true;
4000 state->success = false;
4001 return 1;
4002 }
4003
4004 DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
4005 return 0;
4006}
4007
4008/***********************************************************************
4009 A list of all possible cache tdb keys with associated validation
4010 functions.
4011***********************************************************************/
4012
4013struct key_val_struct {
4014 const char *keyname;
4015 int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
4016} key_val[] = {
4017 {"SEQNUM/", validate_seqnum},
4018 {"NS/", validate_ns},
4019 {"SN/", validate_sn},
4020 {"U/", validate_u},
4021 {"LOC_POL/", validate_loc_pol},
4022 {"PWD_POL/", validate_pwd_pol},
4023 {"CRED/", validate_cred},
4024 {"UL/", validate_ul},
4025 {"GL/", validate_gl},
4026 {"UG/", validate_ug},
4027 {"UA", validate_ua},
4028 {"GM/", validate_gm},
4029 {"DR/", validate_dr},
4030 {"DE/", validate_de},
4031 {"NSS/PWINFO/", validate_pwinfo},
4032 {"TRUSTDOMCACHE/", validate_trustdomcache},
4033 {"NSS/NA/", validate_nss_na},
4034 {"NSS/AN/", validate_nss_an},
4035 {"WINBINDD_OFFLINE", validate_offline},
4036 {"NDR/", validate_ndr},
4037 {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
4038 {NULL, NULL}
4039};
4040
4041/***********************************************************************
4042 Function to look at every entry in the tdb and validate it as far as
4043 possible.
4044***********************************************************************/
4045
4046static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
4047{
4048 int i;
4049 unsigned int max_key_len = 1024;
4050 struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
4051
4052 /* Paranoia check. */
4053 if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
4054 max_key_len = 1024 * 1024;
4055 }
4056 if (kbuf.dsize > max_key_len) {
4057 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
4058 "(%u) > (%u)\n\n",
4059 (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
4060 return 1;
4061 }
4062
4063 for (i = 0; key_val[i].keyname; i++) {
4064 size_t namelen = strlen(key_val[i].keyname);
4065 if (kbuf.dsize >= namelen && (
4066 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
4067 TALLOC_CTX *mem_ctx;
4068 char *keystr;
4069 int ret;
4070
4071 keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
4072 if (!keystr) {
4073 return 1;
4074 }
4075 memcpy(keystr, kbuf.dptr, kbuf.dsize);
4076 keystr[kbuf.dsize] = '\0';
4077
4078 mem_ctx = talloc_init("validate_ctx");
4079 if (!mem_ctx) {
4080 SAFE_FREE(keystr);
4081 return 1;
4082 }
4083
4084 ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
4085 v_state);
4086
4087 SAFE_FREE(keystr);
4088 talloc_destroy(mem_ctx);
4089 return ret;
4090 }
4091 }
4092
4093 DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
4094 dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
4095 DEBUG(0,("data :\n"));
4096 dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
4097 v_state->unknown_key = true;
4098 v_state->success = false;
4099 return 1; /* terminate. */
4100}
4101
4102static void validate_panic(const char *const why)
4103{
4104 DEBUG(0,("validating cache: would panic %s\n", why ));
4105 DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
4106 exit(47);
4107}
4108
4109static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
4110 TDB_DATA key,
4111 TDB_DATA data,
4112 void *state)
4113{
4114 uint64_t ctimeout;
4115 TDB_DATA blob;
4116
4117 if (is_non_centry_key(key)) {
4118 return 0;
4119 }
4120
4121 if (data.dptr == NULL || data.dsize == 0) {
4122 if (tdb_delete(tdb, key) < 0) {
4123 DEBUG(0, ("tdb_delete for [%s] failed!\n",
4124 key.dptr));
4125 return 1;
4126 }
4127 }
4128
4129 /* add timeout to blob (uint64_t) */
4130 blob.dsize = data.dsize + 8;
4131
4132 blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
4133 if (blob.dptr == NULL) {
4134 return 1;
4135 }
4136 memset(blob.dptr, 0, blob.dsize);
4137
4138 /* copy status and seqnum */
4139 memcpy(blob.dptr, data.dptr, 8);
4140
4141 /* add timeout */
4142 ctimeout = lp_winbind_cache_time() + time(NULL);
4143 SBVAL(blob.dptr, 8, ctimeout);
4144
4145 /* copy the rest */
4146 memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
4147
4148 if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
4149 DEBUG(0, ("tdb_store to update [%s] failed!\n",
4150 key.dptr));
4151 SAFE_FREE(blob.dptr);
4152 return 1;
4153 }
4154
4155 SAFE_FREE(blob.dptr);
4156 return 0;
4157}
4158
4159static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
4160{
4161 int rc;
4162
4163 DEBUG(1, ("Upgrade to version 2 of the winbindd_cache.tdb\n"));
4164
4165 rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
4166 if (rc < 0) {
4167 return false;
4168 }
4169
4170 return true;
4171}
4172
4173/***********************************************************************
4174 Try and validate every entry in the winbindd cache. If we fail here,
4175 delete the cache tdb and return non-zero.
4176***********************************************************************/
4177
4178int winbindd_validate_cache(void)
4179{
4180 int ret = -1;
4181 const char *tdb_path = cache_path("winbindd_cache.tdb");
4182 TDB_CONTEXT *tdb = NULL;
4183 uint32_t vers_id;
4184 bool ok;
4185
4186 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4187 smb_panic_fn = validate_panic;
4188
4189 tdb = tdb_open_log(tdb_path,
4190 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
4191 TDB_INCOMPATIBLE_HASH |
4192 ( lp_winbind_offline_logon()
4193 ? TDB_DEFAULT
4194 : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
4195 O_RDWR|O_CREAT,
4196 0600);
4197 if (!tdb) {
4198 DEBUG(0, ("winbindd_validate_cache: "
4199 "error opening/initializing tdb\n"));
4200 goto done;
4201 }
4202
4203 /* Version check and upgrade code. */
4204 if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
4205 DEBUG(10, ("Fresh database\n"));
4206 tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
4207 vers_id = WINBINDD_CACHE_VERSION;
4208 }
4209
4210 if (vers_id != WINBINDD_CACHE_VERSION) {
4211 if (vers_id == WINBINDD_CACHE_VER1) {
4212 ok = wbcache_upgrade_v1_to_v2(tdb);
4213 if (!ok) {
4214 DEBUG(10, ("winbindd_validate_cache: upgrade to version 2 failed.\n"));
4215 unlink(tdb_path);
4216 goto done;
4217 }
4218
4219 tdb_store_uint32(tdb,
4220 WINBINDD_CACHE_VERSION_KEYSTR,
4221 WINBINDD_CACHE_VERSION);
4222 vers_id = WINBINDD_CACHE_VER2;
4223 }
4224 }
4225
4226 tdb_close(tdb);
4227
4228 ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
4229
4230 if (ret != 0) {
4231 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
4232 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
4233 unlink(tdb_path);
4234 }
4235
4236done:
4237 DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
4238 smb_panic_fn = smb_panic;
4239 return ret;
4240}
4241
4242/***********************************************************************
4243 Try and validate every entry in the winbindd cache.
4244***********************************************************************/
4245
4246int winbindd_validate_cache_nobackup(void)
4247{
4248 int ret = -1;
4249 const char *tdb_path = cache_path("winbindd_cache.tdb");
4250
4251 DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
4252 smb_panic_fn = validate_panic;
4253
4254
4255 if (wcache == NULL || wcache->tdb == NULL) {
4256 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
4257 } else {
4258 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
4259 }
4260
4261 if (ret != 0) {
4262 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
4263 "successful.\n"));
4264 }
4265
4266 DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
4267 "function\n"));
4268 smb_panic_fn = smb_panic;
4269 return ret;
4270}
4271
4272bool winbindd_cache_validate_and_initialize(void)
4273{
4274 close_winbindd_cache();
4275
4276 if (lp_winbind_offline_logon()) {
4277 if (winbindd_validate_cache() < 0) {
4278 DEBUG(0, ("winbindd cache tdb corrupt and no backup "
4279 "could be restored.\n"));
4280 }
4281 }
4282
4283 return initialize_winbindd_cache();
4284}
4285
4286/*********************************************************************
4287 ********************************************************************/
4288
4289static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
4290 struct winbindd_tdc_domain **domains,
4291 size_t *num_domains )
4292{
4293 struct winbindd_tdc_domain *list = NULL;
4294 size_t idx;
4295 int i;
4296 bool set_only = false;
4297
4298 /* don't allow duplicates */
4299
4300 idx = *num_domains;
4301 list = *domains;
4302
4303 for ( i=0; i< (*num_domains); i++ ) {
4304 if ( strequal( new_dom->name, list[i].domain_name ) ) {
4305 DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
4306 new_dom->name));
4307 idx = i;
4308 set_only = true;
4309
4310 break;
4311 }
4312 }
4313
4314 if ( !set_only ) {
4315 if ( !*domains ) {
4316 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
4317 idx = 0;
4318 } else {
4319 list = TALLOC_REALLOC_ARRAY( *domains, *domains,
4320 struct winbindd_tdc_domain,
4321 (*num_domains)+1);
4322 idx = *num_domains;
4323 }
4324
4325 ZERO_STRUCT( list[idx] );
4326 }
4327
4328 if ( !list )
4329 return false;
4330
4331 list[idx].domain_name = talloc_strdup( list, new_dom->name );
4332 list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
4333
4334 if ( !is_null_sid( &new_dom->sid ) ) {
4335 sid_copy( &list[idx].sid, &new_dom->sid );
4336 } else {
4337 sid_copy(&list[idx].sid, &global_sid_NULL);
4338 }
4339
4340 if ( new_dom->domain_flags != 0x0 )
4341 list[idx].trust_flags = new_dom->domain_flags;
4342
4343 if ( new_dom->domain_type != 0x0 )
4344 list[idx].trust_type = new_dom->domain_type;
4345
4346 if ( new_dom->domain_trust_attribs != 0x0 )
4347 list[idx].trust_attribs = new_dom->domain_trust_attribs;
4348
4349 if ( !set_only ) {
4350 *domains = list;
4351 *num_domains = idx + 1;
4352 }
4353
4354 return true;
4355}
4356
4357/*********************************************************************
4358 ********************************************************************/
4359
4360static TDB_DATA make_tdc_key( const char *domain_name )
4361{
4362 char *keystr = NULL;
4363 TDB_DATA key = { NULL, 0 };
4364
4365 if ( !domain_name ) {
4366 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
4367 return key;
4368 }
4369
4370 if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
4371 return key;
4372 }
4373 key = string_term_tdb_data(keystr);
4374
4375 return key;
4376}
4377
4378/*********************************************************************
4379 ********************************************************************/
4380
4381static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
4382 size_t num_domains,
4383 unsigned char **buf )
4384{
4385 unsigned char *buffer = NULL;
4386 int len = 0;
4387 int buflen = 0;
4388 int i = 0;
4389
4390 DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
4391 (int)num_domains));
4392
4393 buflen = 0;
4394
4395 again:
4396 len = 0;
4397
4398 /* Store the number of array items first */
4399 len += tdb_pack( buffer+len, buflen-len, "d",
4400 num_domains );
4401
4402 /* now pack each domain trust record */
4403 for ( i=0; i<num_domains; i++ ) {
4404
4405 fstring tmp;
4406
4407 if ( buflen > 0 ) {
4408 DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
4409 domains[i].domain_name,
4410 domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
4411 }
4412
4413 len += tdb_pack( buffer+len, buflen-len, "fffddd",
4414 domains[i].domain_name,
4415 domains[i].dns_name,
4416 sid_to_fstring(tmp, &domains[i].sid),
4417 domains[i].trust_flags,
4418 domains[i].trust_attribs,
4419 domains[i].trust_type );
4420 }
4421
4422 if ( buflen < len ) {
4423 SAFE_FREE(buffer);
4424 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
4425 DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
4426 buflen = -1;
4427 goto done;
4428 }
4429 buflen = len;
4430 goto again;
4431 }
4432
4433 *buf = buffer;
4434
4435 done:
4436 return buflen;
4437}
4438
4439/*********************************************************************
4440 ********************************************************************/
4441
4442static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
4443 struct winbindd_tdc_domain **domains )
4444{
4445 fstring domain_name, dns_name, sid_string;
4446 uint32 type, attribs, flags;
4447 int num_domains;
4448 int len = 0;
4449 int i;
4450 struct winbindd_tdc_domain *list = NULL;
4451
4452 /* get the number of domains */
4453 len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
4454 if ( len == -1 ) {
4455 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4456 return 0;
4457 }
4458
4459 list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
4460 if ( !list ) {
4461 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
4462 return 0;
4463 }
4464
4465 for ( i=0; i<num_domains; i++ ) {
4466 len += tdb_unpack( buf+len, buflen-len, "fffddd",
4467 domain_name,
4468 dns_name,
4469 sid_string,
4470 &flags,
4471 &attribs,
4472 &type );
4473
4474 if ( len == -1 ) {
4475 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
4476 TALLOC_FREE( list );
4477 return 0;
4478 }
4479
4480 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
4481 "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
4482 domain_name, dns_name, sid_string,
4483 flags, attribs, type));
4484
4485 list[i].domain_name = talloc_strdup( list, domain_name );
4486 list[i].dns_name = talloc_strdup( list, dns_name );
4487 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
4488 DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
4489 domain_name));
4490 }
4491 list[i].trust_flags = flags;
4492 list[i].trust_attribs = attribs;
4493 list[i].trust_type = type;
4494 }
4495
4496 *domains = list;
4497
4498 return num_domains;
4499}
4500
4501/*********************************************************************
4502 ********************************************************************/
4503
4504static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4505{
4506 TDB_DATA key = make_tdc_key( lp_workgroup() );
4507 TDB_DATA data = { NULL, 0 };
4508 int ret;
4509
4510 if ( !key.dptr )
4511 return false;
4512
4513 /* See if we were asked to delete the cache entry */
4514
4515 if ( !domains ) {
4516 ret = tdb_delete( wcache->tdb, key );
4517 goto done;
4518 }
4519
4520 data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4521
4522 if ( !data.dptr ) {
4523 ret = -1;
4524 goto done;
4525 }
4526
4527 ret = tdb_store( wcache->tdb, key, data, 0 );
4528
4529 done:
4530 SAFE_FREE( data.dptr );
4531 SAFE_FREE( key.dptr );
4532
4533 return ( ret != -1 );
4534}
4535
4536/*********************************************************************
4537 ********************************************************************/
4538
4539bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4540{
4541 TDB_DATA key = make_tdc_key( lp_workgroup() );
4542 TDB_DATA data = { NULL, 0 };
4543
4544 *domains = NULL;
4545 *num_domains = 0;
4546
4547 if ( !key.dptr )
4548 return false;
4549
4550 data = tdb_fetch( wcache->tdb, key );
4551
4552 SAFE_FREE( key.dptr );
4553
4554 if ( !data.dptr )
4555 return false;
4556
4557 *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4558
4559 SAFE_FREE( data.dptr );
4560
4561 if ( !*domains )
4562 return false;
4563
4564 return true;
4565}
4566
4567/*********************************************************************
4568 ********************************************************************/
4569
4570bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4571{
4572 struct winbindd_tdc_domain *dom_list = NULL;
4573 size_t num_domains = 0;
4574 bool ret = false;
4575
4576 DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4577 "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4578 domain->name, domain->alt_name,
4579 sid_string_dbg(&domain->sid),
4580 domain->domain_flags,
4581 domain->domain_trust_attribs,
4582 domain->domain_type));
4583
4584 if ( !init_wcache() ) {
4585 return false;
4586 }
4587
4588 /* fetch the list */
4589
4590 wcache_tdc_fetch_list( &dom_list, &num_domains );
4591
4592 /* add the new domain */
4593
4594 if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4595 goto done;
4596 }
4597
4598 /* pack the domain */
4599
4600 if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4601 goto done;
4602 }
4603
4604 /* Success */
4605
4606 ret = true;
4607 done:
4608 TALLOC_FREE( dom_list );
4609
4610 return ret;
4611}
4612
4613/*********************************************************************
4614 ********************************************************************/
4615
4616struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4617{
4618 struct winbindd_tdc_domain *dom_list = NULL;
4619 size_t num_domains = 0;
4620 int i;
4621 struct winbindd_tdc_domain *d = NULL;
4622
4623 DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4624
4625 if ( !init_wcache() ) {
4626 return false;
4627 }
4628
4629 /* fetch the list */
4630
4631 wcache_tdc_fetch_list( &dom_list, &num_domains );
4632
4633 for ( i=0; i<num_domains; i++ ) {
4634 if ( strequal(name, dom_list[i].domain_name) ||
4635 strequal(name, dom_list[i].dns_name) )
4636 {
4637 DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4638 name));
4639
4640 d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4641 if ( !d )
4642 break;
4643
4644 d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4645 d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4646 sid_copy( &d->sid, &dom_list[i].sid );
4647 d->trust_flags = dom_list[i].trust_flags;
4648 d->trust_type = dom_list[i].trust_type;
4649 d->trust_attribs = dom_list[i].trust_attribs;
4650
4651 break;
4652 }
4653 }
4654
4655 TALLOC_FREE( dom_list );
4656
4657 return d;
4658}
4659
4660/*********************************************************************
4661 ********************************************************************/
4662
4663struct winbindd_tdc_domain*
4664 wcache_tdc_fetch_domainbysid(TALLOC_CTX *ctx,
4665 const struct dom_sid *sid)
4666{
4667 struct winbindd_tdc_domain *dom_list = NULL;
4668 size_t num_domains = 0;
4669 int i;
4670 struct winbindd_tdc_domain *d = NULL;
4671
4672 DEBUG(10,("wcache_tdc_fetch_domainbysid: Searching for domain %s\n",
4673 sid_string_dbg(sid)));
4674
4675 if (!init_wcache()) {
4676 return false;
4677 }
4678
4679 /* fetch the list */
4680
4681 wcache_tdc_fetch_list(&dom_list, &num_domains);
4682
4683 for (i = 0; i<num_domains; i++) {
4684 if (sid_equal(sid, &(dom_list[i].sid))) {
4685 DEBUG(10, ("wcache_tdc_fetch_domainbysid: "
4686 "Found domain %s for SID %s\n",
4687 dom_list[i].domain_name,
4688 sid_string_dbg(sid)));
4689
4690 d = TALLOC_P(ctx, struct winbindd_tdc_domain);
4691 if (!d)
4692 break;
4693
4694 d->domain_name = talloc_strdup(d,
4695 dom_list[i].domain_name);
4696
4697 d->dns_name = talloc_strdup(d, dom_list[i].dns_name);
4698 sid_copy(&d->sid, &dom_list[i].sid);
4699 d->trust_flags = dom_list[i].trust_flags;
4700 d->trust_type = dom_list[i].trust_type;
4701 d->trust_attribs = dom_list[i].trust_attribs;
4702
4703 break;
4704 }
4705 }
4706
4707 TALLOC_FREE(dom_list);
4708
4709 return d;
4710}
4711
4712
4713/*********************************************************************
4714 ********************************************************************/
4715
4716void wcache_tdc_clear( void )
4717{
4718 if ( !init_wcache() )
4719 return;
4720
4721 wcache_tdc_store_list( NULL, 0 );
4722
4723 return;
4724}
4725
4726
4727/*********************************************************************
4728 ********************************************************************/
4729
4730static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
4731 NTSTATUS status,
4732 const struct dom_sid *user_sid,
4733 const char *homedir,
4734 const char *shell,
4735 const char *gecos,
4736 uint32 gid)
4737{
4738 struct cache_entry *centry;
4739 fstring tmp;
4740
4741 if ( (centry = centry_start(domain, status)) == NULL )
4742 return;
4743
4744 centry_put_string( centry, homedir );
4745 centry_put_string( centry, shell );
4746 centry_put_string( centry, gecos );
4747 centry_put_uint32( centry, gid );
4748
4749 centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4750
4751 DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4752
4753 centry_free(centry);
4754}
4755
4756#ifdef HAVE_ADS
4757
4758NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
4759 const struct dom_sid *user_sid,
4760 TALLOC_CTX *ctx,
4761 const char **homedir, const char **shell,
4762 const char **gecos, gid_t *p_gid)
4763{
4764 struct winbind_cache *cache = get_cache(domain);
4765 struct cache_entry *centry = NULL;
4766 NTSTATUS nt_status;
4767 fstring tmp;
4768
4769 if (!cache->tdb)
4770 goto do_query;
4771
4772 centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4773 sid_to_fstring(tmp, user_sid));
4774
4775 if (!centry)
4776 goto do_query;
4777
4778 *homedir = centry_string( centry, ctx );
4779 *shell = centry_string( centry, ctx );
4780 *gecos = centry_string( centry, ctx );
4781 *p_gid = centry_uint32( centry );
4782
4783 centry_free(centry);
4784
4785 DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4786 sid_string_dbg(user_sid)));
4787
4788 return NT_STATUS_OK;
4789
4790do_query:
4791
4792 nt_status = nss_get_info( domain->name, user_sid, ctx,
4793 homedir, shell, gecos, p_gid );
4794
4795 DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4796
4797 if ( NT_STATUS_IS_OK(nt_status) ) {
4798 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4799 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4800 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4801 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4802
4803 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4804 *homedir, *shell, *gecos, *p_gid );
4805 }
4806
4807 if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4808 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4809 domain->name ));
4810 set_domain_offline( domain );
4811 }
4812
4813 return nt_status;
4814}
4815
4816#endif
4817
4818/* the cache backend methods are exposed via this structure */
4819struct winbindd_methods cache_methods = {
4820 true,
4821 query_user_list,
4822 enum_dom_groups,
4823 enum_local_groups,
4824 name_to_sid,
4825 sid_to_name,
4826 rids_to_names,
4827 query_user,
4828 lookup_usergroups,
4829 lookup_useraliases,
4830 lookup_groupmem,
4831 sequence_number,
4832 lockout_policy,
4833 password_policy,
4834 trusted_domains
4835};
4836
4837static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, char *domain_name,
4838 uint32_t opnum, const DATA_BLOB *req,
4839 TDB_DATA *pkey)
4840{
4841 char *key;
4842 size_t keylen;
4843
4844 key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
4845 if (key == NULL) {
4846 return false;
4847 }
4848 keylen = talloc_get_size(key) - 1;
4849
4850 key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
4851 if (key == NULL) {
4852 return false;
4853 }
4854 memcpy(key + keylen, req->data, req->length);
4855
4856 pkey->dptr = (uint8_t *)key;
4857 pkey->dsize = talloc_get_size(key);
4858 return true;
4859}
4860
4861static bool wcache_opnum_cacheable(uint32_t opnum)
4862{
4863 switch (opnum) {
4864 case NDR_WBINT_PING:
4865 case NDR_WBINT_QUERYSEQUENCENUMBER:
4866 case NDR_WBINT_ALLOCATEUID:
4867 case NDR_WBINT_ALLOCATEGID:
4868 case NDR_WBINT_CHECKMACHINEACCOUNT:
4869 case NDR_WBINT_CHANGEMACHINEACCOUNT:
4870 case NDR_WBINT_PINGDC:
4871 return false;
4872 }
4873 return true;
4874}
4875
4876bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
4877 uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
4878{
4879 TDB_DATA key, data;
4880 bool ret = false;
4881
4882 if (!wcache_opnum_cacheable(opnum) ||
4883 is_my_own_sam_domain(domain) ||
4884 is_builtin_domain(domain)) {
4885 return false;
4886 }
4887
4888 if (wcache->tdb == NULL) {
4889 return false;
4890 }
4891
4892 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4893 return false;
4894 }
4895 data = tdb_fetch(wcache->tdb, key);
4896 TALLOC_FREE(key.dptr);
4897
4898 if (data.dptr == NULL) {
4899 return false;
4900 }
4901 if (data.dsize < 12) {
4902 goto fail;
4903 }
4904
4905 if (!is_domain_offline(domain)) {
4906 uint32_t entry_seqnum, dom_seqnum, last_check;
4907 uint64_t entry_timeout;
4908
4909 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
4910 &last_check)) {
4911 goto fail;
4912 }
4913 entry_seqnum = IVAL(data.dptr, 0);
4914 if (entry_seqnum != dom_seqnum) {
4915 DEBUG(10, ("Entry has wrong sequence number: %d\n",
4916 (int)entry_seqnum));
4917 goto fail;
4918 }
4919 entry_timeout = BVAL(data.dptr, 4);
4920 if (time(NULL) > entry_timeout) {
4921 DEBUG(10, ("Entry has timed out\n"));
4922 goto fail;
4923 }
4924 }
4925
4926 resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
4927 data.dsize - 12);
4928 if (resp->data == NULL) {
4929 DEBUG(10, ("talloc failed\n"));
4930 goto fail;
4931 }
4932 resp->length = data.dsize - 12;
4933
4934 ret = true;
4935fail:
4936 SAFE_FREE(data.dptr);
4937 return ret;
4938}
4939
4940void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
4941 const DATA_BLOB *req, const DATA_BLOB *resp)
4942{
4943 TDB_DATA key, data;
4944 uint32_t dom_seqnum, last_check;
4945 uint64_t timeout;
4946
4947 if (!wcache_opnum_cacheable(opnum) ||
4948 is_my_own_sam_domain(domain) ||
4949 is_builtin_domain(domain)) {
4950 return;
4951 }
4952
4953 if (wcache->tdb == NULL) {
4954 return;
4955 }
4956
4957 if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
4958 DEBUG(10, ("could not fetch seqnum for domain %s\n",
4959 domain->name));
4960 return;
4961 }
4962
4963 if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
4964 return;
4965 }
4966
4967 timeout = time(NULL) + lp_winbind_cache_time();
4968
4969 data.dsize = resp->length + 12;
4970 data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
4971 if (data.dptr == NULL) {
4972 goto done;
4973 }
4974
4975 SIVAL(data.dptr, 0, dom_seqnum);
4976 SBVAL(data.dptr, 4, timeout);
4977 memcpy(data.dptr + 12, resp->data, resp->length);
4978
4979 tdb_store(wcache->tdb, key, data, 0);
4980
4981done:
4982 TALLOC_FREE(key.dptr);
4983 return;
4984}
Note: See TracBrowser for help on using the repository browser.