source: branches/samba-3.5.x/source3/winbindd/winbindd_cache.c

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

Samba Server 3.5: update branche to 3.5.13

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