schannel Change to TDB_CLEAR_IF_FIRST to reduce fsync()
[metze/samba/wip.git] / libcli / auth / schannel_state_tdb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    module to store/fetch session keys for the schannel server
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
8    Copyright (C) Guenther Deschner 2009
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 <tdb.h>
27 #include "../lib/util/util_tdb.h"
28 #include "../libcli/auth/libcli_auth.h"
29 #include "../libcli/auth/schannel_state.h"
30 #include "../librpc/gen_ndr/ndr_schannel.h"
31 #if _SAMBA_BUILD_ == 4
32 #include "tdb_wrap.h"
33 #endif
34
35 #define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL"
36
37 /******************************************************************************
38  Open or create the schannel session store tdb.  Non-static so it can
39  be called from parent processes to corectly handle TDB_CLEAR_IF_FIRST
40 *******************************************************************************/
41
42 struct tdb_wrap *open_schannel_session_store(TALLOC_CTX *mem_ctx,
43                                              const char *private_dir)
44 {
45         struct tdb_wrap *tdb_sc = NULL;
46         char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", private_dir);
47
48         if (!fname) {
49                 return NULL;
50         }
51
52         tdb_sc = tdb_wrap_open(mem_ctx, fname, 0, TDB_CLEAR_IF_FIRST|TDB_NOSYNC, O_RDWR|O_CREAT, 0600);
53
54         if (!tdb_sc) {
55                 DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n",
56                          fname, strerror(errno)));
57                 TALLOC_FREE(fname);
58                 return NULL;
59         }
60
61         TALLOC_FREE(fname);
62
63         return tdb_sc;
64 }
65
66 /********************************************************************
67  ********************************************************************/
68
69 static
70 NTSTATUS schannel_store_session_key_tdb(struct tdb_wrap *tdb_sc,
71                                         TALLOC_CTX *mem_ctx,
72                                         struct netlogon_creds_CredentialState *creds)
73 {
74         enum ndr_err_code ndr_err;
75         DATA_BLOB blob;
76         TDB_DATA value;
77         int ret;
78         char *keystr;
79         char *name_upper;
80
81         name_upper = strupper_talloc(mem_ctx, creds->computer_name);
82         if (!name_upper) {
83                 return NT_STATUS_NO_MEMORY;
84         }
85
86         keystr = talloc_asprintf(mem_ctx, "%s/%s",
87                                  SECRETS_SCHANNEL_STATE, name_upper);
88         TALLOC_FREE(name_upper);
89         if (!keystr) {
90                 return NT_STATUS_NO_MEMORY;
91         }
92
93         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, creds,
94                         (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
95         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
96                 talloc_free(keystr);
97                 return ndr_map_error2ntstatus(ndr_err);
98         }
99
100         value.dptr = blob.data;
101         value.dsize = blob.length;
102
103         ret = tdb_store_bystring(tdb_sc->tdb, keystr, value, TDB_REPLACE);
104         if (ret != TDB_SUCCESS) {
105                 DEBUG(0,("Unable to add %s to session key db - %s\n",
106                          keystr, tdb_errorstr(tdb_sc->tdb)));
107                 talloc_free(keystr);
108                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
109         }
110
111         DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
112                 keystr));
113
114         if (DEBUGLEVEL >= 10) {
115                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
116         }
117
118         talloc_free(keystr);
119
120         return NT_STATUS_OK;
121 }
122
123 /********************************************************************
124  ********************************************************************/
125
126 static
127 NTSTATUS schannel_fetch_session_key_tdb(struct tdb_wrap *tdb_sc,
128                                         TALLOC_CTX *mem_ctx,
129                                         const char *computer_name,
130                                         struct netlogon_creds_CredentialState **pcreds)
131 {
132         NTSTATUS status;
133         TDB_DATA value;
134         enum ndr_err_code ndr_err;
135         DATA_BLOB blob;
136         struct netlogon_creds_CredentialState *creds = NULL;
137         char *keystr = NULL;
138         char *name_upper;
139
140         *pcreds = NULL;
141
142         name_upper = strupper_talloc(mem_ctx, computer_name);
143         if (!name_upper) {
144                 return NT_STATUS_NO_MEMORY;
145         }
146
147         keystr = talloc_asprintf(mem_ctx, "%s/%s",
148                                  SECRETS_SCHANNEL_STATE, name_upper);
149         TALLOC_FREE(name_upper);
150         if (!keystr) {
151                 return NT_STATUS_NO_MEMORY;
152         }
153
154         value = tdb_fetch_bystring(tdb_sc->tdb, keystr);
155         if (!value.dptr) {
156                 DEBUG(0,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
157                         keystr ));
158                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
159                 goto done;
160         }
161
162         creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
163         if (!creds) {
164                 status = NT_STATUS_NO_MEMORY;
165                 goto done;
166         }
167
168         blob = data_blob_const(value.dptr, value.dsize);
169
170         ndr_err = ndr_pull_struct_blob(&blob, creds, creds,
171                         (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
172         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
173                 status = ndr_map_error2ntstatus(ndr_err);
174                 goto done;
175         }
176
177         if (DEBUGLEVEL >= 10) {
178                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
179         }
180
181         DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
182                 keystr));
183
184         status = NT_STATUS_OK;
185
186  done:
187
188         talloc_free(keystr);
189         SAFE_FREE(value.dptr);
190
191         if (!NT_STATUS_IS_OK(status)) {
192                 talloc_free(creds);
193                 return status;
194         }
195
196         *pcreds = creds;
197
198         return NT_STATUS_OK;
199 }
200
201 /******************************************************************************
202  Wrapper around schannel_fetch_session_key_tdb()
203  Note we must be root here.
204 *******************************************************************************/
205
206 NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
207                                   const char *db_priv_dir,
208                                   const char *computer_name,
209                                   struct netlogon_creds_CredentialState **_creds)
210 {
211         TALLOC_CTX *tmpctx;
212         struct tdb_wrap *tdb_sc;
213         struct netlogon_creds_CredentialState *creds;
214         NTSTATUS status;
215
216         tmpctx = talloc_named(mem_ctx, 0, "schannel_get_creds_state");
217         if (!tmpctx) {
218                 return NT_STATUS_NO_MEMORY;
219         }
220
221         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
222         if (!tdb_sc) {
223                 return NT_STATUS_ACCESS_DENIED;
224         }
225
226         status = schannel_fetch_session_key_tdb(tdb_sc, tmpctx, 
227                                                 computer_name, &creds);
228         if (NT_STATUS_IS_OK(status)) {
229                 *_creds = talloc_steal(mem_ctx, creds);
230                 if (!*_creds) {
231                         status = NT_STATUS_NO_MEMORY;
232                 }
233         }
234
235         talloc_free(tmpctx);
236         return status;
237 }
238
239 /******************************************************************************
240  Wrapper around schannel_store_session_key_tdb()
241  Note we must be root here.
242 *******************************************************************************/
243
244 NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
245                                    const char *db_priv_dir,
246                                    struct netlogon_creds_CredentialState *creds)
247 {
248         TALLOC_CTX *tmpctx;
249         struct tdb_wrap *tdb_sc;
250         NTSTATUS status;
251
252         tmpctx = talloc_named(mem_ctx, 0, "schannel_save_creds_state");
253         if (!tmpctx) {
254                 return NT_STATUS_NO_MEMORY;
255         }
256
257         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
258         if (!tdb_sc) {
259                 return NT_STATUS_ACCESS_DENIED;
260         }
261
262         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, creds);
263
264         talloc_free(tmpctx);
265         return status;
266 }
267
268 /********************************************************************
269  Validate an incoming authenticator against the credentials for the
270  remote machine stored in the schannel database.
271
272  The credentials are (re)read and from the schannel database, and
273  written back after the caclulations are performed.
274
275  If the creds_out parameter is not NULL returns the credentials.
276  ********************************************************************/
277
278 NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx,
279                                     const char *db_priv_dir,
280                                     const char *computer_name,
281                                     struct netr_Authenticator *received_authenticator,
282                                     struct netr_Authenticator *return_authenticator,
283                                     struct netlogon_creds_CredentialState **creds_out)
284 {
285         TALLOC_CTX *tmpctx;
286         struct tdb_wrap *tdb_sc;
287         struct netlogon_creds_CredentialState *creds;
288         NTSTATUS status;
289         int ret;
290
291         tmpctx = talloc_named(mem_ctx, 0, "schannel_check_creds_state");
292         if (!tmpctx) {
293                 return NT_STATUS_NO_MEMORY;
294         }
295
296         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
297         if (!tdb_sc) {
298                 status = NT_STATUS_ACCESS_DENIED;
299                 goto done;
300         }
301
302         ret = tdb_transaction_start(tdb_sc->tdb);
303         if (ret != 0) {
304                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
305                 goto done;
306         }
307
308         /* Because this is a shared structure (even across
309          * disconnects) we must update the database every time we
310          * update the structure */
311
312         status = schannel_fetch_session_key_tdb(tdb_sc, tmpctx, 
313                                                 computer_name, &creds);
314         if (!NT_STATUS_IS_OK(status)) {
315                 tdb_transaction_cancel(tdb_sc->tdb);
316                 goto done;
317         }
318
319         status = netlogon_creds_server_step_check(creds,
320                                                   received_authenticator,
321                                                   return_authenticator);
322         if (!NT_STATUS_IS_OK(status)) {
323                 tdb_transaction_cancel(tdb_sc->tdb);
324                 goto done;
325         }
326
327         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, creds);
328         if (!NT_STATUS_IS_OK(status)) {
329                 tdb_transaction_cancel(tdb_sc->tdb);
330                 goto done;
331         }
332
333         tdb_transaction_commit(tdb_sc->tdb);
334
335         if (creds_out) {
336                 *creds_out = talloc_steal(mem_ctx, creds);
337                 if (!*creds_out) {
338                         status = NT_STATUS_NO_MEMORY;
339                         goto done;
340                 }
341         }
342
343         status = NT_STATUS_OK;
344
345 done:
346         talloc_free(tmpctx);
347         return status;
348 }
349