source: trunk-3.0/source/nsswitch/winbindd_cache.c@ 101

Last change on this file since 101 was 71, checked in by Paul Smedley, 18 years ago

Update source to 3.0.26a

File size: 69.3 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
8 Copyright (C) Volker Lendecke 2005
9 Copyright (C) Guenther Deschner 2005
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
40
41/*
42 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
43 * Here are the list of entry types that are *not* stored
44 * as form struct cache_entry in the cache.
45 */
46
47static const char *non_centry_keys[] = {
48 "SEQNUM/",
49 "DR/",
50 "DE/",
51 "WINBINDD_OFFLINE",
52 WINBINDD_CACHE_VERSION_KEYSTR,
53 NULL
54};
55
56/************************************************************************
57 Is this key a non-centry type ?
58************************************************************************/
59
60static BOOL is_non_centry_key(TDB_DATA kbuf)
61{
62 int i;
63
64 if (kbuf.dptr == NULL || kbuf.dsize == 0) {
65 return False;
66 }
67 for (i = 0; non_centry_keys[i] != NULL; i++) {
68 size_t namelen = strlen(non_centry_keys[i]);
69 if (kbuf.dsize < namelen) {
70 continue;
71 }
72 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
73 return True;
74 }
75 }
76 return False;
77}
78
79/* Global online/offline state - False when online. winbindd starts up online
80 and sets this to true if the first query fails and there's an entry in
81 the cache tdb telling us to stay offline. */
82
83static BOOL global_winbindd_offline_state;
84
85struct winbind_cache {
86 TDB_CONTEXT *tdb;
87};
88
89struct cache_entry {
90 NTSTATUS status;
91 uint32 sequence_number;
92 uint8 *data;
93 uint32 len, ofs;
94};
95
96#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
97
98static struct winbind_cache *wcache;
99
100void winbindd_check_cache_size(time_t t)
101{
102 static time_t last_check_time;
103 struct stat st;
104
105 if (last_check_time == (time_t)0)
106 last_check_time = t;
107
108 if (t - last_check_time < 60 && t - last_check_time > 0)
109 return;
110
111 if (wcache == NULL || wcache->tdb == NULL) {
112 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
113 return;
114 }
115
116 if (fstat(tdb_fd(wcache->tdb), &st) == -1) {
117 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
118 return;
119 }
120
121 if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
122 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
123 (unsigned long)st.st_size,
124 (unsigned long)WINBINDD_MAX_CACHE_SIZE));
125 wcache_flush_cache();
126 }
127}
128
129/* get the winbind_cache structure */
130static struct winbind_cache *get_cache(struct winbindd_domain *domain)
131{
132 struct winbind_cache *ret = wcache;
133#ifdef HAVE_ADS
134 struct winbindd_domain *our_domain = domain;
135#endif
136
137 /* We have to know what type of domain we are dealing with first. */
138
139 if ( !domain->initialized ) {
140 init_dc_connection( domain );
141 }
142
143 /*
144 OK. listen up becasue I'm only going to say this once.
145 We have the following scenarios to consider
146 (a) trusted AD domains on a Samba DC,
147 (b) trusted AD domains and we are joined to a non-kerberos domain
148 (c) trusted AD domains and we are joined to a kerberos (AD) domain
149
150 For (a) we can always contact the trusted domain using krb5
151 since we have the domain trust account password
152
153 For (b) we can only use RPC since we have no way of
154 getting a krb5 ticket in our own domain
155
156 For (c) we can always use krb5 since we have a kerberos trust
157
158 --jerry
159 */
160
161 if (!domain->backend) {
162#ifdef HAVE_ADS
163 /* find our domain first so we can figure out if we
164 are joined to a kerberized domain */
165
166 if ( !domain->primary )
167 our_domain = find_our_domain();
168
169 if ( (our_domain->active_directory || IS_DC) && domain->active_directory ) {
170 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171 domain->backend = &ads_methods;
172 } else {
173#endif /* HAVE_ADS */
174 DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175 domain->backend = &reconnect_methods;
176#ifdef HAVE_ADS
177 }
178#endif /* HAVE_ADS */
179 }
180
181 if (ret)
182 return ret;
183
184 ret = SMB_XMALLOC_P(struct winbind_cache);
185 ZERO_STRUCTP(ret);
186
187 wcache = ret;
188 wcache_flush_cache();
189
190 return ret;
191}
192
193/*
194 free a centry structure
195*/
196static void centry_free(struct cache_entry *centry)
197{
198 if (!centry)
199 return;
200 SAFE_FREE(centry->data);
201 free(centry);
202}
203
204/*
205 pull a uint32 from a cache entry
206*/
207static uint32 centry_uint32(struct cache_entry *centry)
208{
209 uint32 ret;
210 if (centry->len - centry->ofs < 4) {
211 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
212 centry->len - centry->ofs));
213 smb_panic("centry_uint32");
214 }
215 ret = IVAL(centry->data, centry->ofs);
216 centry->ofs += 4;
217 return ret;
218}
219
220/*
221 pull a uint16 from a cache entry
222*/
223static uint16 centry_uint16(struct cache_entry *centry)
224{
225 uint16 ret;
226 if (centry->len - centry->ofs < 2) {
227 DEBUG(0,("centry corruption? needed 2 bytes, have %d\n",
228 centry->len - centry->ofs));
229 smb_panic("centry_uint16");
230 }
231 ret = CVAL(centry->data, centry->ofs);
232 centry->ofs += 2;
233 return ret;
234}
235
236/*
237 pull a uint8 from a cache entry
238*/
239static uint8 centry_uint8(struct cache_entry *centry)
240{
241 uint8 ret;
242 if (centry->len - centry->ofs < 1) {
243 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
244 centry->len - centry->ofs));
245 smb_panic("centry_uint32");
246 }
247 ret = CVAL(centry->data, centry->ofs);
248 centry->ofs += 1;
249 return ret;
250}
251
252/*
253 pull a NTTIME from a cache entry
254*/
255static NTTIME centry_nttime(struct cache_entry *centry)
256{
257 NTTIME ret;
258 if (centry->len - centry->ofs < 8) {
259 DEBUG(0,("centry corruption? needed 8 bytes, have %d\n",
260 centry->len - centry->ofs));
261 smb_panic("centry_nttime");
262 }
263 ret = IVAL(centry->data, centry->ofs);
264 centry->ofs += 4;
265 ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
266 centry->ofs += 4;
267 return ret;
268}
269
270/*
271 pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
272*/
273static time_t centry_time(struct cache_entry *centry)
274{
275 return (time_t)centry_nttime(centry);
276}
277
278/* pull a string from a cache entry, using the supplied
279 talloc context
280*/
281static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
282{
283 uint32 len;
284 char *ret;
285
286 len = centry_uint8(centry);
287
288 if (len == 0xFF) {
289 /* a deliberate NULL string */
290 return NULL;
291 }
292
293 if (centry->len - centry->ofs < len) {
294 DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
295 len, centry->len - centry->ofs));
296 smb_panic("centry_string");
297 }
298
299 ret = TALLOC_ARRAY(mem_ctx, char, len+1);
300 if (!ret) {
301 smb_panic("centry_string out of memory\n");
302 }
303 memcpy(ret,centry->data + centry->ofs, len);
304 ret[len] = 0;
305 centry->ofs += len;
306 return ret;
307}
308
309/* pull a hash16 from a cache entry, using the supplied
310 talloc context
311*/
312static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
313{
314 uint32 len;
315 char *ret;
316
317 len = centry_uint8(centry);
318
319 if (len != 16) {
320 DEBUG(0,("centry corruption? hash len (%u) != 16\n",
321 len ));
322 return NULL;
323 }
324
325 if (centry->len - centry->ofs < 16) {
326 DEBUG(0,("centry corruption? needed 16 bytes, have %d\n",
327 centry->len - centry->ofs));
328 return NULL;
329 }
330
331 ret = TALLOC_ARRAY(mem_ctx, char, 16);
332 if (!ret) {
333 smb_panic("centry_hash out of memory\n");
334 }
335 memcpy(ret,centry->data + centry->ofs, 16);
336 centry->ofs += 16;
337 return ret;
338}
339
340/* pull a sid from a cache entry, using the supplied
341 talloc context
342*/
343static BOOL centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
344{
345 char *sid_string;
346 sid_string = centry_string(centry, mem_ctx);
347 if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
348 return False;
349 }
350 return True;
351}
352
353/* the server is considered down if it can't give us a sequence number */
354static BOOL wcache_server_down(struct winbindd_domain *domain)
355{
356 BOOL ret;
357
358 if (!wcache->tdb)
359 return False;
360
361 ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
362
363 if (ret)
364 DEBUG(10,("wcache_server_down: server for Domain %s down\n",
365 domain->name ));
366 return ret;
367}
368
369static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
370{
371 TDB_DATA data;
372 fstring key;
373 uint32 time_diff;
374
375 if (!wcache->tdb) {
376 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
377 return NT_STATUS_UNSUCCESSFUL;
378 }
379
380 fstr_sprintf( key, "SEQNUM/%s", domain->name );
381
382 data = tdb_fetch_bystring( wcache->tdb, key );
383 if ( !data.dptr || data.dsize!=8 ) {
384 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
385 return NT_STATUS_UNSUCCESSFUL;
386 }
387
388 domain->sequence_number = IVAL(data.dptr, 0);
389 domain->last_seq_check = IVAL(data.dptr, 4);
390
391 SAFE_FREE(data.dptr);
392
393 /* have we expired? */
394
395 time_diff = now - domain->last_seq_check;
396 if ( time_diff > lp_winbind_cache_time() ) {
397 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
398 domain->name, domain->sequence_number,
399 (uint32)domain->last_seq_check));
400 return NT_STATUS_UNSUCCESSFUL;
401 }
402
403 DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
404 domain->name, domain->sequence_number,
405 (uint32)domain->last_seq_check));
406
407 return NT_STATUS_OK;
408}
409
410static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
411{
412 TDB_DATA data;
413 fstring key_str;
414 char buf[8];
415
416 if (!wcache->tdb) {
417 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
418 return NT_STATUS_UNSUCCESSFUL;
419 }
420
421 fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
422
423 SIVAL(buf, 0, domain->sequence_number);
424 SIVAL(buf, 4, domain->last_seq_check);
425 data.dptr = buf;
426 data.dsize = 8;
427
428 if ( tdb_store_bystring( wcache->tdb, key_str, data, TDB_REPLACE) == -1 ) {
429 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
430 return NT_STATUS_UNSUCCESSFUL;
431 }
432
433 DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
434 domain->name, domain->sequence_number,
435 (uint32)domain->last_seq_check));
436
437 return NT_STATUS_OK;
438}
439
440/*
441 refresh the domain sequence number. If force is True
442 then always refresh it, no matter how recently we fetched it
443*/
444
445static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
446{
447 NTSTATUS status;
448 unsigned time_diff;
449 time_t t = time(NULL);
450 unsigned cache_time = lp_winbind_cache_time();
451
452 get_cache( domain );
453
454#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
455 /* trying to reconnect is expensive, don't do it too often */
456 if (domain->sequence_number == DOM_SEQUENCE_NONE) {
457 cache_time *= 8;
458 }
459#endif
460
461 time_diff = t - domain->last_seq_check;
462
463 /* see if we have to refetch the domain sequence number */
464 if (!force && (time_diff < cache_time)) {
465 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
466 goto done;
467 }
468
469 /* try to get the sequence number from the tdb cache first */
470 /* this will update the timestamp as well */
471
472 status = fetch_cache_seqnum( domain, t );
473 if ( NT_STATUS_IS_OK(status) )
474 goto done;
475
476 /* important! make sure that we know if this is a native
477 mode domain or not */
478
479 status = domain->backend->sequence_number(domain, &domain->sequence_number);
480
481 /* the above call could have set our domain->backend to NULL when
482 * coming from offline to online mode, make sure to reinitialize the
483 * backend - Guenther */
484 get_cache( domain );
485
486 if (!NT_STATUS_IS_OK(status)) {
487 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
488 domain->sequence_number = DOM_SEQUENCE_NONE;
489 }
490
491 domain->last_status = status;
492 domain->last_seq_check = time(NULL);
493
494 /* save the new sequence number ni the cache */
495 store_cache_seqnum( domain );
496
497done:
498 DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
499 domain->name, domain->sequence_number));
500
501 return;
502}
503
504/*
505 decide if a cache entry has expired
506*/
507static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
508{
509 /* If we've been told to be offline - stay in that state... */
510 if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
511 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
512 keystr, domain->name ));
513 return False;
514 }
515
516 /* when the domain is offline return the cached entry.
517 * This deals with transient offline states... */
518
519 if (!domain->online) {
520 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
521 keystr, domain->name ));
522 return False;
523 }
524
525 /* if the server is OK and our cache entry came from when it was down then
526 the entry is invalid */
527 if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
528 (centry->sequence_number == DOM_SEQUENCE_NONE)) {
529 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
530 keystr, domain->name ));
531 return True;
532 }
533
534 /* if the server is down or the cache entry is not older than the
535 current sequence number then it is OK */
536 if (wcache_server_down(domain) ||
537 centry->sequence_number == domain->sequence_number) {
538 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
539 keystr, domain->name ));
540 return False;
541 }
542
543 DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
544 keystr, domain->name ));
545
546 /* it's expired */
547 return True;
548}
549
550static struct cache_entry *wcache_fetch_raw(char *kstr)
551{
552 TDB_DATA data;
553 struct cache_entry *centry;
554 TDB_DATA key;
555
556 key.dptr = kstr;
557 key.dsize = strlen(kstr);
558 data = tdb_fetch(wcache->tdb, key);
559 if (!data.dptr) {
560 /* a cache miss */
561 return NULL;
562 }
563
564 centry = SMB_XMALLOC_P(struct cache_entry);
565 centry->data = (unsigned char *)data.dptr;
566 centry->len = data.dsize;
567 centry->ofs = 0;
568
569 if (centry->len < 8) {
570 /* huh? corrupt cache? */
571 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
572 centry_free(centry);
573 return NULL;
574 }
575
576 centry->status = NT_STATUS(centry_uint32(centry));
577 centry->sequence_number = centry_uint32(centry);
578
579 return centry;
580}
581
582/*
583 fetch an entry from the cache, with a varargs key. auto-fetch the sequence
584 number and return status
585*/
586static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
587 struct winbindd_domain *domain,
588 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
589static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
590 struct winbindd_domain *domain,
591 const char *format, ...)
592{
593 va_list ap;
594 char *kstr;
595 struct cache_entry *centry;
596
597 if (opt_nocache) {
598 return NULL;
599 }
600
601 refresh_sequence_number(domain, False);
602
603 va_start(ap, format);
604 smb_xvasprintf(&kstr, format, ap);
605 va_end(ap);
606
607 centry = wcache_fetch_raw(kstr);
608 if (centry == NULL) {
609 free(kstr);
610 return NULL;
611 }
612
613 if (centry_expired(domain, kstr, centry)) {
614
615 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
616 kstr, domain->name ));
617
618 centry_free(centry);
619 free(kstr);
620 return NULL;
621 }
622
623 DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
624 kstr, domain->name ));
625
626 free(kstr);
627 return centry;
628}
629
630static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
631static void wcache_delete(const char *format, ...)
632{
633 va_list ap;
634 char *kstr;
635 TDB_DATA key;
636
637 va_start(ap, format);
638 smb_xvasprintf(&kstr, format, ap);
639 va_end(ap);
640
641 key.dptr = kstr;
642 key.dsize = strlen(kstr);
643
644 tdb_delete(wcache->tdb, key);
645 free(kstr);
646}
647
648/*
649 make sure we have at least len bytes available in a centry
650*/
651static void centry_expand(struct cache_entry *centry, uint32 len)
652{
653 if (centry->len - centry->ofs >= len)
654 return;
655 centry->len *= 2;
656 centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
657 centry->len);
658 if (!centry->data) {
659 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
660 smb_panic("out of memory in centry_expand");
661 }
662}
663
664/*
665 push a uint32 into a centry
666*/
667static void centry_put_uint32(struct cache_entry *centry, uint32 v)
668{
669 centry_expand(centry, 4);
670 SIVAL(centry->data, centry->ofs, v);
671 centry->ofs += 4;
672}
673
674/*
675 push a uint16 into a centry
676*/
677static void centry_put_uint16(struct cache_entry *centry, uint16 v)
678{
679 centry_expand(centry, 2);
680 SIVAL(centry->data, centry->ofs, v);
681 centry->ofs += 2;
682}
683
684/*
685 push a uint8 into a centry
686*/
687static void centry_put_uint8(struct cache_entry *centry, uint8 v)
688{
689 centry_expand(centry, 1);
690 SCVAL(centry->data, centry->ofs, v);
691 centry->ofs += 1;
692}
693
694/*
695 push a string into a centry
696 */
697static void centry_put_string(struct cache_entry *centry, const char *s)
698{
699 int len;
700
701 if (!s) {
702 /* null strings are marked as len 0xFFFF */
703 centry_put_uint8(centry, 0xFF);
704 return;
705 }
706
707 len = strlen(s);
708 /* can't handle more than 254 char strings. Truncating is probably best */
709 if (len > 254) {
710 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
711 len = 254;
712 }
713 centry_put_uint8(centry, len);
714 centry_expand(centry, len);
715 memcpy(centry->data + centry->ofs, s, len);
716 centry->ofs += len;
717}
718
719/*
720 push a 16 byte hash into a centry - treat as 16 byte string.
721 */
722static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
723{
724 centry_put_uint8(centry, 16);
725 centry_expand(centry, 16);
726 memcpy(centry->data + centry->ofs, val, 16);
727 centry->ofs += 16;
728}
729
730static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
731{
732 fstring sid_string;
733 centry_put_string(centry, sid_to_string(sid_string, sid));
734}
735
736/*
737 push a NTTIME into a centry
738*/
739static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
740{
741 centry_expand(centry, 8);
742 SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
743 centry->ofs += 4;
744 SIVAL(centry->data, centry->ofs, nt >> 32);
745 centry->ofs += 4;
746}
747
748/*
749 push a time_t into a centry - use a 64 bit size.
750 NTTIME here is being used as a convenient 64-bit size.
751*/
752static void centry_put_time(struct cache_entry *centry, time_t t)
753{
754 NTTIME nt = (NTTIME)t;
755 centry_put_nttime(centry, nt);
756}
757
758/*
759 start a centry for output. When finished, call centry_end()
760*/
761struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
762{
763 struct cache_entry *centry;
764
765 if (!wcache->tdb)
766 return NULL;
767
768 centry = SMB_XMALLOC_P(struct cache_entry);
769
770 centry->len = 8192; /* reasonable default */
771 centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
772 centry->ofs = 0;
773 centry->sequence_number = domain->sequence_number;
774 centry_put_uint32(centry, NT_STATUS_V(status));
775 centry_put_uint32(centry, centry->sequence_number);
776 return centry;
777}
778
779/*
780 finish a centry and write it to the tdb
781*/
782static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
783static void centry_end(struct cache_entry *centry, const char *format, ...)
784{
785 va_list ap;
786 char *kstr;
787 TDB_DATA key, data;
788
789 va_start(ap, format);
790 smb_xvasprintf(&kstr, format, ap);
791 va_end(ap);
792
793 key.dptr = kstr;
794 key.dsize = strlen(kstr);
795 data.dptr = (char *)centry->data;
796 data.dsize = centry->ofs;
797
798 tdb_store(wcache->tdb, key, data, TDB_REPLACE);
799 free(kstr);
800}
801
802static void wcache_save_name_to_sid(struct winbindd_domain *domain,
803 NTSTATUS status, const char *domain_name,
804 const char *name, const DOM_SID *sid,
805 enum lsa_SidType type)
806{
807 struct cache_entry *centry;
808 fstring uname;
809
810 centry = centry_start(domain, status);
811 if (!centry)
812 return;
813 centry_put_uint32(centry, type);
814 centry_put_sid(centry, sid);
815 fstrcpy(uname, name);
816 strupper_m(uname);
817 centry_end(centry, "NS/%s/%s", domain_name, uname);
818 DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
819 sid_string_static(sid)));
820 centry_free(centry);
821}
822
823static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
824 const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
825{
826 struct cache_entry *centry;
827 fstring sid_string;
828
829 if (is_null_sid(sid)) {
830 return;
831 }
832
833 centry = centry_start(domain, status);
834 if (!centry)
835 return;
836 if (NT_STATUS_IS_OK(status)) {
837 centry_put_uint32(centry, type);
838 centry_put_string(centry, domain_name);
839 centry_put_string(centry, name);
840 }
841 centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
842 DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
843 centry_free(centry);
844}
845
846
847static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
848{
849 struct cache_entry *centry;
850 fstring sid_string;
851
852 if (is_null_sid(&info->user_sid)) {
853 return;
854 }
855
856 centry = centry_start(domain, status);
857 if (!centry)
858 return;
859 centry_put_string(centry, info->acct_name);
860 centry_put_string(centry, info->full_name);
861 centry_put_string(centry, info->homedir);
862 centry_put_string(centry, info->shell);
863 centry_put_uint32(centry, info->primary_gid);
864 centry_put_sid(centry, &info->user_sid);
865 centry_put_sid(centry, &info->group_sid);
866 centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
867 DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
868 centry_free(centry);
869}
870
871static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_12 *lockout_policy)
872{
873 struct cache_entry *centry;
874
875 centry = centry_start(domain, status);
876 if (!centry)
877 return;
878
879 centry_put_nttime(centry, lockout_policy->duration);
880 centry_put_nttime(centry, lockout_policy->reset_count);
881 centry_put_uint16(centry, lockout_policy->bad_attempt_lockout);
882
883 centry_end(centry, "LOC_POL/%s", domain->name);
884
885 DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
886
887 centry_free(centry);
888}
889
890static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
891{
892 struct cache_entry *centry;
893
894 centry = centry_start(domain, status);
895 if (!centry)
896 return;
897
898 centry_put_uint16(centry, policy->min_length_password);
899 centry_put_uint16(centry, policy->password_history);
900 centry_put_uint32(centry, policy->password_properties);
901 centry_put_nttime(centry, policy->expire);
902 centry_put_nttime(centry, policy->min_passwordage);
903
904 centry_end(centry, "PWD_POL/%s", domain->name);
905
906 DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
907
908 centry_free(centry);
909}
910
911NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
912{
913 struct winbind_cache *cache = get_cache(domain);
914 TDB_DATA data;
915 fstring key_str;
916 uint32 rid;
917
918 if (!cache->tdb) {
919 return NT_STATUS_INTERNAL_DB_ERROR;
920 }
921
922 if (is_null_sid(sid)) {
923 return NT_STATUS_INVALID_SID;
924 }
925
926 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
927 return NT_STATUS_INVALID_SID;
928 }
929
930 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
931
932 data = tdb_fetch(cache->tdb, make_tdb_data(key_str, strlen(key_str)));
933 if (!data.dptr) {
934 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
935 }
936
937 SAFE_FREE(data.dptr);
938 return NT_STATUS_OK;
939}
940
941/* Lookup creds for a SID - copes with old (unsalted) creds as well
942 as new salted ones. */
943
944NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
945 TALLOC_CTX *mem_ctx,
946 const DOM_SID *sid,
947 const uint8 **cached_nt_pass,
948 const uint8 **cached_salt)
949{
950 struct winbind_cache *cache = get_cache(domain);
951 struct cache_entry *centry = NULL;
952 NTSTATUS status;
953 time_t t;
954 uint32 rid;
955
956 if (!cache->tdb) {
957 return NT_STATUS_INTERNAL_DB_ERROR;
958 }
959
960 if (is_null_sid(sid)) {
961 return NT_STATUS_INVALID_SID;
962 }
963
964 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
965 return NT_STATUS_INVALID_SID;
966 }
967
968 /* Try and get a salted cred first. If we can't
969 fall back to an unsalted cred. */
970
971 centry = wcache_fetch(cache, domain, "CRED/%s", sid_string_static(sid));
972 if (!centry) {
973 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n",
974 sid_string_static(sid)));
975 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
976 }
977
978 t = centry_time(centry);
979
980 /* In the salted case this isn't actually the nt_hash itself,
981 but the MD5 of the salt + nt_hash. Let the caller
982 sort this out. It can tell as we only return the cached_salt
983 if we are returning a salted cred. */
984
985 *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
986 if (*cached_nt_pass == NULL) {
987 const char *sidstr = sid_string_static(sid);
988
989 /* Bad (old) cred cache. Delete and pretend we
990 don't have it. */
991 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
992 sidstr));
993 wcache_delete("CRED/%s", sidstr);
994 centry_free(centry);
995 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
996 }
997
998 /* We only have 17 bytes more data in the salted cred case. */
999 if (centry->len - centry->ofs == 17) {
1000 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1001 } else {
1002 *cached_salt = NULL;
1003 }
1004
1005#if DEBUG_PASSWORD
1006 dump_data(100, (const char *)*cached_nt_pass, NT_HASH_LEN);
1007 if (*cached_salt) {
1008 dump_data(100, (const char *)*cached_salt, NT_HASH_LEN);
1009 }
1010#endif
1011 status = centry->status;
1012
1013 DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1014 sid_string_static(sid), nt_errstr(status) ));
1015
1016 centry_free(centry);
1017 return status;
1018}
1019
1020/* Store creds for a SID - only writes out new salted ones. */
1021
1022NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
1023 TALLOC_CTX *mem_ctx,
1024 const DOM_SID *sid,
1025 const uint8 nt_pass[NT_HASH_LEN])
1026{
1027 struct cache_entry *centry;
1028 fstring sid_string;
1029 uint32 rid;
1030 uint8 cred_salt[NT_HASH_LEN];
1031 uint8 salted_hash[NT_HASH_LEN];
1032
1033 if (is_null_sid(sid)) {
1034 return NT_STATUS_INVALID_SID;
1035 }
1036
1037 if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1038 return NT_STATUS_INVALID_SID;
1039 }
1040
1041 centry = centry_start(domain, NT_STATUS_OK);
1042 if (!centry) {
1043 return NT_STATUS_INTERNAL_DB_ERROR;
1044 }
1045
1046#if DEBUG_PASSWORD
1047 dump_data(100, (const char *)nt_pass, NT_HASH_LEN);
1048#endif
1049
1050 centry_put_time(centry, time(NULL));
1051
1052 /* Create a salt and then salt the hash. */
1053 generate_random_buffer(cred_salt, NT_HASH_LEN);
1054 E_md5hash(cred_salt, nt_pass, salted_hash);
1055
1056 centry_put_hash16(centry, salted_hash);
1057 centry_put_hash16(centry, cred_salt);
1058 centry_end(centry, "CRED/%s", sid_to_string(sid_string, sid));
1059
1060 DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1061
1062 centry_free(centry);
1063
1064 return NT_STATUS_OK;
1065}
1066
1067
1068/* Query display info. This is the basic user list fn */
1069static NTSTATUS query_user_list(struct winbindd_domain *domain,
1070 TALLOC_CTX *mem_ctx,
1071 uint32 *num_entries,
1072 WINBIND_USERINFO **info)
1073{
1074 struct winbind_cache *cache = get_cache(domain);
1075 struct cache_entry *centry = NULL;
1076 NTSTATUS status;
1077 unsigned int i, retry;
1078
1079 if (!cache->tdb)
1080 goto do_query;
1081
1082 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1083 if (!centry)
1084 goto do_query;
1085
1086 *num_entries = centry_uint32(centry);
1087
1088 if (*num_entries == 0)
1089 goto do_cached;
1090
1091 (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1092 if (! (*info))
1093 smb_panic("query_user_list out of memory");
1094 for (i=0; i<(*num_entries); i++) {
1095 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1096 (*info)[i].full_name = centry_string(centry, mem_ctx);
1097 (*info)[i].homedir = centry_string(centry, mem_ctx);
1098 (*info)[i].shell = centry_string(centry, mem_ctx);
1099 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1100 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1101 }
1102
1103do_cached:
1104 status = centry->status;
1105
1106 DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1107 domain->name, nt_errstr(status) ));
1108
1109 centry_free(centry);
1110 return status;
1111
1112do_query:
1113 *num_entries = 0;
1114 *info = NULL;
1115
1116 /* Return status value returned by seq number check */
1117
1118 if (!NT_STATUS_IS_OK(domain->last_status))
1119 return domain->last_status;
1120
1121 /* Put the query_user_list() in a retry loop. There appears to be
1122 * some bug either with Windows 2000 or Samba's handling of large
1123 * rpc replies. This manifests itself as sudden disconnection
1124 * at a random point in the enumeration of a large (60k) user list.
1125 * The retry loop simply tries the operation again. )-: It's not
1126 * pretty but an acceptable workaround until we work out what the
1127 * real problem is. */
1128
1129 retry = 0;
1130 do {
1131
1132 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1133 domain->name ));
1134
1135 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1136 if (!NT_STATUS_IS_OK(status)) {
1137 DEBUG(3, ("query_user_list: returned 0x%08x, "
1138 "retrying\n", NT_STATUS_V(status)));
1139 }
1140 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1141 DEBUG(3, ("query_user_list: flushing "
1142 "connection cache\n"));
1143 invalidate_cm_connection(&domain->conn);
1144 }
1145
1146 } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
1147 (retry++ < 5));
1148
1149 /* and save it */
1150 refresh_sequence_number(domain, False);
1151 centry = centry_start(domain, status);
1152 if (!centry)
1153 goto skip_save;
1154 centry_put_uint32(centry, *num_entries);
1155 for (i=0; i<(*num_entries); i++) {
1156 centry_put_string(centry, (*info)[i].acct_name);
1157 centry_put_string(centry, (*info)[i].full_name);
1158 centry_put_string(centry, (*info)[i].homedir);
1159 centry_put_string(centry, (*info)[i].shell);
1160 centry_put_sid(centry, &(*info)[i].user_sid);
1161 centry_put_sid(centry, &(*info)[i].group_sid);
1162 if (domain->backend && domain->backend->consistent) {
1163 /* when the backend is consistent we can pre-prime some mappings */
1164 wcache_save_name_to_sid(domain, NT_STATUS_OK,
1165 domain->name,
1166 (*info)[i].acct_name,
1167 &(*info)[i].user_sid,
1168 SID_NAME_USER);
1169 wcache_save_sid_to_name(domain, NT_STATUS_OK,
1170 &(*info)[i].user_sid,
1171 domain->name,
1172 (*info)[i].acct_name,
1173 SID_NAME_USER);
1174 wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1175 }
1176 }
1177 centry_end(centry, "UL/%s", domain->name);
1178 centry_free(centry);
1179
1180skip_save:
1181 return status;
1182}
1183
1184/* list all domain groups */
1185static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1186 TALLOC_CTX *mem_ctx,
1187 uint32 *num_entries,
1188 struct acct_info **info)
1189{
1190 struct winbind_cache *cache = get_cache(domain);
1191 struct cache_entry *centry = NULL;
1192 NTSTATUS status;
1193 unsigned int i;
1194
1195 if (!cache->tdb)
1196 goto do_query;
1197
1198 centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1199 if (!centry)
1200 goto do_query;
1201
1202 *num_entries = centry_uint32(centry);
1203
1204 if (*num_entries == 0)
1205 goto do_cached;
1206
1207 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1208 if (! (*info))
1209 smb_panic("enum_dom_groups out of memory");
1210 for (i=0; i<(*num_entries); i++) {
1211 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1212 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1213 (*info)[i].rid = centry_uint32(centry);
1214 }
1215
1216do_cached:
1217 status = centry->status;
1218
1219 DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1220 domain->name, nt_errstr(status) ));
1221
1222 centry_free(centry);
1223 return status;
1224
1225do_query:
1226 *num_entries = 0;
1227 *info = NULL;
1228
1229 /* Return status value returned by seq number check */
1230
1231 if (!NT_STATUS_IS_OK(domain->last_status))
1232 return domain->last_status;
1233
1234 DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1235 domain->name ));
1236
1237 status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1238
1239 /* and save it */
1240 refresh_sequence_number(domain, False);
1241 centry = centry_start(domain, status);
1242 if (!centry)
1243 goto skip_save;
1244 centry_put_uint32(centry, *num_entries);
1245 for (i=0; i<(*num_entries); i++) {
1246 centry_put_string(centry, (*info)[i].acct_name);
1247 centry_put_string(centry, (*info)[i].acct_desc);
1248 centry_put_uint32(centry, (*info)[i].rid);
1249 }
1250 centry_end(centry, "GL/%s/domain", domain->name);
1251 centry_free(centry);
1252
1253skip_save:
1254 return status;
1255}
1256
1257/* list all domain groups */
1258static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1259 TALLOC_CTX *mem_ctx,
1260 uint32 *num_entries,
1261 struct acct_info **info)
1262{
1263 struct winbind_cache *cache = get_cache(domain);
1264 struct cache_entry *centry = NULL;
1265 NTSTATUS status;
1266 unsigned int i;
1267
1268 if (!cache->tdb)
1269 goto do_query;
1270
1271 centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1272 if (!centry)
1273 goto do_query;
1274
1275 *num_entries = centry_uint32(centry);
1276
1277 if (*num_entries == 0)
1278 goto do_cached;
1279
1280 (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1281 if (! (*info))
1282 smb_panic("enum_dom_groups out of memory");
1283 for (i=0; i<(*num_entries); i++) {
1284 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1285 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1286 (*info)[i].rid = centry_uint32(centry);
1287 }
1288
1289do_cached:
1290
1291 /* If we are returning cached data and the domain controller
1292 is down then we don't know whether the data is up to date
1293 or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1294 indicate this. */
1295
1296 if (wcache_server_down(domain)) {
1297 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1298 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1299 } else
1300 status = centry->status;
1301
1302 DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1303 domain->name, nt_errstr(status) ));
1304
1305 centry_free(centry);
1306 return status;
1307
1308do_query:
1309 *num_entries = 0;
1310 *info = NULL;
1311
1312 /* Return status value returned by seq number check */
1313
1314 if (!NT_STATUS_IS_OK(domain->last_status))
1315 return domain->last_status;
1316
1317 DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1318 domain->name ));
1319
1320 status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1321
1322 /* and save it */
1323 refresh_sequence_number(domain, False);
1324 centry = centry_start(domain, status);
1325 if (!centry)
1326 goto skip_save;
1327 centry_put_uint32(centry, *num_entries);
1328 for (i=0; i<(*num_entries); i++) {
1329 centry_put_string(centry, (*info)[i].acct_name);
1330 centry_put_string(centry, (*info)[i].acct_desc);
1331 centry_put_uint32(centry, (*info)[i].rid);
1332 }
1333 centry_end(centry, "GL/%s/local", domain->name);
1334 centry_free(centry);
1335
1336skip_save:
1337 return status;
1338}
1339
1340/* convert a single name to a sid in a domain */
1341static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1342 TALLOC_CTX *mem_ctx,
1343 const char *domain_name,
1344 const char *name,
1345 DOM_SID *sid,
1346 enum lsa_SidType *type)
1347{
1348 struct winbind_cache *cache = get_cache(domain);
1349 struct cache_entry *centry = NULL;
1350 NTSTATUS status;
1351 fstring uname;
1352
1353 if (!cache->tdb)
1354 goto do_query;
1355
1356 fstrcpy(uname, name);
1357 strupper_m(uname);
1358 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1359 if (!centry)
1360 goto do_query;
1361 *type = (enum lsa_SidType)centry_uint32(centry);
1362 status = centry->status;
1363 if (NT_STATUS_IS_OK(status)) {
1364 centry_sid(centry, mem_ctx, sid);
1365 }
1366
1367 DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1368 domain->name, nt_errstr(status) ));
1369
1370 centry_free(centry);
1371 return status;
1372
1373do_query:
1374 ZERO_STRUCTP(sid);
1375
1376 /* If the seq number check indicated that there is a problem
1377 * with this DC, then return that status... except for
1378 * access_denied. This is special because the dc may be in
1379 * "restrict anonymous = 1" mode, in which case it will deny
1380 * most unauthenticated operations, but *will* allow the LSA
1381 * name-to-sid that we try as a fallback. */
1382
1383 if (!(NT_STATUS_IS_OK(domain->last_status)
1384 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1385 return domain->last_status;
1386
1387 DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1388 domain->name ));
1389
1390 status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1391
1392 /* and save it */
1393 refresh_sequence_number(domain, False);
1394
1395 if (domain->online && !is_null_sid(sid)) {
1396 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1397 }
1398
1399 if (NT_STATUS_IS_OK(status)) {
1400 strupper_m(CONST_DISCARD(char *,domain_name));
1401 strlower_m(CONST_DISCARD(char *,name));
1402 wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1403 }
1404
1405 return status;
1406}
1407
1408/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1409 given */
1410static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1411 TALLOC_CTX *mem_ctx,
1412 const DOM_SID *sid,
1413 char **domain_name,
1414 char **name,
1415 enum lsa_SidType *type)
1416{
1417 struct winbind_cache *cache = get_cache(domain);
1418 struct cache_entry *centry = NULL;
1419 NTSTATUS status;
1420 fstring sid_string;
1421
1422 if (!cache->tdb)
1423 goto do_query;
1424
1425 centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1426 if (!centry)
1427 goto do_query;
1428 if (NT_STATUS_IS_OK(centry->status)) {
1429 *type = (enum lsa_SidType)centry_uint32(centry);
1430 *domain_name = centry_string(centry, mem_ctx);
1431 *name = centry_string(centry, mem_ctx);
1432 }
1433 status = centry->status;
1434
1435 DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1436 domain->name, nt_errstr(status) ));
1437
1438 centry_free(centry);
1439 return status;
1440
1441do_query:
1442 *name = NULL;
1443 *domain_name = NULL;
1444
1445 /* If the seq number check indicated that there is a problem
1446 * with this DC, then return that status... except for
1447 * access_denied. This is special because the dc may be in
1448 * "restrict anonymous = 1" mode, in which case it will deny
1449 * most unauthenticated operations, but *will* allow the LSA
1450 * sid-to-name that we try as a fallback. */
1451
1452 if (!(NT_STATUS_IS_OK(domain->last_status)
1453 || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1454 return domain->last_status;
1455
1456 DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1457 domain->name ));
1458
1459 status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1460
1461 /* and save it */
1462 refresh_sequence_number(domain, False);
1463 wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1464
1465 /* We can't save the name to sid mapping here, as with sid history a
1466 * later name2sid would give the wrong sid. */
1467
1468 return status;
1469}
1470
1471static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1472 TALLOC_CTX *mem_ctx,
1473 const DOM_SID *domain_sid,
1474 uint32 *rids,
1475 size_t num_rids,
1476 char **domain_name,
1477 char ***names,
1478 enum lsa_SidType **types)
1479{
1480 struct winbind_cache *cache = get_cache(domain);
1481 size_t i;
1482 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1483 BOOL have_mapped;
1484 BOOL have_unmapped;
1485
1486 *domain_name = NULL;
1487 *names = NULL;
1488 *types = NULL;
1489
1490 if (!cache->tdb) {
1491 goto do_query;
1492 }
1493
1494 if (num_rids == 0) {
1495 return NT_STATUS_OK;
1496 }
1497
1498 *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1499 *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1500
1501 if ((*names == NULL) || (*types == NULL)) {
1502 result = NT_STATUS_NO_MEMORY;
1503 goto error;
1504 }
1505
1506 have_mapped = have_unmapped = False;
1507
1508 for (i=0; i<num_rids; i++) {
1509 DOM_SID sid;
1510 struct cache_entry *centry;
1511
1512 if (!sid_compose(&sid, domain_sid, rids[i])) {
1513 result = NT_STATUS_INTERNAL_ERROR;
1514 goto error;
1515 }
1516
1517 centry = wcache_fetch(cache, domain, "SN/%s",
1518 sid_string_static(&sid));
1519 if (!centry) {
1520 goto do_query;
1521 }
1522
1523 (*types)[i] = SID_NAME_UNKNOWN;
1524 (*names)[i] = talloc_strdup(*names, "");
1525
1526 if (NT_STATUS_IS_OK(centry->status)) {
1527 char *dom;
1528 have_mapped = True;
1529 (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1530 dom = centry_string(centry, mem_ctx);
1531 if (*domain_name == NULL) {
1532 *domain_name = dom;
1533 } else {
1534 talloc_free(dom);
1535 }
1536 (*names)[i] = centry_string(centry, *names);
1537 } else {
1538 have_unmapped = True;
1539 }
1540
1541 centry_free(centry);
1542 }
1543
1544 if (!have_mapped) {
1545 return NT_STATUS_NONE_MAPPED;
1546 }
1547 if (!have_unmapped) {
1548 return NT_STATUS_OK;
1549 }
1550 return STATUS_SOME_UNMAPPED;
1551
1552 do_query:
1553
1554 TALLOC_FREE(*names);
1555 TALLOC_FREE(*types);
1556
1557 result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1558 rids, num_rids, domain_name,
1559 names, types);
1560
1561 if (!NT_STATUS_IS_OK(result) &&
1562 !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1563 return result;
1564 }
1565
1566 refresh_sequence_number(domain, False);
1567
1568 for (i=0; i<num_rids; i++) {
1569 DOM_SID sid;
1570 NTSTATUS status;
1571
1572 if (!sid_compose(&sid, domain_sid, rids[i])) {
1573 result = NT_STATUS_INTERNAL_ERROR;
1574 goto error;
1575 }
1576
1577 status = (*types)[i] == SID_NAME_UNKNOWN ?
1578 NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1579
1580 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1581 (*names)[i], (*types)[i]);
1582 }
1583
1584 return result;
1585
1586 error:
1587
1588 TALLOC_FREE(*names);
1589 TALLOC_FREE(*types);
1590 return result;
1591}
1592
1593/* Lookup user information from a rid */
1594static NTSTATUS query_user(struct winbindd_domain *domain,
1595 TALLOC_CTX *mem_ctx,
1596 const DOM_SID *user_sid,
1597 WINBIND_USERINFO *info)
1598{
1599 struct winbind_cache *cache = get_cache(domain);
1600 struct cache_entry *centry = NULL;
1601 NTSTATUS status;
1602
1603 if (!cache->tdb)
1604 goto do_query;
1605
1606 centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1607
1608 /* If we have an access denied cache entry and a cached info3 in the
1609 samlogon cache then do a query. This will force the rpc back end
1610 to return the info3 data. */
1611
1612 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1613 netsamlogon_cache_have(user_sid)) {
1614 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1615 domain->last_status = NT_STATUS_OK;
1616 centry_free(centry);
1617 goto do_query;
1618 }
1619
1620 if (!centry)
1621 goto do_query;
1622
1623 info->acct_name = centry_string(centry, mem_ctx);
1624 info->full_name = centry_string(centry, mem_ctx);
1625 info->homedir = centry_string(centry, mem_ctx);
1626 info->shell = centry_string(centry, mem_ctx);
1627 info->primary_gid = centry_uint32(centry);
1628 centry_sid(centry, mem_ctx, &info->user_sid);
1629 centry_sid(centry, mem_ctx, &info->group_sid);
1630 status = centry->status;
1631
1632 DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1633 domain->name, nt_errstr(status) ));
1634
1635 centry_free(centry);
1636 return status;
1637
1638do_query:
1639 ZERO_STRUCTP(info);
1640
1641 /* Return status value returned by seq number check */
1642
1643 if (!NT_STATUS_IS_OK(domain->last_status))
1644 return domain->last_status;
1645
1646 DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1647 domain->name ));
1648
1649 status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1650
1651 /* and save it */
1652 refresh_sequence_number(domain, False);
1653 wcache_save_user(domain, status, info);
1654
1655 return status;
1656}
1657
1658
1659/* Lookup groups a user is a member of. */
1660static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1661 TALLOC_CTX *mem_ctx,
1662 const DOM_SID *user_sid,
1663 uint32 *num_groups, DOM_SID **user_gids)
1664{
1665 struct winbind_cache *cache = get_cache(domain);
1666 struct cache_entry *centry = NULL;
1667 NTSTATUS status;
1668 unsigned int i;
1669 fstring sid_string;
1670
1671 if (!cache->tdb)
1672 goto do_query;
1673
1674 centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1675
1676 /* If we have an access denied cache entry and a cached info3 in the
1677 samlogon cache then do a query. This will force the rpc back end
1678 to return the info3 data. */
1679
1680 if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1681 netsamlogon_cache_have(user_sid)) {
1682 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1683 domain->last_status = NT_STATUS_OK;
1684 centry_free(centry);
1685 goto do_query;
1686 }
1687
1688 if (!centry)
1689 goto do_query;
1690
1691 *num_groups = centry_uint32(centry);
1692
1693 if (*num_groups == 0)
1694 goto do_cached;
1695
1696 (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1697 if (! (*user_gids))
1698 smb_panic("lookup_usergroups out of memory");
1699 for (i=0; i<(*num_groups); i++) {
1700 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1701 }
1702
1703do_cached:
1704 status = centry->status;
1705
1706 DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1707 domain->name, nt_errstr(status) ));
1708
1709 centry_free(centry);
1710 return status;
1711
1712do_query:
1713 (*num_groups) = 0;
1714 (*user_gids) = NULL;
1715
1716 /* Return status value returned by seq number check */
1717
1718 if (!NT_STATUS_IS_OK(domain->last_status))
1719 return domain->last_status;
1720
1721 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1722 domain->name ));
1723
1724 status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1725
1726 /* and save it */
1727 refresh_sequence_number(domain, False);
1728 centry = centry_start(domain, status);
1729 if (!centry)
1730 goto skip_save;
1731 centry_put_uint32(centry, *num_groups);
1732 for (i=0; i<(*num_groups); i++) {
1733 centry_put_sid(centry, &(*user_gids)[i]);
1734 }
1735 centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1736 centry_free(centry);
1737
1738skip_save:
1739 return status;
1740}
1741
1742static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1743 TALLOC_CTX *mem_ctx,
1744 uint32 num_sids, const DOM_SID *sids,
1745 uint32 *num_aliases, uint32 **alias_rids)
1746{
1747 struct winbind_cache *cache = get_cache(domain);
1748 struct cache_entry *centry = NULL;
1749 NTSTATUS status;
1750 char *sidlist = talloc_strdup(mem_ctx, "");
1751 int i;
1752
1753 if (!cache->tdb)
1754 goto do_query;
1755
1756 if (num_sids == 0) {
1757 *num_aliases = 0;
1758 *alias_rids = NULL;
1759 return NT_STATUS_OK;
1760 }
1761
1762 /* We need to cache indexed by the whole list of SIDs, the aliases
1763 * resulting might come from any of the SIDs. */
1764
1765 for (i=0; i<num_sids; i++) {
1766 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1767 sid_string_static(&sids[i]));
1768 if (sidlist == NULL)
1769 return NT_STATUS_NO_MEMORY;
1770 }
1771
1772 centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1773
1774 if (!centry)
1775 goto do_query;
1776
1777 *num_aliases = centry_uint32(centry);
1778 *alias_rids = NULL;
1779
1780 if (*num_aliases) {
1781 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1782
1783 if ((*alias_rids) == NULL) {
1784 centry_free(centry);
1785 return NT_STATUS_NO_MEMORY;
1786 }
1787 } else {
1788 (*alias_rids) = NULL;
1789 }
1790
1791 for (i=0; i<(*num_aliases); i++)
1792 (*alias_rids)[i] = centry_uint32(centry);
1793
1794 status = centry->status;
1795
1796 DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1797 "status %s\n", domain->name, nt_errstr(status)));
1798
1799 centry_free(centry);
1800 return status;
1801
1802 do_query:
1803 (*num_aliases) = 0;
1804 (*alias_rids) = NULL;
1805
1806 if (!NT_STATUS_IS_OK(domain->last_status))
1807 return domain->last_status;
1808
1809 DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1810 "for domain %s\n", domain->name ));
1811
1812 status = domain->backend->lookup_useraliases(domain, mem_ctx,
1813 num_sids, sids,
1814 num_aliases, alias_rids);
1815
1816 /* and save it */
1817 refresh_sequence_number(domain, False);
1818 centry = centry_start(domain, status);
1819 if (!centry)
1820 goto skip_save;
1821 centry_put_uint32(centry, *num_aliases);
1822 for (i=0; i<(*num_aliases); i++)
1823 centry_put_uint32(centry, (*alias_rids)[i]);
1824 centry_end(centry, "UA%s", sidlist);
1825 centry_free(centry);
1826
1827 skip_save:
1828 return status;
1829}
1830
1831
1832static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1833 TALLOC_CTX *mem_ctx,
1834 const DOM_SID *group_sid, uint32 *num_names,
1835 DOM_SID **sid_mem, char ***names,
1836 uint32 **name_types)
1837{
1838 struct winbind_cache *cache = get_cache(domain);
1839 struct cache_entry *centry = NULL;
1840 NTSTATUS status;
1841 unsigned int i;
1842 fstring sid_string;
1843
1844 if (!cache->tdb)
1845 goto do_query;
1846
1847 centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1848 if (!centry)
1849 goto do_query;
1850
1851 *num_names = centry_uint32(centry);
1852
1853 if (*num_names == 0)
1854 goto do_cached;
1855
1856 (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1857 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1858 (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1859
1860 if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1861 smb_panic("lookup_groupmem out of memory");
1862 }
1863
1864 for (i=0; i<(*num_names); i++) {
1865 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1866 (*names)[i] = centry_string(centry, mem_ctx);
1867 (*name_types)[i] = centry_uint32(centry);
1868 }
1869
1870do_cached:
1871 status = centry->status;
1872
1873 DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1874 domain->name, nt_errstr(status)));
1875
1876 centry_free(centry);
1877 return status;
1878
1879do_query:
1880 (*num_names) = 0;
1881 (*sid_mem) = NULL;
1882 (*names) = NULL;
1883 (*name_types) = NULL;
1884
1885 /* Return status value returned by seq number check */
1886
1887 if (!NT_STATUS_IS_OK(domain->last_status))
1888 return domain->last_status;
1889
1890 DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1891 domain->name ));
1892
1893 status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
1894 sid_mem, names, name_types);
1895
1896 /* and save it */
1897 refresh_sequence_number(domain, False);
1898 centry = centry_start(domain, status);
1899 if (!centry)
1900 goto skip_save;
1901 centry_put_uint32(centry, *num_names);
1902 for (i=0; i<(*num_names); i++) {
1903 centry_put_sid(centry, &(*sid_mem)[i]);
1904 centry_put_string(centry, (*names)[i]);
1905 centry_put_uint32(centry, (*name_types)[i]);
1906 }
1907 centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1908 centry_free(centry);
1909
1910skip_save:
1911 return status;
1912}
1913
1914/* find the sequence number for a domain */
1915static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1916{
1917 refresh_sequence_number(domain, False);
1918
1919 *seq = domain->sequence_number;
1920
1921 return NT_STATUS_OK;
1922}
1923
1924/* enumerate trusted domains
1925 * (we need to have the list of trustdoms in the cache when we go offline) -
1926 * Guenther */
1927static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1928 TALLOC_CTX *mem_ctx,
1929 uint32 *num_domains,
1930 char ***names,
1931 char ***alt_names,
1932 DOM_SID **dom_sids)
1933{
1934 struct winbind_cache *cache = get_cache(domain);
1935 struct cache_entry *centry = NULL;
1936 NTSTATUS status;
1937 int i;
1938
1939 if (!cache->tdb)
1940 goto do_query;
1941
1942 centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1943
1944 if (!centry) {
1945 goto do_query;
1946 }
1947
1948 *num_domains = centry_uint32(centry);
1949
1950 if (*num_domains) {
1951 (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1952 (*alt_names) = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1953 (*dom_sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1954
1955 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1956 smb_panic("trusted_domains out of memory");
1957 }
1958 } else {
1959 (*names) = NULL;
1960 (*alt_names) = NULL;
1961 (*dom_sids) = NULL;
1962 }
1963
1964 for (i=0; i<(*num_domains); i++) {
1965 (*names)[i] = centry_string(centry, mem_ctx);
1966 (*alt_names)[i] = centry_string(centry, mem_ctx);
1967 centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1968 }
1969
1970 status = centry->status;
1971
1972 DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1973 domain->name, *num_domains, nt_errstr(status) ));
1974
1975 centry_free(centry);
1976 return status;
1977
1978do_query:
1979 (*num_domains) = 0;
1980 (*dom_sids) = NULL;
1981 (*names) = NULL;
1982 (*alt_names) = NULL;
1983
1984 /* Return status value returned by seq number check */
1985
1986 if (!NT_STATUS_IS_OK(domain->last_status))
1987 return domain->last_status;
1988
1989 DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1990 domain->name ));
1991
1992 status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1993 names, alt_names, dom_sids);
1994
1995 /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1996 * so that the generic centry handling still applies correctly -
1997 * Guenther*/
1998
1999 if (!NT_STATUS_IS_ERR(status)) {
2000 status = NT_STATUS_OK;
2001 }
2002
2003 /* and save it */
2004 refresh_sequence_number(domain, False);
2005
2006 centry = centry_start(domain, status);
2007 if (!centry)
2008 goto skip_save;
2009
2010 centry_put_uint32(centry, *num_domains);
2011
2012 for (i=0; i<(*num_domains); i++) {
2013 centry_put_string(centry, (*names)[i]);
2014 centry_put_string(centry, (*alt_names)[i]);
2015 centry_put_sid(centry, &(*dom_sids)[i]);
2016 }
2017
2018 centry_end(centry, "TRUSTDOMS/%s", domain->name);
2019
2020 centry_free(centry);
2021
2022skip_save:
2023 return status;
2024}
2025
2026/* get lockout policy */
2027static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2028 TALLOC_CTX *mem_ctx,
2029 SAM_UNK_INFO_12 *policy){
2030 struct winbind_cache *cache = get_cache(domain);
2031 struct cache_entry *centry = NULL;
2032 NTSTATUS status;
2033
2034 if (!cache->tdb)
2035 goto do_query;
2036
2037 centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2038
2039 if (!centry)
2040 goto do_query;
2041
2042 policy->duration = centry_nttime(centry);
2043 policy->reset_count = centry_nttime(centry);
2044 policy->bad_attempt_lockout = centry_uint16(centry);
2045
2046 status = centry->status;
2047
2048 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2049 domain->name, nt_errstr(status) ));
2050
2051 centry_free(centry);
2052 return status;
2053
2054do_query:
2055 ZERO_STRUCTP(policy);
2056
2057 /* Return status value returned by seq number check */
2058
2059 if (!NT_STATUS_IS_OK(domain->last_status))
2060 return domain->last_status;
2061
2062 DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2063 domain->name ));
2064
2065 status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2066
2067 /* and save it */
2068 refresh_sequence_number(domain, False);
2069 wcache_save_lockout_policy(domain, status, policy);
2070
2071 return status;
2072}
2073
2074/* get password policy */
2075static NTSTATUS password_policy(struct winbindd_domain *domain,
2076 TALLOC_CTX *mem_ctx,
2077 SAM_UNK_INFO_1 *policy)
2078{
2079 struct winbind_cache *cache = get_cache(domain);
2080 struct cache_entry *centry = NULL;
2081 NTSTATUS status;
2082
2083 if (!cache->tdb)
2084 goto do_query;
2085
2086 centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2087
2088 if (!centry)
2089 goto do_query;
2090
2091 policy->min_length_password = centry_uint16(centry);
2092 policy->password_history = centry_uint16(centry);
2093 policy->password_properties = centry_uint32(centry);
2094 policy->expire = centry_nttime(centry);
2095 policy->min_passwordage = centry_nttime(centry);
2096
2097 status = centry->status;
2098
2099 DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2100 domain->name, nt_errstr(status) ));
2101
2102 centry_free(centry);
2103 return status;
2104
2105do_query:
2106 ZERO_STRUCTP(policy);
2107
2108 /* Return status value returned by seq number check */
2109
2110 if (!NT_STATUS_IS_OK(domain->last_status))
2111 return domain->last_status;
2112
2113 DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2114 domain->name ));
2115
2116 status = domain->backend->password_policy(domain, mem_ctx, policy);
2117
2118 /* and save it */
2119 refresh_sequence_number(domain, False);
2120 wcache_save_password_policy(domain, status, policy);
2121
2122 return status;
2123}
2124
2125
2126/* Invalidate cached user and group lists coherently */
2127
2128static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2129 void *state)
2130{
2131 if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2132 strncmp(kbuf.dptr, "GL/", 3) == 0)
2133 tdb_delete(the_tdb, kbuf);
2134
2135 return 0;
2136}
2137
2138/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2139
2140void wcache_invalidate_samlogon(struct winbindd_domain *domain,
2141 NET_USER_INFO_3 *info3)
2142{
2143 struct winbind_cache *cache;
2144
2145 /* dont clear cached U/SID and UG/SID entries when we want to logon
2146 * offline - gd */
2147
2148 if (lp_winbind_offline_logon()) {
2149 return;
2150 }
2151
2152 if (!domain)
2153 return;
2154
2155 cache = get_cache(domain);
2156 netsamlogon_clear_cached_user(cache->tdb, info3);
2157}
2158
2159void wcache_invalidate_cache(void)
2160{
2161 struct winbindd_domain *domain;
2162
2163 for (domain = domain_list(); domain; domain = domain->next) {
2164 struct winbind_cache *cache = get_cache(domain);
2165
2166 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2167 "entries for %s\n", domain->name));
2168 if (cache)
2169 tdb_traverse(cache->tdb, traverse_fn, NULL);
2170 }
2171}
2172
2173static BOOL init_wcache(void)
2174{
2175 if (wcache == NULL) {
2176 wcache = SMB_XMALLOC_P(struct winbind_cache);
2177 ZERO_STRUCTP(wcache);
2178 }
2179
2180 if (wcache->tdb != NULL)
2181 return True;
2182
2183 /* when working offline we must not clear the cache on restart */
2184 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2185 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2186 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2187 O_RDWR|O_CREAT, 0600);
2188
2189 if (wcache->tdb == NULL) {
2190 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2191 return False;
2192 }
2193
2194 return True;
2195}
2196
2197/************************************************************************
2198 This is called by the parent to initialize the cache file.
2199 We don't need sophisticated locking here as we know we're the
2200 only opener.
2201************************************************************************/
2202
2203BOOL initialize_winbindd_cache(void)
2204{
2205 BOOL cache_bad = True;
2206 uint32 vers;
2207
2208 if (!init_wcache()) {
2209 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2210 return False;
2211 }
2212
2213 /* Check version number. */
2214 if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2215 vers == WINBINDD_CACHE_VERSION) {
2216 cache_bad = False;
2217 }
2218
2219 if (cache_bad) {
2220 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2221 "and re-creating with version number %d\n",
2222 WINBINDD_CACHE_VERSION ));
2223
2224 tdb_close(wcache->tdb);
2225 wcache->tdb = NULL;
2226
2227 if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2228 DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2229 lock_path("winbindd_cache.tdb"),
2230 strerror(errno) ));
2231 return False;
2232 }
2233 if (!init_wcache()) {
2234 DEBUG(0,("initialize_winbindd_cache: re-initialization "
2235 "init_wcache failed.\n"));
2236 return False;
2237 }
2238
2239 /* Write the version. */
2240 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2241 DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2242 tdb_errorstr(wcache->tdb) ));
2243 return False;
2244 }
2245 }
2246
2247 tdb_close(wcache->tdb);
2248 wcache->tdb = NULL;
2249 return True;
2250}
2251
2252void cache_store_response(pid_t pid, struct winbindd_response *response)
2253{
2254 fstring key_str;
2255
2256 if (!init_wcache())
2257 return;
2258
2259 DEBUG(10, ("Storing response for pid %d, len %d\n",
2260 pid, response->length));
2261
2262 fstr_sprintf(key_str, "DR/%d", pid);
2263 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2264 make_tdb_data((const char *)response, sizeof(*response)),
2265 TDB_REPLACE) == -1)
2266 return;
2267
2268 if (response->length == sizeof(*response))
2269 return;
2270
2271 /* There's extra data */
2272
2273 DEBUG(10, ("Storing extra data: len=%d\n",
2274 (int)(response->length - sizeof(*response))));
2275
2276 fstr_sprintf(key_str, "DE/%d", pid);
2277 if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2278 make_tdb_data((const char *)response->extra_data.data,
2279 response->length - sizeof(*response)),
2280 TDB_REPLACE) == 0)
2281 return;
2282
2283 /* We could not store the extra data, make sure the tdb does not
2284 * contain a main record with wrong dangling extra data */
2285
2286 fstr_sprintf(key_str, "DR/%d", pid);
2287 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2288
2289 return;
2290}
2291
2292BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2293{
2294 TDB_DATA data;
2295 fstring key_str;
2296
2297 if (!init_wcache())
2298 return False;
2299
2300 DEBUG(10, ("Retrieving response for pid %d\n", pid));
2301
2302 fstr_sprintf(key_str, "DR/%d", pid);
2303 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2304
2305 if (data.dptr == NULL)
2306 return False;
2307
2308 if (data.dsize != sizeof(*response))
2309 return False;
2310
2311 memcpy(response, data.dptr, data.dsize);
2312 SAFE_FREE(data.dptr);
2313
2314 if (response->length == sizeof(*response)) {
2315 response->extra_data.data = NULL;
2316 return True;
2317 }
2318
2319 /* There's extra data */
2320
2321 DEBUG(10, ("Retrieving extra data length=%d\n",
2322 (int)(response->length - sizeof(*response))));
2323
2324 fstr_sprintf(key_str, "DE/%d", pid);
2325 data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2326
2327 if (data.dptr == NULL) {
2328 DEBUG(0, ("Did not find extra data\n"));
2329 return False;
2330 }
2331
2332 if (data.dsize != (response->length - sizeof(*response))) {
2333 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2334 SAFE_FREE(data.dptr);
2335 return False;
2336 }
2337
2338 dump_data(11, data.dptr, data.dsize);
2339
2340 response->extra_data.data = data.dptr;
2341 return True;
2342}
2343
2344void cache_cleanup_response(pid_t pid)
2345{
2346 fstring key_str;
2347
2348 if (!init_wcache())
2349 return;
2350
2351 fstr_sprintf(key_str, "DR/%d", pid);
2352 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2353
2354 fstr_sprintf(key_str, "DE/%d", pid);
2355 tdb_delete(wcache->tdb, string_tdb_data(key_str));
2356
2357 return;
2358}
2359
2360
2361BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2362 const char **domain_name, const char **name,
2363 enum lsa_SidType *type)
2364{
2365 struct winbindd_domain *domain;
2366 struct winbind_cache *cache;
2367 struct cache_entry *centry = NULL;
2368 NTSTATUS status;
2369
2370 domain = find_lookup_domain_from_sid(sid);
2371 if (domain == NULL) {
2372 return False;
2373 }
2374
2375 cache = get_cache(domain);
2376
2377 if (cache->tdb == NULL) {
2378 return False;
2379 }
2380
2381 centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2382 if (centry == NULL) {
2383 return False;
2384 }
2385
2386 if (NT_STATUS_IS_OK(centry->status)) {
2387 *type = (enum lsa_SidType)centry_uint32(centry);
2388 *domain_name = centry_string(centry, mem_ctx);
2389 *name = centry_string(centry, mem_ctx);
2390 }
2391
2392 status = centry->status;
2393 centry_free(centry);
2394 return NT_STATUS_IS_OK(status);
2395}
2396
2397BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2398 const char *domain_name,
2399 const char *name,
2400 DOM_SID *sid,
2401 enum lsa_SidType *type)
2402{
2403 struct winbindd_domain *domain;
2404 struct winbind_cache *cache;
2405 struct cache_entry *centry = NULL;
2406 NTSTATUS status;
2407 fstring uname;
2408
2409 domain = find_lookup_domain_from_name(domain_name);
2410 if (domain == NULL) {
2411 return False;
2412 }
2413
2414 cache = get_cache(domain);
2415
2416 if (cache->tdb == NULL) {
2417 return False;
2418 }
2419
2420 fstrcpy(uname, name);
2421 strupper_m(uname);
2422
2423 centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2424 if (centry == NULL) {
2425 return False;
2426 }
2427
2428 if (NT_STATUS_IS_OK(centry->status)) {
2429 *type = (enum lsa_SidType)centry_uint32(centry);
2430 centry_sid(centry, mem_ctx, sid);
2431 }
2432
2433 status = centry->status;
2434 centry_free(centry);
2435
2436 return NT_STATUS_IS_OK(status);
2437}
2438
2439void cache_name2sid(struct winbindd_domain *domain,
2440 const char *domain_name, const char *name,
2441 enum lsa_SidType type, const DOM_SID *sid)
2442{
2443 refresh_sequence_number(domain, False);
2444 wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2445 sid, type);
2446}
2447
2448/* delete all centries that don't have NT_STATUS_OK set */
2449/*
2450 * The original idea that this cache only contains centries has
2451 * been blurred - now other stuff gets put in here. Ensure we
2452 * ignore these things on cleanup.
2453 */
2454
2455static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
2456 TDB_DATA dbuf, void *state)
2457{
2458 struct cache_entry *centry;
2459
2460 if (is_non_centry_key(kbuf)) {
2461 return 0;
2462 }
2463
2464 centry = wcache_fetch_raw(kbuf.dptr);
2465 if (!centry) {
2466 return 0;
2467 }
2468
2469 if (!NT_STATUS_IS_OK(centry->status)) {
2470 DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2471 tdb_delete(the_tdb, kbuf);
2472 }
2473
2474 centry_free(centry);
2475 return 0;
2476}
2477
2478/* flush the cache */
2479void wcache_flush_cache(void)
2480{
2481 if (!wcache)
2482 return;
2483 if (wcache->tdb) {
2484 tdb_close(wcache->tdb);
2485 wcache->tdb = NULL;
2486 }
2487 if (opt_nocache)
2488 return;
2489
2490 /* when working offline we must not clear the cache on restart */
2491 wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2492 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
2493 lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
2494 O_RDWR|O_CREAT, 0600);
2495
2496 if (!wcache->tdb) {
2497 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2498 return;
2499 }
2500
2501 tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2502
2503 DEBUG(10,("wcache_flush_cache success\n"));
2504}
2505
2506/* Count cached creds */
2507
2508static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2509 void *state)
2510{
2511 int *cred_count = (int*)state;
2512
2513 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2514 (*cred_count)++;
2515 }
2516 return 0;
2517}
2518
2519NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2520{
2521 struct winbind_cache *cache = get_cache(domain);
2522
2523 *count = 0;
2524
2525 if (!cache->tdb) {
2526 return NT_STATUS_INTERNAL_DB_ERROR;
2527 }
2528
2529 tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2530
2531 return NT_STATUS_OK;
2532}
2533
2534struct cred_list {
2535 struct cred_list *prev, *next;
2536 TDB_DATA key;
2537 fstring name;
2538 time_t created;
2539};
2540static struct cred_list *wcache_cred_list;
2541
2542static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
2543 void *state)
2544{
2545 struct cred_list *cred;
2546
2547 if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2548
2549 cred = SMB_MALLOC_P(struct cred_list);
2550 if (cred == NULL) {
2551 DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2552 return -1;
2553 }
2554
2555 ZERO_STRUCTP(cred);
2556
2557 /* save a copy of the key */
2558
2559 fstrcpy(cred->name, kbuf.dptr);
2560 DLIST_ADD(wcache_cred_list, cred);
2561 }
2562
2563 return 0;
2564}
2565
2566NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid)
2567{
2568 struct winbind_cache *cache = get_cache(domain);
2569 NTSTATUS status;
2570 int ret;
2571 struct cred_list *cred, *oldest = NULL;
2572
2573 if (!cache->tdb) {
2574 return NT_STATUS_INTERNAL_DB_ERROR;
2575 }
2576
2577 /* we possibly already have an entry */
2578 if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2579
2580 fstring key_str;
2581
2582 DEBUG(11,("we already have an entry, deleting that\n"));
2583
2584 fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2585
2586 tdb_delete(cache->tdb, string_tdb_data(key_str));
2587
2588 return NT_STATUS_OK;
2589 }
2590
2591 ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2592 if (ret == 0) {
2593 return NT_STATUS_OK;
2594 } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2595 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2596 }
2597
2598 ZERO_STRUCTP(oldest);
2599
2600 for (cred = wcache_cred_list; cred; cred = cred->next) {
2601
2602 TDB_DATA data;
2603 time_t t;
2604
2605 data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2606 if (!data.dptr) {
2607 DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
2608 cred->name));
2609 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2610 goto done;
2611 }
2612
2613 t = IVAL(data.dptr, 0);
2614 SAFE_FREE(data.dptr);
2615
2616 if (!oldest) {
2617 oldest = SMB_MALLOC_P(struct cred_list);
2618 if (oldest == NULL) {
2619 status = NT_STATUS_NO_MEMORY;
2620 goto done;
2621 }
2622
2623 fstrcpy(oldest->name, cred->name);
2624 oldest->created = t;
2625 continue;
2626 }
2627
2628 if (t < oldest->created) {
2629 fstrcpy(oldest->name, cred->name);
2630 oldest->created = t;
2631 }
2632 }
2633
2634 if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2635 status = NT_STATUS_OK;
2636 } else {
2637 status = NT_STATUS_UNSUCCESSFUL;
2638 }
2639done:
2640 SAFE_FREE(wcache_cred_list);
2641 SAFE_FREE(oldest);
2642
2643 return status;
2644}
2645
2646/* Change the global online/offline state. */
2647BOOL set_global_winbindd_state_offline(void)
2648{
2649 TDB_DATA data;
2650
2651 DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2652
2653 /* Only go offline if someone has created
2654 the key "WINBINDD_OFFLINE" in the cache tdb. */
2655
2656 if (wcache == NULL || wcache->tdb == NULL) {
2657 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2658 return False;
2659 }
2660
2661 if (!lp_winbind_offline_logon()) {
2662 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2663 return False;
2664 }
2665
2666 if (global_winbindd_offline_state) {
2667 /* Already offline. */
2668 return True;
2669 }
2670
2671 data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2672
2673 if (!data.dptr || data.dsize != 4) {
2674 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2675 SAFE_FREE(data.dptr);
2676 return False;
2677 } else {
2678 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2679 global_winbindd_offline_state = True;
2680 SAFE_FREE(data.dptr);
2681 return True;
2682 }
2683}
2684
2685void set_global_winbindd_state_online(void)
2686{
2687 DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2688
2689 if (!lp_winbind_offline_logon()) {
2690 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2691 return;
2692 }
2693
2694 if (!global_winbindd_offline_state) {
2695 /* Already online. */
2696 return;
2697 }
2698 global_winbindd_offline_state = False;
2699
2700 if (!wcache->tdb) {
2701 return;
2702 }
2703
2704 /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2705 tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2706}
2707
2708BOOL get_global_winbindd_state_offline(void)
2709{
2710 return global_winbindd_offline_state;
2711}
2712
2713/* the cache backend methods are exposed via this structure */
2714struct winbindd_methods cache_methods = {
2715 True,
2716 query_user_list,
2717 enum_dom_groups,
2718 enum_local_groups,
2719 name_to_sid,
2720 sid_to_name,
2721 rids_to_names,
2722 query_user,
2723 lookup_usergroups,
2724 lookup_useraliases,
2725 lookup_groupmem,
2726 sequence_number,
2727 lockout_policy,
2728 password_policy,
2729 trusted_domains
2730};
Note: See TracBrowser for help on using the repository browser.