libsmb: Fix removing invalid samlogon cache entries
[bbaumbach/samba-autobuild/.git] / source3 / libsmb / samlogon_cache.c
1 /*
2    Unix SMB/CIFS implementation.
3    Net_sam_logon info3 helpers
4    Copyright (C) Alexander Bokovoy              2002.
5    Copyright (C) Andrew Bartlett                2002.
6    Copyright (C) Gerald Carter                  2003.
7    Copyright (C) Tim Potter                     2003.
8    Copyright (C) Guenther Deschner              2008.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "librpc/gen_ndr/ndr_krb5pac.h"
27 #include "../libcli/security/security.h"
28 #include "util_tdb.h"
29
30 #define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
31
32 static TDB_CONTEXT *netsamlogon_tdb = NULL;
33
34 /***********************************************************************
35  open the tdb
36  ***********************************************************************/
37
38 bool netsamlogon_cache_init(void)
39 {
40         bool first_try = true;
41         char *path = NULL;
42         int ret;
43         struct tdb_context *tdb;
44
45         if (netsamlogon_tdb) {
46                 return true;
47         }
48
49         path = cache_path(NETSAMLOGON_TDB);
50         if (path == NULL) {
51                 return false;
52         }
53 again:
54         tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
55                            O_RDWR | O_CREAT, 0600);
56         if (tdb == NULL) {
57                 DEBUG(0,("tdb_open_log('%s') - failed\n", path));
58                 goto clear;
59         }
60
61         ret = tdb_check(tdb, NULL, NULL);
62         if (ret != 0) {
63                 tdb_close(tdb);
64                 DEBUG(0,("tdb_check('%s') - failed\n", path));
65                 goto clear;
66         }
67
68         netsamlogon_tdb = tdb;
69         talloc_free(path);
70         return true;
71
72 clear:
73         if (!first_try) {
74                 talloc_free(path);
75                 return false;
76         }
77         first_try = false;
78
79         DEBUG(0,("retry after truncate for '%s'\n", path));
80         truncate(path, 0);
81         goto again;
82 }
83
84
85 /***********************************************************************
86  Shutdown samlogon_cache database
87 ***********************************************************************/
88
89 bool netsamlogon_cache_shutdown(void)
90 {
91         if (netsamlogon_tdb) {
92                 return (tdb_close(netsamlogon_tdb) == 0);
93         }
94
95         return true;
96 }
97
98 /***********************************************************************
99  Clear cache getpwnam and getgroups entries from the winbindd cache
100 ***********************************************************************/
101
102 void netsamlogon_clear_cached_user(const struct dom_sid *user_sid)
103 {
104         fstring keystr;
105
106         if (!netsamlogon_cache_init()) {
107                 DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
108                         "%s for write!\n",
109                         NETSAMLOGON_TDB));
110                 return;
111         }
112
113         /* Prepare key as DOMAIN-SID/USER-RID string */
114         sid_to_fstring(keystr, user_sid);
115
116         DEBUG(10,("netsamlogon_clear_cached_user: SID [%s]\n", keystr));
117
118         tdb_delete_bystring(netsamlogon_tdb, keystr);
119 }
120
121 /***********************************************************************
122  Store a netr_SamInfo3 structure in a tdb for later user
123  username should be in UTF-8 format
124 ***********************************************************************/
125
126 bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
127 {
128         TDB_DATA data;
129         fstring keystr;
130         bool result = false;
131         struct dom_sid  user_sid;
132         time_t t = time(NULL);
133         TALLOC_CTX *tmp_ctx = talloc_stackframe();
134         DATA_BLOB blob;
135         enum ndr_err_code ndr_err;
136         struct netsamlogoncache_entry r;
137
138         if (!info3) {
139                 return false;
140         }
141
142         if (!netsamlogon_cache_init()) {
143                 DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n",
144                         NETSAMLOGON_TDB));
145                 return false;
146         }
147
148         sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid);
149
150         /* Prepare key as DOMAIN-SID/USER-RID string */
151         sid_to_fstring(keystr, &user_sid);
152
153         DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr));
154
155         /* Prepare data */
156
157         if (info3->base.full_name.string == NULL) {
158                 struct netr_SamInfo3 *cached_info3;
159                 const char *full_name = NULL;
160
161                 cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid);
162                 if (cached_info3 != NULL) {
163                         full_name = cached_info3->base.full_name.string;
164                 }
165
166                 if (full_name != NULL) {
167                         info3->base.full_name.string = talloc_strdup(info3, full_name);
168                 }
169         }
170
171         /* only Samba fills in the username, not sure why NT doesn't */
172         /* so we fill it in since winbindd_getpwnam() makes use of it */
173
174         if (!info3->base.account_name.string) {
175                 info3->base.account_name.string = talloc_strdup(info3, username);
176         }
177
178         r.timestamp = t;
179         r.info3 = *info3;
180
181         if (DEBUGLEVEL >= 10) {
182                 NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
183         }
184
185         ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r,
186                                        (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
187         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
188                 DEBUG(0,("netsamlogon_cache_store: failed to push entry to cache\n"));
189                 TALLOC_FREE(tmp_ctx);
190                 return false;
191         }
192
193         data.dsize = blob.length;
194         data.dptr = blob.data;
195
196         if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) == 0) {
197                 result = true;
198         }
199
200         TALLOC_FREE(tmp_ctx);
201
202         return result;
203 }
204
205 /***********************************************************************
206  Retrieves a netr_SamInfo3 structure from a tdb.  Caller must
207  free the user_info struct (malloc()'d memory)
208 ***********************************************************************/
209
210 struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid)
211 {
212         struct netr_SamInfo3 *info3 = NULL;
213         TDB_DATA data;
214         fstring keystr, tmp;
215         enum ndr_err_code ndr_err;
216         DATA_BLOB blob;
217         struct netsamlogoncache_entry r;
218
219         if (!netsamlogon_cache_init()) {
220                 DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
221                         NETSAMLOGON_TDB));
222                 return NULL;
223         }
224
225         /* Prepare key as DOMAIN-SID/USER-RID string */
226         slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, user_sid));
227         DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr));
228         data = tdb_fetch_bystring( netsamlogon_tdb, keystr );
229
230         if (!data.dptr) {
231                 return NULL;
232         }
233
234         info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
235         if (!info3) {
236                 goto done;
237         }
238
239         blob = data_blob_const(data.dptr, data.dsize);
240
241         ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
242                                       (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
243
244         if (DEBUGLEVEL >= 10) {
245                 NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
246         }
247
248         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
249                 DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n"));
250                 tdb_delete_bystring(netsamlogon_tdb, keystr);
251                 TALLOC_FREE(info3);
252                 goto done;
253         }
254
255         info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
256                                                       sizeof(r.info3));
257
258  done:
259         SAFE_FREE(data.dptr);
260
261         return info3;
262
263 #if 0   /* The netsamlogon cache needs to hang around.  Something about
264            this feels wrong, but it is the only way we can get all of the
265            groups.  The old universal groups cache didn't expire either.
266            --jerry */
267         {
268                 time_t          now = time(NULL);
269                 uint32          time_diff;
270
271                 /* is the entry expired? */
272                 time_diff = now - t;
273
274                 if ( (time_diff < 0 ) || (time_diff > lp_winbind_cache_time()) ) {
275                         DEBUG(10,("netsamlogon_cache_get: cache entry expired \n"));
276                         tdb_delete( netsamlogon_tdb, key );
277                         TALLOC_FREE( user );
278                 }
279         }
280 #endif
281 }
282
283 bool netsamlogon_cache_have(const struct dom_sid *user_sid)
284 {
285         TALLOC_CTX *mem_ctx = talloc_init("netsamlogon_cache_have");
286         struct netr_SamInfo3 *info3 = NULL;
287         bool result;
288
289         if (!mem_ctx)
290                 return False;
291
292         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
293
294         result = (info3 != NULL);
295
296         talloc_destroy(mem_ctx);
297
298         return result;
299 }