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

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

Update 3.2 branch to 3.2.8

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