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