source: branches/samba-3.3.x/source/winbindd/winbindd_cache.c

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

Update Samba 3.3 branch to 3.3.2

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