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

Last change on this file was 414, checked in by Herwig Bauernfeind, 16 years ago

Samba 3.5.0: Initial import

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