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