source: branches/samba-3.2.x/source/winbindd/winbindd_cache.c@ 201

Last change on this file since 201 was 141, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.1

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