libcli/auth/schannel_state_tdb.c - fix an obviously wrong error handling
[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.
39 *******************************************************************************/
40
41 #define SCHANNEL_STORE_VERSION_1 1
42 #define SCHANNEL_STORE_VERSION_2 2 /* should not be used */
43 #define SCHANNEL_STORE_VERSION_CURRENT SCHANNEL_STORE_VERSION_1
44
45 static struct tdb_wrap *open_schannel_session_store(TALLOC_CTX *mem_ctx,
46                                                     const char *private_dir)
47 {
48         TDB_DATA vers;
49         uint32_t ver;
50         struct tdb_wrap *tdb_sc = NULL;
51         char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", private_dir);
52
53         if (!fname) {
54                 return NULL;
55         }
56
57         tdb_sc = tdb_wrap_open(mem_ctx, fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
58
59         if (!tdb_sc) {
60                 DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n",
61                          fname, strerror(errno)));
62                 TALLOC_FREE(fname);
63                 return NULL;
64         }
65
66  again:
67         vers = tdb_fetch_bystring(tdb_sc->tdb, "SCHANNEL_STORE_VERSION");
68         if (vers.dptr == NULL) {
69                 /* First opener, no version. */
70                 SIVAL(&ver,0,SCHANNEL_STORE_VERSION_CURRENT);
71                 vers.dptr = (uint8_t *)&ver;
72                 vers.dsize = 4;
73                 tdb_store_bystring(tdb_sc->tdb, "SCHANNEL_STORE_VERSION", vers, TDB_REPLACE);
74                 vers.dptr = NULL;
75         } else if (vers.dsize == 4) {
76                 ver = IVAL(vers.dptr,0);
77                 if (ver == SCHANNEL_STORE_VERSION_2) {
78                         DEBUG(0,("open_schannel_session_store: wrong version number %d in %s\n",
79                                 (int)ver, fname ));
80                         tdb_wipe_all(tdb_sc->tdb);
81                         goto again;
82                 }
83                 if (ver != SCHANNEL_STORE_VERSION_CURRENT) {
84                         DEBUG(0,("open_schannel_session_store: wrong version number %d in %s\n",
85                                 (int)ver, fname ));
86                         TALLOC_FREE(tdb_sc);
87                 }
88         } else {
89                 TALLOC_FREE(tdb_sc);
90                 DEBUG(0,("open_schannel_session_store: wrong version number size %d in %s\n",
91                         (int)vers.dsize, fname ));
92         }
93
94         SAFE_FREE(vers.dptr);
95         TALLOC_FREE(fname);
96
97         return tdb_sc;
98 }
99
100 /********************************************************************
101  ********************************************************************/
102
103 static
104 NTSTATUS schannel_store_session_key_tdb(struct tdb_wrap *tdb_sc,
105                                         TALLOC_CTX *mem_ctx,
106                                         struct smb_iconv_convenience *ic,
107                                         struct netlogon_creds_CredentialState *creds)
108 {
109         enum ndr_err_code ndr_err;
110         DATA_BLOB blob;
111         TDB_DATA value;
112         int ret;
113         char *keystr;
114         char *name_upper;
115
116         name_upper = strupper_talloc(mem_ctx, creds->computer_name);
117         if (!name_upper) {
118                 return NT_STATUS_NO_MEMORY;
119         }
120
121         keystr = talloc_asprintf(mem_ctx, "%s/%s",
122                                  SECRETS_SCHANNEL_STATE, name_upper);
123         TALLOC_FREE(name_upper);
124         if (!keystr) {
125                 return NT_STATUS_NO_MEMORY;
126         }
127
128         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, ic, creds,
129                         (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
130         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
131                 talloc_free(keystr);
132                 return ndr_map_error2ntstatus(ndr_err);
133         }
134
135         value.dptr = blob.data;
136         value.dsize = blob.length;
137
138         ret = tdb_store_bystring(tdb_sc->tdb, keystr, value, TDB_REPLACE);
139         if (ret != TDB_SUCCESS) {
140                 DEBUG(0,("Unable to add %s to session key db - %s\n",
141                          keystr, tdb_errorstr(tdb_sc->tdb)));
142                 talloc_free(keystr);
143                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
144         }
145
146         DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
147                 keystr));
148
149         if (DEBUGLEVEL >= 10) {
150                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
151         }
152
153         talloc_free(keystr);
154
155         return NT_STATUS_OK;
156 }
157
158 /********************************************************************
159  ********************************************************************/
160
161 static
162 NTSTATUS schannel_fetch_session_key_tdb(struct tdb_wrap *tdb_sc,
163                                         TALLOC_CTX *mem_ctx,
164                                         struct smb_iconv_convenience *ic,
165                                         const char *computer_name,
166                                         struct netlogon_creds_CredentialState **pcreds)
167 {
168         NTSTATUS status;
169         TDB_DATA value;
170         enum ndr_err_code ndr_err;
171         DATA_BLOB blob;
172         struct netlogon_creds_CredentialState *creds = NULL;
173         char *keystr = NULL;
174         char *name_upper;
175
176         *pcreds = NULL;
177
178         name_upper = strupper_talloc(mem_ctx, computer_name);
179         if (!name_upper) {
180                 return NT_STATUS_NO_MEMORY;
181         }
182
183         keystr = talloc_asprintf(mem_ctx, "%s/%s",
184                                  SECRETS_SCHANNEL_STATE, name_upper);
185         TALLOC_FREE(name_upper);
186         if (!keystr) {
187                 status = NT_STATUS_NO_MEMORY;
188                 goto done;
189         }
190
191         value = tdb_fetch_bystring(tdb_sc->tdb, keystr);
192         if (!value.dptr) {
193                 DEBUG(0,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
194                         keystr ));
195                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
196                 goto done;
197         }
198
199         creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
200         if (!creds) {
201                 status = NT_STATUS_NO_MEMORY;
202                 goto done;
203         }
204
205         blob = data_blob_const(value.dptr, value.dsize);
206
207         ndr_err = ndr_pull_struct_blob(&blob, creds, ic, creds,
208                         (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
209         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
210                 status = ndr_map_error2ntstatus(ndr_err);
211                 goto done;
212         }
213
214         if (DEBUGLEVEL >= 10) {
215                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
216         }
217
218         DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
219                 keystr));
220
221         status = NT_STATUS_OK;
222
223  done:
224
225         talloc_free(keystr);
226
227         if (!NT_STATUS_IS_OK(status)) {
228                 talloc_free(creds);
229                 return status;
230         }
231
232         *pcreds = creds;
233
234         return NT_STATUS_OK;
235 }
236
237 /******************************************************************************
238  Wrapper around schannel_fetch_session_key_tdb()
239  Note we must be root here.
240 *******************************************************************************/
241
242 NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
243                                   struct smb_iconv_convenience *ic,
244                                   const char *db_priv_dir,
245                                   const char *computer_name,
246                                   struct netlogon_creds_CredentialState **_creds)
247 {
248         TALLOC_CTX *tmpctx;
249         struct tdb_wrap *tdb_sc;
250         struct netlogon_creds_CredentialState *creds;
251         NTSTATUS status;
252
253         tmpctx = talloc_named(mem_ctx, 0, "schannel_get_creds_state");
254         if (!tmpctx) {
255                 return NT_STATUS_NO_MEMORY;
256         }
257
258         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
259         if (!tdb_sc) {
260                 return NT_STATUS_ACCESS_DENIED;
261         }
262
263         status = schannel_fetch_session_key_tdb(tdb_sc, tmpctx, ic,
264                                                 computer_name, &creds);
265         if (NT_STATUS_IS_OK(status)) {
266                 *_creds = talloc_steal(mem_ctx, creds);
267                 if (!*_creds) {
268                         status = NT_STATUS_NO_MEMORY;
269                 }
270         }
271
272         talloc_free(tmpctx);
273         return status;
274 }
275
276 /******************************************************************************
277  Wrapper around schannel_store_session_key_tdb()
278  Note we must be root here.
279 *******************************************************************************/
280
281 NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
282                                    struct smb_iconv_convenience *ic,
283                                    const char *db_priv_dir,
284                                    struct netlogon_creds_CredentialState *creds)
285 {
286         TALLOC_CTX *tmpctx;
287         struct tdb_wrap *tdb_sc;
288         NTSTATUS status;
289
290         tmpctx = talloc_named(mem_ctx, 0, "schannel_save_creds_state");
291         if (!tmpctx) {
292                 return NT_STATUS_NO_MEMORY;
293         }
294
295         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
296         if (!tdb_sc) {
297                 return NT_STATUS_ACCESS_DENIED;
298         }
299
300         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, ic, creds);
301
302         talloc_free(tmpctx);
303         return status;
304 }
305
306 /********************************************************************
307  Validate an incoming authenticator against the credentials for the
308  remote machine stored in the schannel database.
309
310  The credentials are (re)read and from the schannel database, and
311  written back after the caclulations are performed.
312
313  If the creds_out parameter is not NULL returns the credentials.
314  ********************************************************************/
315
316 NTSTATUS schannel_check_creds_state(TALLOC_CTX *mem_ctx,
317                                     struct smb_iconv_convenience *ic,
318                                     const char *db_priv_dir,
319                                     const char *computer_name,
320                                     struct netr_Authenticator *received_authenticator,
321                                     struct netr_Authenticator *return_authenticator,
322                                     struct netlogon_creds_CredentialState **creds_out)
323 {
324         TALLOC_CTX *tmpctx;
325         struct tdb_wrap *tdb_sc;
326         struct netlogon_creds_CredentialState *creds;
327         NTSTATUS status;
328         int ret;
329
330         tmpctx = talloc_named(mem_ctx, 0, "schannel_check_creds_state");
331         if (!tmpctx) {
332                 return NT_STATUS_NO_MEMORY;
333         }
334
335         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
336         if (!tdb_sc) {
337                 status = NT_STATUS_ACCESS_DENIED;
338                 goto done;
339         }
340
341         ret = tdb_transaction_start(tdb_sc->tdb);
342         if (ret != 0) {
343                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
344                 goto done;
345         }
346
347         /* Because this is a shared structure (even across
348          * disconnects) we must update the database every time we
349          * update the structure */
350
351         status = schannel_fetch_session_key_tdb(tdb_sc, tmpctx, ic,
352                                                 computer_name, &creds);
353         if (!NT_STATUS_IS_OK(status)) {
354                 tdb_transaction_cancel(tdb_sc->tdb);
355                 goto done;
356         }
357
358         status = netlogon_creds_server_step_check(creds,
359                                                   received_authenticator,
360                                                   return_authenticator);
361         if (!NT_STATUS_IS_OK(status)) {
362                 tdb_transaction_cancel(tdb_sc->tdb);
363                 goto done;
364         }
365
366         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, ic, creds);
367         if (!NT_STATUS_IS_OK(status)) {
368                 tdb_transaction_cancel(tdb_sc->tdb);
369                 goto done;
370         }
371
372         tdb_transaction_commit(tdb_sc->tdb);
373
374         if (creds_out) {
375                 *creds_out = talloc_steal(mem_ctx, creds);
376                 if (!*creds_out) {
377                         status = NT_STATUS_NO_MEMORY;
378                         goto done;
379                 }
380         }
381
382         status = NT_STATUS_OK;
383
384 done:
385         talloc_free(tmpctx);
386         return status;
387 }
388