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

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

Samba Server: update vendor to 3.6.0

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