source: vendor/3.6.9/source3/winbindd/winbindd_cache.c

Last change on this file was 746, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated vendor to 3.6.9

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