source: trunk/samba/source/nsswitch/winbindd_cache.c @ 30

Last change on this file since 30 was 30, checked in by Paul Smedley, 14 years ago

Code updated to Samba 3.0.25rc2 level

File size: 69.0 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                        if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1140                                DEBUG(3, ("query_user_list: flushing "
1141                                          "connection cache\n"));
1142                                invalidate_cm_connection(&domain->conn);
1143                        }
1144
1145        } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1146                 (retry++ < 5));
1147
1148        /* and save it */
1149        refresh_sequence_number(domain, False);
1150        centry = centry_start(domain, status);
1151        if (!centry)
1152                goto skip_save;
1153        centry_put_uint32(centry, *num_entries);
1154        for (i=0; i<(*num_entries); i++) {
1155                centry_put_string(centry, (*info)[i].acct_name);
1156                centry_put_string(centry, (*info)[i].full_name);
1157                centry_put_string(centry, (*info)[i].homedir);
1158                centry_put_string(centry, (*info)[i].shell);
1159                centry_put_sid(centry, &(*info)[i].user_sid);
1160                centry_put_sid(centry, &(*info)[i].group_sid);
1161                if (domain->backend && domain->backend->consistent) {
1162                        /* when the backend is consistent we can pre-prime some mappings */
1163                        wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1164                                                domain->name,
1165                                                (*info)[i].acct_name, 
1166                                                &(*info)[i].user_sid,
1167                                                SID_NAME_USER);
1168                        wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1169                                                &(*info)[i].user_sid,
1170                                                domain->name,
1171                                                (*info)[i].acct_name, 
1172                                                SID_NAME_USER);
1173                        wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1174                }
1175        }       
1176        centry_end(centry, "UL/%s", domain->name);
1177        centry_free(centry);
1178
1179skip_save:
1180        return status;
1181}
1182
1183/* list all domain groups */
1184static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1185                                TALLOC_CTX *mem_ctx,
1186                                uint32 *num_entries, 
1187                                struct acct_info **info)
1188{
1189        struct winbind_cache *cache = get_cache(domain);
1190        struct cache_entry *centry = NULL;
1191        NTSTATUS status;
1192        unsigned int i;
1193
1194        if (!cache->tdb)
1195                goto do_query;
1196
1197        centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1198        if (!centry)
1199                goto do_query;
1200
1201        *num_entries = centry_uint32(centry);
1202       
1203        if (*num_entries == 0)
1204                goto do_cached;
1205
1206        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1207        if (! (*info))
1208                smb_panic("enum_dom_groups out of memory");
1209        for (i=0; i<(*num_entries); i++) {
1210                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1211                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1212                (*info)[i].rid = centry_uint32(centry);
1213        }
1214
1215do_cached:     
1216        status = centry->status;
1217
1218        DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1219                domain->name, nt_errstr(status) ));
1220
1221        centry_free(centry);
1222        return status;
1223
1224do_query:
1225        *num_entries = 0;
1226        *info = NULL;
1227
1228        /* Return status value returned by seq number check */
1229
1230        if (!NT_STATUS_IS_OK(domain->last_status))
1231                return domain->last_status;
1232
1233        DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1234                domain->name ));
1235
1236        status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1237
1238        /* and save it */
1239        refresh_sequence_number(domain, False);
1240        centry = centry_start(domain, status);
1241        if (!centry)
1242                goto skip_save;
1243        centry_put_uint32(centry, *num_entries);
1244        for (i=0; i<(*num_entries); i++) {
1245                centry_put_string(centry, (*info)[i].acct_name);
1246                centry_put_string(centry, (*info)[i].acct_desc);
1247                centry_put_uint32(centry, (*info)[i].rid);
1248        }       
1249        centry_end(centry, "GL/%s/domain", domain->name);
1250        centry_free(centry);
1251
1252skip_save:
1253        return status;
1254}
1255
1256/* list all domain groups */
1257static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1258                                TALLOC_CTX *mem_ctx,
1259                                uint32 *num_entries, 
1260                                struct acct_info **info)
1261{
1262        struct winbind_cache *cache = get_cache(domain);
1263        struct cache_entry *centry = NULL;
1264        NTSTATUS status;
1265        unsigned int i;
1266
1267        if (!cache->tdb)
1268                goto do_query;
1269
1270        centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1271        if (!centry)
1272                goto do_query;
1273
1274        *num_entries = centry_uint32(centry);
1275       
1276        if (*num_entries == 0)
1277                goto do_cached;
1278
1279        (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1280        if (! (*info))
1281                smb_panic("enum_dom_groups out of memory");
1282        for (i=0; i<(*num_entries); i++) {
1283                fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1284                fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1285                (*info)[i].rid = centry_uint32(centry);
1286        }
1287
1288do_cached:     
1289
1290        /* If we are returning cached data and the domain controller
1291           is down then we don't know whether the data is up to date
1292           or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1293           indicate this. */
1294
1295        if (wcache_server_down(domain)) {
1296                DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1297                status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1298        } else
1299                status = centry->status;
1300
1301        DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1302                domain->name, nt_errstr(status) ));
1303
1304        centry_free(centry);
1305        return status;
1306
1307do_query:
1308        *num_entries = 0;
1309        *info = NULL;
1310
1311        /* Return status value returned by seq number check */
1312
1313        if (!NT_STATUS_IS_OK(domain->last_status))
1314                return domain->last_status;
1315
1316        DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1317                domain->name ));
1318
1319        status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1320
1321        /* and save it */
1322        refresh_sequence_number(domain, False);
1323        centry = centry_start(domain, status);
1324        if (!centry)
1325                goto skip_save;
1326        centry_put_uint32(centry, *num_entries);
1327        for (i=0; i<(*num_entries); i++) {
1328                centry_put_string(centry, (*info)[i].acct_name);
1329                centry_put_string(centry, (*info)[i].acct_desc);
1330                centry_put_uint32(centry, (*info)[i].rid);
1331        }
1332        centry_end(centry, "GL/%s/local", domain->name);
1333        centry_free(centry);
1334
1335skip_save:
1336        return status;
1337}
1338
1339/* convert a single name to a sid in a domain */
1340static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1341                            TALLOC_CTX *mem_ctx,
1342                            const char *domain_name,
1343                            const char *name,
1344                            DOM_SID *sid,
1345                            enum lsa_SidType *type)
1346{
1347        struct winbind_cache *cache = get_cache(domain);
1348        struct cache_entry *centry = NULL;
1349        NTSTATUS status;
1350        fstring uname;
1351
1352        if (!cache->tdb)
1353                goto do_query;
1354
1355        fstrcpy(uname, name);
1356        strupper_m(uname);
1357        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1358        if (!centry)
1359                goto do_query;
1360        *type = (enum lsa_SidType)centry_uint32(centry);
1361        status = centry->status;
1362        if (NT_STATUS_IS_OK(status)) {
1363                centry_sid(centry, mem_ctx, sid);
1364        }
1365
1366        DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1367                domain->name, nt_errstr(status) ));
1368
1369        centry_free(centry);
1370        return status;
1371
1372do_query:
1373        ZERO_STRUCTP(sid);
1374
1375        /* If the seq number check indicated that there is a problem
1376         * with this DC, then return that status... except for
1377         * access_denied.  This is special because the dc may be in
1378         * "restrict anonymous = 1" mode, in which case it will deny
1379         * most unauthenticated operations, but *will* allow the LSA
1380         * name-to-sid that we try as a fallback. */
1381
1382        if (!(NT_STATUS_IS_OK(domain->last_status)
1383              || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1384                return domain->last_status;
1385
1386        DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1387                domain->name ));
1388
1389        status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
1390
1391        /* and save it */
1392        refresh_sequence_number(domain, False);
1393
1394        if (domain->online && !is_null_sid(sid)) {
1395                wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1396        }
1397
1398        if (NT_STATUS_IS_OK(status)) {
1399                strupper_m(CONST_DISCARD(char *,domain_name));
1400                strlower_m(CONST_DISCARD(char *,name));
1401                wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1402        }
1403
1404        return status;
1405}
1406
1407/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1408   given */
1409static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1410                            TALLOC_CTX *mem_ctx,
1411                            const DOM_SID *sid,
1412                            char **domain_name,
1413                            char **name,
1414                            enum lsa_SidType *type)
1415{
1416        struct winbind_cache *cache = get_cache(domain);
1417        struct cache_entry *centry = NULL;
1418        NTSTATUS status;
1419        fstring sid_string;
1420
1421        if (!cache->tdb)
1422                goto do_query;
1423
1424        centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1425        if (!centry)
1426                goto do_query;
1427        if (NT_STATUS_IS_OK(centry->status)) {
1428                *type = (enum lsa_SidType)centry_uint32(centry);
1429                *domain_name = centry_string(centry, mem_ctx);
1430                *name = centry_string(centry, mem_ctx);
1431        }
1432        status = centry->status;
1433
1434        DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1435                domain->name, nt_errstr(status) ));
1436
1437        centry_free(centry);
1438        return status;
1439
1440do_query:
1441        *name = NULL;
1442        *domain_name = NULL;
1443
1444        /* If the seq number check indicated that there is a problem
1445         * with this DC, then return that status... except for
1446         * access_denied.  This is special because the dc may be in
1447         * "restrict anonymous = 1" mode, in which case it will deny
1448         * most unauthenticated operations, but *will* allow the LSA
1449         * sid-to-name that we try as a fallback. */
1450
1451        if (!(NT_STATUS_IS_OK(domain->last_status)
1452              || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1453                return domain->last_status;
1454
1455        DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1456                domain->name ));
1457
1458        status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1459
1460        /* and save it */
1461        refresh_sequence_number(domain, False);
1462        wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1463
1464        /* We can't save the name to sid mapping here, as with sid history a
1465         * later name2sid would give the wrong sid. */
1466
1467        return status;
1468}
1469
1470static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1471                              TALLOC_CTX *mem_ctx,
1472                              const DOM_SID *domain_sid,
1473                              uint32 *rids,
1474                              size_t num_rids,
1475                              char **domain_name,
1476                              char ***names,
1477                              enum lsa_SidType **types)
1478{
1479        struct winbind_cache *cache = get_cache(domain);
1480        size_t i;
1481        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1482        BOOL have_mapped;
1483        BOOL have_unmapped;
1484
1485        *domain_name = NULL;
1486        *names = NULL;
1487        *types = NULL;
1488
1489        if (!cache->tdb) {
1490                goto do_query;
1491        }
1492
1493        if (num_rids == 0) {
1494                return NT_STATUS_OK;
1495        }
1496
1497        *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1498        *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1499
1500        if ((*names == NULL) || (*types == NULL)) {
1501                result = NT_STATUS_NO_MEMORY;
1502                goto error;
1503        }
1504
1505        have_mapped = have_unmapped = False;
1506
1507        for (i=0; i<num_rids; i++) {
1508                DOM_SID sid;
1509                struct cache_entry *centry;
1510
1511                if (!sid_compose(&sid, domain_sid, rids[i])) {
1512                        result = NT_STATUS_INTERNAL_ERROR;
1513                        goto error;
1514                }
1515
1516                centry = wcache_fetch(cache, domain, "SN/%s",
1517                                      sid_string_static(&sid));
1518                if (!centry) {
1519                        goto do_query;
1520                }
1521
1522                (*types)[i] = SID_NAME_UNKNOWN;
1523                (*names)[i] = talloc_strdup(*names, "");
1524
1525                if (NT_STATUS_IS_OK(centry->status)) {
1526                        char *dom;
1527                        have_mapped = True;
1528                        (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1529                        dom = centry_string(centry, mem_ctx);
1530                        if (*domain_name == NULL) {
1531                                *domain_name = dom;
1532                        } else {
1533                                talloc_free(dom);
1534                        }
1535                        (*names)[i] = centry_string(centry, *names);
1536                } else {
1537                        have_unmapped = True;
1538                }
1539
1540                centry_free(centry);
1541        }
1542
1543        if (!have_mapped) {
1544                return NT_STATUS_NONE_MAPPED;
1545        }
1546        if (!have_unmapped) {
1547                return NT_STATUS_OK;
1548        }
1549        return STATUS_SOME_UNMAPPED;
1550
1551 do_query:
1552
1553        TALLOC_FREE(*names);
1554        TALLOC_FREE(*types);
1555
1556        result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1557                                                rids, num_rids, domain_name,
1558                                                names, types);
1559
1560        if (!NT_STATUS_IS_OK(result) &&
1561            !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1562                return result;
1563        }
1564
1565        refresh_sequence_number(domain, False);
1566
1567        for (i=0; i<num_rids; i++) {
1568                DOM_SID sid;
1569                NTSTATUS status;
1570
1571                if (!sid_compose(&sid, domain_sid, rids[i])) {
1572                        result = NT_STATUS_INTERNAL_ERROR;
1573                        goto error;
1574                }
1575
1576                status = (*types)[i] == SID_NAME_UNKNOWN ?
1577                        NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1578
1579                wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1580                                        (*names)[i], (*types)[i]);
1581        }
1582
1583        return result;
1584
1585 error:
1586       
1587        TALLOC_FREE(*names);
1588        TALLOC_FREE(*types);
1589        return result;
1590}
1591
1592/* Lookup user information from a rid */
1593static NTSTATUS query_user(struct winbindd_domain *domain, 
1594                           TALLOC_CTX *mem_ctx, 
1595                           const DOM_SID *user_sid, 
1596                           WINBIND_USERINFO *info)
1597{
1598        struct winbind_cache *cache = get_cache(domain);
1599        struct cache_entry *centry = NULL;
1600        NTSTATUS status;
1601
1602        if (!cache->tdb)
1603                goto do_query;
1604
1605        centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1606       
1607        /* If we have an access denied cache entry and a cached info3 in the
1608           samlogon cache then do a query.  This will force the rpc back end
1609           to return the info3 data. */
1610
1611        if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1612            netsamlogon_cache_have(user_sid)) {
1613                DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1614                domain->last_status = NT_STATUS_OK;
1615                centry_free(centry);
1616                goto do_query;
1617        }
1618       
1619        if (!centry)
1620                goto do_query;
1621
1622        info->acct_name = centry_string(centry, mem_ctx);
1623        info->full_name = centry_string(centry, mem_ctx);
1624        info->homedir = centry_string(centry, mem_ctx);
1625        info->shell = centry_string(centry, mem_ctx);
1626        info->primary_gid = centry_uint32(centry);
1627        centry_sid(centry, mem_ctx, &info->user_sid);
1628        centry_sid(centry, mem_ctx, &info->group_sid);
1629        status = centry->status;
1630
1631        DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1632                domain->name, nt_errstr(status) ));
1633
1634        centry_free(centry);
1635        return status;
1636
1637do_query:
1638        ZERO_STRUCTP(info);
1639
1640        /* Return status value returned by seq number check */
1641
1642        if (!NT_STATUS_IS_OK(domain->last_status))
1643                return domain->last_status;
1644       
1645        DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1646                domain->name ));
1647
1648        status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1649
1650        /* and save it */
1651        refresh_sequence_number(domain, False);
1652        wcache_save_user(domain, status, info);
1653
1654        return status;
1655}
1656
1657
1658/* Lookup groups a user is a member of. */
1659static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1660                                  TALLOC_CTX *mem_ctx,
1661                                  const DOM_SID *user_sid, 
1662                                  uint32 *num_groups, DOM_SID **user_gids)
1663{
1664        struct winbind_cache *cache = get_cache(domain);
1665        struct cache_entry *centry = NULL;
1666        NTSTATUS status;
1667        unsigned int i;
1668        fstring sid_string;
1669
1670        if (!cache->tdb)
1671                goto do_query;
1672
1673        centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1674       
1675        /* If we have an access denied cache entry and a cached info3 in the
1676           samlogon cache then do a query.  This will force the rpc back end
1677           to return the info3 data. */
1678
1679        if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1680            netsamlogon_cache_have(user_sid)) {
1681                DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1682                domain->last_status = NT_STATUS_OK;
1683                centry_free(centry);
1684                goto do_query;
1685        }
1686       
1687        if (!centry)
1688                goto do_query;
1689
1690        *num_groups = centry_uint32(centry);
1691       
1692        if (*num_groups == 0)
1693                goto do_cached;
1694
1695        (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1696        if (! (*user_gids))
1697                smb_panic("lookup_usergroups out of memory");
1698        for (i=0; i<(*num_groups); i++) {
1699                centry_sid(centry, mem_ctx, &(*user_gids)[i]);
1700        }
1701
1702do_cached:     
1703        status = centry->status;
1704
1705        DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
1706                domain->name, nt_errstr(status) ));
1707
1708        centry_free(centry);
1709        return status;
1710
1711do_query:
1712        (*num_groups) = 0;
1713        (*user_gids) = NULL;
1714
1715        /* Return status value returned by seq number check */
1716
1717        if (!NT_STATUS_IS_OK(domain->last_status))
1718                return domain->last_status;
1719
1720        DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1721                domain->name ));
1722
1723        status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1724
1725        /* and save it */
1726        refresh_sequence_number(domain, False);
1727        centry = centry_start(domain, status);
1728        if (!centry)
1729                goto skip_save;
1730        centry_put_uint32(centry, *num_groups);
1731        for (i=0; i<(*num_groups); i++) {
1732                centry_put_sid(centry, &(*user_gids)[i]);
1733        }       
1734        centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1735        centry_free(centry);
1736
1737skip_save:
1738        return status;
1739}
1740
1741static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1742                                   TALLOC_CTX *mem_ctx,
1743                                   uint32 num_sids, const DOM_SID *sids,
1744                                   uint32 *num_aliases, uint32 **alias_rids)
1745{
1746        struct winbind_cache *cache = get_cache(domain);
1747        struct cache_entry *centry = NULL;
1748        NTSTATUS status;
1749        char *sidlist = talloc_strdup(mem_ctx, "");
1750        int i;
1751
1752        if (!cache->tdb)
1753                goto do_query;
1754
1755        if (num_sids == 0) {
1756                *num_aliases = 0;
1757                *alias_rids = NULL;
1758                return NT_STATUS_OK;
1759        }
1760
1761        /* We need to cache indexed by the whole list of SIDs, the aliases
1762         * resulting might come from any of the SIDs. */
1763
1764        for (i=0; i<num_sids; i++) {
1765                sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1766                                          sid_string_static(&sids[i]));
1767                if (sidlist == NULL)
1768                        return NT_STATUS_NO_MEMORY;
1769        }
1770
1771        centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1772
1773        if (!centry)
1774                goto do_query;
1775
1776        *num_aliases = centry_uint32(centry);
1777        *alias_rids = NULL;
1778
1779        (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1780
1781        if ((*num_aliases != 0) && ((*alias_rids) == NULL)) {
1782                centry_free(centry);
1783                return NT_STATUS_NO_MEMORY;
1784        }
1785
1786        for (i=0; i<(*num_aliases); i++)
1787                (*alias_rids)[i] = centry_uint32(centry);
1788
1789        status = centry->status;
1790
1791        DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
1792                  "status %s\n", domain->name, nt_errstr(status)));
1793
1794        centry_free(centry);
1795        return status;
1796
1797 do_query:
1798        (*num_aliases) = 0;
1799        (*alias_rids) = NULL;
1800
1801        if (!NT_STATUS_IS_OK(domain->last_status))
1802                return domain->last_status;
1803
1804        DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1805                  "for domain %s\n", domain->name ));
1806
1807        status = domain->backend->lookup_useraliases(domain, mem_ctx,
1808                                                     num_sids, sids,
1809                                                     num_aliases, alias_rids);
1810
1811        /* and save it */
1812        refresh_sequence_number(domain, False);
1813        centry = centry_start(domain, status);
1814        if (!centry)
1815                goto skip_save;
1816        centry_put_uint32(centry, *num_aliases);
1817        for (i=0; i<(*num_aliases); i++)
1818                centry_put_uint32(centry, (*alias_rids)[i]);
1819        centry_end(centry, "UA%s", sidlist);
1820        centry_free(centry);
1821
1822 skip_save:
1823        return status;
1824}
1825
1826
1827static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1828                                TALLOC_CTX *mem_ctx,
1829                                const DOM_SID *group_sid, uint32 *num_names, 
1830                                DOM_SID **sid_mem, char ***names, 
1831                                uint32 **name_types)
1832{
1833        struct winbind_cache *cache = get_cache(domain);
1834        struct cache_entry *centry = NULL;
1835        NTSTATUS status;
1836        unsigned int i;
1837        fstring sid_string;
1838
1839        if (!cache->tdb)
1840                goto do_query;
1841
1842        centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1843        if (!centry)
1844                goto do_query;
1845
1846        *num_names = centry_uint32(centry);
1847       
1848        if (*num_names == 0)
1849                goto do_cached;
1850
1851        (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1852        (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1853        (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1854
1855        if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1856                smb_panic("lookup_groupmem out of memory");
1857        }
1858
1859        for (i=0; i<(*num_names); i++) {
1860                centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
1861                (*names)[i] = centry_string(centry, mem_ctx);
1862                (*name_types)[i] = centry_uint32(centry);
1863        }
1864
1865do_cached:     
1866        status = centry->status;
1867
1868        DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
1869                domain->name, nt_errstr(status)));
1870
1871        centry_free(centry);
1872        return status;
1873
1874do_query:
1875        (*num_names) = 0;
1876        (*sid_mem) = NULL;
1877        (*names) = NULL;
1878        (*name_types) = NULL;
1879       
1880        /* Return status value returned by seq number check */
1881
1882        if (!NT_STATUS_IS_OK(domain->last_status))
1883                return domain->last_status;
1884
1885        DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1886                domain->name ));
1887
1888        status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1889                                                  sid_mem, names, name_types);
1890
1891        /* and save it */
1892        refresh_sequence_number(domain, False);
1893        centry = centry_start(domain, status);
1894        if (!centry)
1895                goto skip_save;
1896        centry_put_uint32(centry, *num_names);
1897        for (i=0; i<(*num_names); i++) {
1898                centry_put_sid(centry, &(*sid_mem)[i]);
1899                centry_put_string(centry, (*names)[i]);
1900                centry_put_uint32(centry, (*name_types)[i]);
1901        }       
1902        centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1903        centry_free(centry);
1904
1905skip_save:
1906        return status;
1907}
1908
1909/* find the sequence number for a domain */
1910static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1911{
1912        refresh_sequence_number(domain, False);
1913
1914        *seq = domain->sequence_number;
1915
1916        return NT_STATUS_OK;
1917}
1918
1919/* enumerate trusted domains
1920 * (we need to have the list of trustdoms in the cache when we go offline) -
1921 * Guenther */
1922static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1923                                TALLOC_CTX *mem_ctx,
1924                                uint32 *num_domains,
1925                                char ***names,
1926                                char ***alt_names,
1927                                DOM_SID **dom_sids)
1928{
1929        struct winbind_cache *cache = get_cache(domain);
1930        struct cache_entry *centry = NULL;
1931        NTSTATUS status;
1932        int i;
1933 
1934        if (!cache->tdb)
1935                goto do_query;
1936 
1937        centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
1938       
1939        if (!centry) {
1940                goto do_query;
1941        }
1942 
1943        *num_domains = centry_uint32(centry);
1944       
1945        (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1946        (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
1947        (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
1948 
1949        if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
1950                smb_panic("trusted_domains out of memory");
1951        }
1952 
1953        for (i=0; i<(*num_domains); i++) {
1954                (*names)[i] = centry_string(centry, mem_ctx);
1955                (*alt_names)[i] = centry_string(centry, mem_ctx);
1956                centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
1957        }
1958
1959        status = centry->status;
1960 
1961        DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
1962                domain->name, *num_domains, nt_errstr(status) ));
1963 
1964        centry_free(centry);
1965        return status;
1966 
1967do_query:
1968        (*num_domains) = 0;
1969        (*dom_sids) = NULL;
1970        (*names) = NULL;
1971        (*alt_names) = NULL;
1972 
1973        /* Return status value returned by seq number check */
1974
1975        if (!NT_STATUS_IS_OK(domain->last_status))
1976                return domain->last_status;
1977       
1978        DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1979                domain->name ));
1980 
1981        status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
1982                                                names, alt_names, dom_sids);
1983
1984        /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
1985         * so that the generic centry handling still applies correctly -
1986         * Guenther*/
1987
1988        if (!NT_STATUS_IS_ERR(status)) {
1989                status = NT_STATUS_OK;
1990        }
1991
1992        /* and save it */
1993        refresh_sequence_number(domain, False);
1994 
1995        centry = centry_start(domain, status);
1996        if (!centry)
1997                goto skip_save;
1998
1999        centry_put_uint32(centry, *num_domains);
2000
2001        for (i=0; i<(*num_domains); i++) {
2002                centry_put_string(centry, (*names)[i]);
2003                centry_put_string(centry, (*alt_names)[i]);
2004                centry_put_sid(centry, &(*dom_sids)[i]);
2005        }
2006       
2007        centry_end(centry, "TRUSTDOMS/%s", domain->name);
2008 
2009        centry_free(centry);
2010 
2011skip_save:
2012        return status;
2013}       
2014
2015/* get lockout policy */
2016static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2017                               TALLOC_CTX *mem_ctx,
2018                               SAM_UNK_INFO_12 *policy){
2019        struct winbind_cache *cache = get_cache(domain);
2020        struct cache_entry *centry = NULL;
2021        NTSTATUS status;
2022 
2023        if (!cache->tdb)
2024                goto do_query;
2025 
2026        centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2027       
2028        if (!centry)
2029                goto do_query;
2030 
2031        policy->duration = centry_nttime(centry);
2032        policy->reset_count = centry_nttime(centry);
2033        policy->bad_attempt_lockout = centry_uint16(centry);
2034 
2035        status = centry->status;
2036 
2037        DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2038                domain->name, nt_errstr(status) ));
2039 
2040        centry_free(centry);
2041        return status;
2042 
2043do_query:
2044        ZERO_STRUCTP(policy);
2045 
2046        /* Return status value returned by seq number check */
2047
2048        if (!NT_STATUS_IS_OK(domain->last_status))
2049                return domain->last_status;
2050       
2051        DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2052                domain->name ));
2053 
2054        status = domain->backend->lockout_policy(domain, mem_ctx, policy); 
2055 
2056        /* and save it */
2057        refresh_sequence_number(domain, False);
2058        wcache_save_lockout_policy(domain, status, policy);
2059 
2060        return status;
2061}
2062 
2063/* get password policy */
2064static NTSTATUS password_policy(struct winbindd_domain *domain,
2065                                TALLOC_CTX *mem_ctx,
2066                                SAM_UNK_INFO_1 *policy)
2067{
2068        struct winbind_cache *cache = get_cache(domain);
2069        struct cache_entry *centry = NULL;
2070        NTSTATUS status;
2071
2072        if (!cache->tdb)
2073                goto do_query;
2074 
2075        centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2076       
2077        if (!centry)
2078                goto do_query;
2079
2080        policy->min_length_password = centry_uint16(centry);
2081        policy->password_history = centry_uint16(centry);
2082        policy->password_properties = centry_uint32(centry);
2083        policy->expire = centry_nttime(centry);
2084        policy->min_passwordage = centry_nttime(centry);
2085
2086        status = centry->status;
2087
2088        DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2089                domain->name, nt_errstr(status) ));
2090
2091        centry_free(centry);
2092        return status;
2093
2094do_query:
2095        ZERO_STRUCTP(policy);
2096
2097        /* Return status value returned by seq number check */
2098
2099        if (!NT_STATUS_IS_OK(domain->last_status))
2100                return domain->last_status;
2101       
2102        DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2103                domain->name ));
2104
2105        status = domain->backend->password_policy(domain, mem_ctx, policy); 
2106
2107        /* and save it */
2108        refresh_sequence_number(domain, False);
2109        wcache_save_password_policy(domain, status, policy);
2110
2111        return status;
2112}
2113
2114
2115/* Invalidate cached user and group lists coherently */
2116
2117static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2118                       void *state)
2119{
2120        if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
2121            strncmp(kbuf.dptr, "GL/", 3) == 0)
2122                tdb_delete(the_tdb, kbuf);
2123
2124        return 0;
2125}
2126
2127/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2128
2129void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2130                                NET_USER_INFO_3 *info3)
2131{
2132        struct winbind_cache *cache;
2133       
2134        if (!domain)
2135                return;
2136
2137        cache = get_cache(domain);
2138        netsamlogon_clear_cached_user(cache->tdb, info3);
2139}
2140
2141void wcache_invalidate_cache(void)
2142{
2143        struct winbindd_domain *domain;
2144
2145        for (domain = domain_list(); domain; domain = domain->next) {
2146                struct winbind_cache *cache = get_cache(domain);
2147
2148                DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2149                           "entries for %s\n", domain->name));
2150                if (cache)
2151                        tdb_traverse(cache->tdb, traverse_fn, NULL);
2152        }
2153}
2154
2155static BOOL init_wcache(void)
2156{
2157        if (wcache == NULL) {
2158                wcache = SMB_XMALLOC_P(struct winbind_cache);
2159                ZERO_STRUCTP(wcache);
2160        }
2161
2162        if (wcache->tdb != NULL)
2163                return True;
2164
2165        /* when working offline we must not clear the cache on restart */
2166        wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2167                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2168                                lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2169                                O_RDWR|O_CREAT, 0600);
2170
2171        if (wcache->tdb == NULL) {
2172                DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2173                return False;
2174        }
2175
2176        return True;
2177}
2178
2179/************************************************************************
2180 This is called by the parent to initialize the cache file.
2181 We don't need sophisticated locking here as we know we're the
2182 only opener.
2183************************************************************************/
2184
2185BOOL initialize_winbindd_cache(void)
2186{
2187        BOOL cache_bad = True;
2188        uint32 vers;
2189
2190        if (!init_wcache()) {
2191                DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2192                return False;
2193        }
2194
2195        /* Check version number. */
2196        if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2197                        vers == WINBINDD_CACHE_VERSION) {
2198                cache_bad = False;
2199        }
2200
2201        if (cache_bad) {
2202                DEBUG(0,("initialize_winbindd_cache: clearing cache "
2203                        "and re-creating with version number %d\n",
2204                        WINBINDD_CACHE_VERSION ));
2205
2206                tdb_close(wcache->tdb);
2207                wcache->tdb = NULL;
2208
2209                if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
2210                        DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2211                                lock_path("winbindd_cache.tdb"),
2212                                strerror(errno) ));
2213                        return False;
2214                }
2215                if (!init_wcache()) {
2216                        DEBUG(0,("initialize_winbindd_cache: re-initialization "
2217                                        "init_wcache failed.\n"));
2218                        return False;
2219                }
2220
2221                /* Write the version. */
2222                if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2223                        DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2224                                tdb_errorstr(wcache->tdb) ));
2225                        return False;
2226                }
2227        }
2228
2229        tdb_close(wcache->tdb);
2230        wcache->tdb = NULL;
2231        return True;
2232}
2233
2234void cache_store_response(pid_t pid, struct winbindd_response *response)
2235{
2236        fstring key_str;
2237
2238        if (!init_wcache())
2239                return;
2240
2241        DEBUG(10, ("Storing response for pid %d, len %d\n",
2242                   pid, response->length));
2243
2244        fstr_sprintf(key_str, "DR/%d", pid);
2245        if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2246                      make_tdb_data((const char *)response, sizeof(*response)),
2247                      TDB_REPLACE) == -1)
2248                return;
2249
2250        if (response->length == sizeof(*response))
2251                return;
2252
2253        /* There's extra data */
2254
2255        DEBUG(10, ("Storing extra data: len=%d\n",
2256                   (int)(response->length - sizeof(*response))));
2257
2258        fstr_sprintf(key_str, "DE/%d", pid);
2259        if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2260                      make_tdb_data((const char *)response->extra_data.data,
2261                                    response->length - sizeof(*response)),
2262                      TDB_REPLACE) == 0)
2263                return;
2264
2265        /* We could not store the extra data, make sure the tdb does not
2266         * contain a main record with wrong dangling extra data */
2267
2268        fstr_sprintf(key_str, "DR/%d", pid);
2269        tdb_delete(wcache->tdb, string_tdb_data(key_str));
2270
2271        return;
2272}
2273
2274BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2275{
2276        TDB_DATA data;
2277        fstring key_str;
2278
2279        if (!init_wcache())
2280                return False;
2281
2282        DEBUG(10, ("Retrieving response for pid %d\n", pid));
2283
2284        fstr_sprintf(key_str, "DR/%d", pid);
2285        data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2286
2287        if (data.dptr == NULL)
2288                return False;
2289
2290        if (data.dsize != sizeof(*response))
2291                return False;
2292
2293        memcpy(response, data.dptr, data.dsize);
2294        SAFE_FREE(data.dptr);
2295
2296        if (response->length == sizeof(*response)) {
2297                response->extra_data.data = NULL;
2298                return True;
2299        }
2300
2301        /* There's extra data */
2302
2303        DEBUG(10, ("Retrieving extra data length=%d\n",
2304                   (int)(response->length - sizeof(*response))));
2305
2306        fstr_sprintf(key_str, "DE/%d", pid);
2307        data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2308
2309        if (data.dptr == NULL) {
2310                DEBUG(0, ("Did not find extra data\n"));
2311                return False;
2312        }
2313
2314        if (data.dsize != (response->length - sizeof(*response))) {
2315                DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2316                SAFE_FREE(data.dptr);
2317                return False;
2318        }
2319
2320        dump_data(11, data.dptr, data.dsize);
2321
2322        response->extra_data.data = data.dptr;
2323        return True;
2324}
2325
2326void cache_cleanup_response(pid_t pid)
2327{
2328        fstring key_str;
2329
2330        if (!init_wcache())
2331                return;
2332
2333        fstr_sprintf(key_str, "DR/%d", pid);
2334        tdb_delete(wcache->tdb, string_tdb_data(key_str));
2335
2336        fstr_sprintf(key_str, "DE/%d", pid);
2337        tdb_delete(wcache->tdb, string_tdb_data(key_str));
2338
2339        return;
2340}
2341
2342
2343BOOL lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2344                       const char **domain_name, const char **name,
2345                       enum lsa_SidType *type)
2346{
2347        struct winbindd_domain *domain;
2348        struct winbind_cache *cache;
2349        struct cache_entry *centry = NULL;
2350        NTSTATUS status;
2351
2352        domain = find_lookup_domain_from_sid(sid);
2353        if (domain == NULL) {
2354                return False;
2355        }
2356
2357        cache = get_cache(domain);
2358
2359        if (cache->tdb == NULL) {
2360                return False;
2361        }
2362
2363        centry = wcache_fetch(cache, domain, "SN/%s", sid_string_static(sid));
2364        if (centry == NULL) {
2365                return False;
2366        }
2367
2368        if (NT_STATUS_IS_OK(centry->status)) {
2369                *type = (enum lsa_SidType)centry_uint32(centry);
2370                *domain_name = centry_string(centry, mem_ctx);
2371                *name = centry_string(centry, mem_ctx);
2372        }
2373
2374        status = centry->status;
2375        centry_free(centry);
2376        return NT_STATUS_IS_OK(status);
2377}
2378
2379BOOL lookup_cached_name(TALLOC_CTX *mem_ctx,
2380                        const char *domain_name,
2381                        const char *name,
2382                        DOM_SID *sid,
2383                        enum lsa_SidType *type)
2384{
2385        struct winbindd_domain *domain;
2386        struct winbind_cache *cache;
2387        struct cache_entry *centry = NULL;
2388        NTSTATUS status;
2389        fstring uname;
2390
2391        domain = find_lookup_domain_from_name(domain_name);
2392        if (domain == NULL) {
2393                return False;
2394        }
2395
2396        cache = get_cache(domain);
2397
2398        if (cache->tdb == NULL) {
2399                return False;
2400        }
2401
2402        fstrcpy(uname, name);
2403        strupper_m(uname);
2404       
2405        centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2406        if (centry == NULL) {
2407                return False;
2408        }
2409
2410        if (NT_STATUS_IS_OK(centry->status)) {
2411                *type = (enum lsa_SidType)centry_uint32(centry);
2412                centry_sid(centry, mem_ctx, sid);
2413        }
2414
2415        status = centry->status;
2416        centry_free(centry);
2417       
2418        return NT_STATUS_IS_OK(status);
2419}
2420
2421void cache_name2sid(struct winbindd_domain *domain, 
2422                    const char *domain_name, const char *name,
2423                    enum lsa_SidType type, const DOM_SID *sid)
2424{
2425        refresh_sequence_number(domain, False);
2426        wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2427                                sid, type);
2428}
2429
2430/* delete all centries that don't have NT_STATUS_OK set */
2431/*
2432 * The original idea that this cache only contains centries has
2433 * been blurred - now other stuff gets put in here. Ensure we
2434 * ignore these things on cleanup.
2435 */
2436
2437static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2438                               TDB_DATA dbuf, void *state)
2439{
2440        struct cache_entry *centry;
2441
2442        if (is_non_centry_key(kbuf)) {
2443                return 0;
2444        }
2445
2446        centry = wcache_fetch_raw(kbuf.dptr);
2447        if (!centry) {
2448                return 0;
2449        }
2450
2451        if (!NT_STATUS_IS_OK(centry->status)) {
2452                DEBUG(10,("deleting centry %s\n", kbuf.dptr));
2453                tdb_delete(the_tdb, kbuf);
2454        }
2455
2456        centry_free(centry);
2457        return 0;
2458}
2459
2460/* flush the cache */
2461void wcache_flush_cache(void)
2462{
2463        if (!wcache)
2464                return;
2465        if (wcache->tdb) {
2466                tdb_close(wcache->tdb);
2467                wcache->tdb = NULL;
2468        }
2469        if (opt_nocache)
2470                return;
2471
2472        /* when working offline we must not clear the cache on restart */
2473        wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
2474                                WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2475                                lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
2476                                O_RDWR|O_CREAT, 0600);
2477
2478        if (!wcache->tdb) {
2479                DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2480                return;
2481        }
2482
2483        tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2484
2485        DEBUG(10,("wcache_flush_cache success\n"));
2486}
2487
2488/* Count cached creds */
2489
2490static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2491                                    void *state)
2492{
2493        int *cred_count = (int*)state;
2494 
2495        if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2496                (*cred_count)++;
2497        }
2498        return 0;
2499}
2500
2501NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2502{
2503        struct winbind_cache *cache = get_cache(domain);
2504
2505        *count = 0;
2506
2507        if (!cache->tdb) {
2508                return NT_STATUS_INTERNAL_DB_ERROR;
2509        }
2510 
2511        tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2512
2513        return NT_STATUS_OK;
2514}
2515
2516struct cred_list {
2517        struct cred_list *prev, *next;
2518        TDB_DATA key;
2519        fstring name;
2520        time_t created;
2521};
2522static struct cred_list *wcache_cred_list;
2523
2524static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2525                                    void *state)
2526{
2527        struct cred_list *cred;
2528
2529        if (strncmp(kbuf.dptr, "CRED/", 5) == 0) {
2530
2531                cred = SMB_MALLOC_P(struct cred_list);
2532                if (cred == NULL) {
2533                        DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2534                        return -1;
2535                }
2536
2537                ZERO_STRUCTP(cred);
2538               
2539                /* save a copy of the key */
2540               
2541                fstrcpy(cred->name, kbuf.dptr);         
2542                DLIST_ADD(wcache_cred_list, cred);
2543        }
2544       
2545        return 0;
2546}
2547
2548NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2549{
2550        struct winbind_cache *cache = get_cache(domain);
2551        NTSTATUS status;
2552        int ret;
2553        struct cred_list *cred, *oldest = NULL;
2554
2555        if (!cache->tdb) {
2556                return NT_STATUS_INTERNAL_DB_ERROR;
2557        }
2558
2559        /* we possibly already have an entry */
2560        if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2561       
2562                fstring key_str;
2563
2564                DEBUG(11,("we already have an entry, deleting that\n"));
2565
2566                fstr_sprintf(key_str, "CRED/%s", sid_string_static(sid));
2567
2568                tdb_delete(cache->tdb, string_tdb_data(key_str));
2569
2570                return NT_STATUS_OK;
2571        }
2572
2573        ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2574        if (ret == 0) {
2575                return NT_STATUS_OK;
2576        } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2577                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2578        }
2579
2580        ZERO_STRUCTP(oldest);
2581
2582        for (cred = wcache_cred_list; cred; cred = cred->next) {
2583
2584                TDB_DATA data;
2585                time_t t;
2586
2587                data = tdb_fetch(cache->tdb, make_tdb_data(cred->name, strlen(cred->name)));
2588                if (!data.dptr) {
2589                        DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2590                                cred->name));
2591                        status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2592                        goto done;
2593                }
2594       
2595                t = IVAL(data.dptr, 0);
2596                SAFE_FREE(data.dptr);
2597
2598                if (!oldest) {
2599                        oldest = SMB_MALLOC_P(struct cred_list);
2600                        if (oldest == NULL) {
2601                                status = NT_STATUS_NO_MEMORY;
2602                                goto done;
2603                        }
2604
2605                        fstrcpy(oldest->name, cred->name);
2606                        oldest->created = t;
2607                        continue;
2608                }
2609
2610                if (t < oldest->created) {
2611                        fstrcpy(oldest->name, cred->name);
2612                        oldest->created = t;
2613                }
2614        }
2615
2616        if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
2617                status = NT_STATUS_OK;
2618        } else {
2619                status = NT_STATUS_UNSUCCESSFUL;
2620        }
2621done:
2622        SAFE_FREE(wcache_cred_list);
2623        SAFE_FREE(oldest);
2624       
2625        return status;
2626}
2627
2628/* Change the global online/offline state. */
2629BOOL set_global_winbindd_state_offline(void)
2630{
2631        TDB_DATA data;
2632
2633        DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
2634
2635        /* Only go offline if someone has created
2636           the key "WINBINDD_OFFLINE" in the cache tdb. */
2637
2638        if (wcache == NULL || wcache->tdb == NULL) {
2639                DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
2640                return False;
2641        }
2642
2643        if (!lp_winbind_offline_logon()) {
2644                DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
2645                return False;
2646        }
2647
2648        if (global_winbindd_offline_state) {
2649                /* Already offline. */
2650                return True;
2651        }
2652
2653        data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
2654
2655        if (!data.dptr || data.dsize != 4) {
2656                DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
2657                SAFE_FREE(data.dptr);
2658                return False;
2659        } else {
2660                DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
2661                global_winbindd_offline_state = True;
2662                SAFE_FREE(data.dptr);
2663                return True;
2664        }
2665}
2666
2667void set_global_winbindd_state_online(void)
2668{
2669        DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
2670
2671        if (!lp_winbind_offline_logon()) {
2672                DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
2673                return;
2674        }
2675
2676        if (!global_winbindd_offline_state) {
2677                /* Already online. */
2678                return;
2679        }
2680        global_winbindd_offline_state = False;
2681
2682        if (!wcache->tdb) {
2683                return;
2684        }
2685
2686        /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
2687        tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
2688}
2689
2690BOOL get_global_winbindd_state_offline(void)
2691{
2692        return global_winbindd_offline_state;
2693}
2694
2695/* the cache backend methods are exposed via this structure */
2696struct winbindd_methods cache_methods = {
2697        True,
2698        query_user_list,
2699        enum_dom_groups,
2700        enum_local_groups,
2701        name_to_sid,
2702        sid_to_name,
2703        rids_to_names,
2704        query_user,
2705        lookup_usergroups,
2706        lookup_useraliases,
2707        lookup_groupmem,
2708        sequence_number,
2709        lockout_policy,
2710        password_policy,
2711        trusted_domains
2712};
Note: See TracBrowser for help on using the repository browser.