samlogon_cache: use a talloc_stackframe inside netsamlogon_cache_store.
[sfrench/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         const 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 again:
51         tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
52                            O_RDWR | O_CREAT, 0600);
53         if (tdb == NULL) {
54                 DEBUG(0,("tdb_open_log('%s') - failed\n", path));
55                 goto clear;
56         }
57
58         ret = tdb_check(tdb, NULL, NULL);
59         if (ret != 0) {
60                 tdb_close(tdb);
61                 DEBUG(0,("tdb_check('%s') - failed\n", path));
62                 goto clear;
63         }
64
65         netsamlogon_tdb = tdb;
66         return true;
67
68 clear:
69         if (!first_try) {
70                 return false;
71         }
72         first_try = false;
73
74         DEBUG(0,("retry after truncate for '%s'\n", path));
75         truncate(path, 0);
76         goto again;
77 }
78
79
80 /***********************************************************************
81  Shutdown samlogon_cache database
82 ***********************************************************************/
83
84 bool netsamlogon_cache_shutdown(void)
85 {
86         if (netsamlogon_tdb) {
87                 return (tdb_close(netsamlogon_tdb) == 0);
88         }
89
90         return true;
91 }
92
93 /***********************************************************************
94  Clear cache getpwnam and getgroups entries from the winbindd cache
95 ***********************************************************************/
96
97 void netsamlogon_clear_cached_user(const struct dom_sid *user_sid)
98 {
99         fstring keystr;
100
101         if (!netsamlogon_cache_init()) {
102                 DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
103                         "%s for write!\n",
104                         NETSAMLOGON_TDB));
105                 return;
106         }
107
108         /* Prepare key as DOMAIN-SID/USER-RID string */
109         sid_to_fstring(keystr, user_sid);
110
111         DEBUG(10,("netsamlogon_clear_cached_user: SID [%s]\n", keystr));
112
113         tdb_delete_bystring(netsamlogon_tdb, keystr);
114 }
115
116 /***********************************************************************
117  Store a netr_SamInfo3 structure in a tdb for later user
118  username should be in UTF-8 format
119 ***********************************************************************/
120
121 bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
122 {
123         TDB_DATA data;
124         fstring keystr;
125         bool result = false;
126         struct dom_sid  user_sid;
127         time_t t = time(NULL);
128         TALLOC_CTX *tmp_ctx = talloc_stackframe();
129         DATA_BLOB blob;
130         enum ndr_err_code ndr_err;
131         struct netsamlogoncache_entry r;
132
133         if (!info3) {
134                 return false;
135         }
136
137         if (!netsamlogon_cache_init()) {
138                 DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n",
139                         NETSAMLOGON_TDB));
140                 return false;
141         }
142
143         sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid);
144
145         /* Prepare key as DOMAIN-SID/USER-RID string */
146         sid_to_fstring(keystr, &user_sid);
147
148         DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr));
149
150         /* Prepare data */
151
152         /* only Samba fills in the username, not sure why NT doesn't */
153         /* so we fill it in since winbindd_getpwnam() makes use of it */
154
155         if (!info3->base.account_name.string) {
156                 info3->base.account_name.string = talloc_strdup(info3, username);
157         }
158
159         r.timestamp = t;
160         r.info3 = *info3;
161
162         if (DEBUGLEVEL >= 10) {
163                 NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
164         }
165
166         ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r,
167                                        (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
168         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
169                 DEBUG(0,("netsamlogon_cache_store: failed to push entry to cache\n"));
170                 TALLOC_FREE(tmp_ctx);
171                 return false;
172         }
173
174         data.dsize = blob.length;
175         data.dptr = blob.data;
176
177         if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) == 0) {
178                 result = true;
179         }
180
181         TALLOC_FREE(tmp_ctx);
182
183         return result;
184 }
185
186 /***********************************************************************
187  Retrieves a netr_SamInfo3 structure from a tdb.  Caller must
188  free the user_info struct (malloc()'d memory)
189 ***********************************************************************/
190
191 struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid)
192 {
193         struct netr_SamInfo3 *info3 = NULL;
194         TDB_DATA data;
195         fstring keystr, tmp;
196         enum ndr_err_code ndr_err;
197         DATA_BLOB blob;
198         struct netsamlogoncache_entry r;
199
200         if (!netsamlogon_cache_init()) {
201                 DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
202                         NETSAMLOGON_TDB));
203                 return NULL;
204         }
205
206         /* Prepare key as DOMAIN-SID/USER-RID string */
207         slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, user_sid));
208         DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr));
209         data = tdb_fetch_bystring( netsamlogon_tdb, keystr );
210
211         if (!data.dptr) {
212                 return NULL;
213         }
214
215         info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
216         if (!info3) {
217                 goto done;
218         }
219
220         blob = data_blob_const(data.dptr, data.dsize);
221
222         ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
223                                       (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
224
225         if (DEBUGLEVEL >= 10) {
226                 NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
227         }
228
229         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
230                 DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n"));
231                 tdb_delete(netsamlogon_tdb, data);
232                 TALLOC_FREE(info3);
233                 goto done;
234         }
235
236         info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
237                                                       sizeof(r.info3));
238
239  done:
240         SAFE_FREE(data.dptr);
241
242         return info3;
243
244 #if 0   /* The netsamlogon cache needs to hang around.  Something about
245            this feels wrong, but it is the only way we can get all of the
246            groups.  The old universal groups cache didn't expire either.
247            --jerry */
248         {
249                 time_t          now = time(NULL);
250                 uint32          time_diff;
251
252                 /* is the entry expired? */
253                 time_diff = now - t;
254
255                 if ( (time_diff < 0 ) || (time_diff > lp_winbind_cache_time()) ) {
256                         DEBUG(10,("netsamlogon_cache_get: cache entry expired \n"));
257                         tdb_delete( netsamlogon_tdb, key );
258                         TALLOC_FREE( user );
259                 }
260         }
261 #endif
262 }
263
264 bool netsamlogon_cache_have(const struct dom_sid *user_sid)
265 {
266         TALLOC_CTX *mem_ctx = talloc_init("netsamlogon_cache_have");
267         struct netr_SamInfo3 *info3 = NULL;
268         bool result;
269
270         if (!mem_ctx)
271                 return False;
272
273         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
274
275         result = (info3 != NULL);
276
277         talloc_destroy(mem_ctx);
278
279         return result;
280 }