76110b89effa96acffcc25e21abaa32cb9f73e12
[samba.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 "../lib/tdb_compat/tdb_compat.h"
27 #include "../lib/util/util_tdb.h"
28 #include "../libcli/auth/schannel.h"
29 #include "../librpc/gen_ndr/ndr_schannel.h"
30 #include "lib/util/tdb_wrap.h"
31
32 #define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL"
33
34 /******************************************************************************
35  Open or create the schannel session store tdb.  Non-static so it can
36  be called from parent processes to corectly handle TDB_CLEAR_IF_FIRST
37 *******************************************************************************/
38
39 struct tdb_wrap *open_schannel_session_store(TALLOC_CTX *mem_ctx,
40                                              const char *private_dir)
41 {
42         struct tdb_wrap *tdb_sc = NULL;
43         char *fname = talloc_asprintf(mem_ctx, "%s/schannel_store.tdb", private_dir);
44
45         if (!fname) {
46                 return NULL;
47         }
48
49         tdb_sc = tdb_wrap_open(mem_ctx, fname, 0, TDB_CLEAR_IF_FIRST|TDB_NOSYNC, O_RDWR|O_CREAT, 0600);
50
51         if (!tdb_sc) {
52                 DEBUG(0,("open_schannel_session_store: Failed to open %s - %s\n",
53                          fname, strerror(errno)));
54                 TALLOC_FREE(fname);
55                 return NULL;
56         }
57
58         TALLOC_FREE(fname);
59
60         return tdb_sc;
61 }
62
63 /********************************************************************
64  ********************************************************************/
65
66 static
67 NTSTATUS schannel_store_session_key_tdb(struct tdb_wrap *tdb_sc,
68                                         TALLOC_CTX *mem_ctx,
69                                         struct netlogon_creds_CredentialState *creds)
70 {
71         enum ndr_err_code ndr_err;
72         DATA_BLOB blob;
73         TDB_DATA value;
74         int ret;
75         char *keystr;
76         char *name_upper;
77
78         name_upper = strupper_talloc(mem_ctx, creds->computer_name);
79         if (!name_upper) {
80                 return NT_STATUS_NO_MEMORY;
81         }
82
83         keystr = talloc_asprintf(mem_ctx, "%s/%s",
84                                  SECRETS_SCHANNEL_STATE, name_upper);
85         TALLOC_FREE(name_upper);
86         if (!keystr) {
87                 return NT_STATUS_NO_MEMORY;
88         }
89
90         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, creds,
91                         (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
92         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
93                 talloc_free(keystr);
94                 return ndr_map_error2ntstatus(ndr_err);
95         }
96
97         value.dptr = blob.data;
98         value.dsize = blob.length;
99
100         ret = tdb_store_bystring(tdb_sc->tdb, keystr, value, TDB_REPLACE);
101         if (ret != TDB_SUCCESS) {
102                 DEBUG(0,("Unable to add %s to session key db - %s\n",
103                          keystr, tdb_errorstr_compat(tdb_sc->tdb)));
104                 talloc_free(keystr);
105                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
106         }
107
108         DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
109                 keystr));
110
111         if (DEBUGLEVEL >= 10) {
112                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
113         }
114
115         talloc_free(keystr);
116
117         return NT_STATUS_OK;
118 }
119
120 /********************************************************************
121  ********************************************************************/
122
123 static
124 NTSTATUS schannel_fetch_session_key_tdb(struct tdb_wrap *tdb_sc,
125                                         TALLOC_CTX *mem_ctx,
126                                         const char *computer_name,
127                                         struct netlogon_creds_CredentialState **pcreds)
128 {
129         NTSTATUS status;
130         TDB_DATA value;
131         enum ndr_err_code ndr_err;
132         DATA_BLOB blob;
133         struct netlogon_creds_CredentialState *creds = NULL;
134         char *keystr = NULL;
135         char *name_upper;
136
137         *pcreds = NULL;
138
139         name_upper = strupper_talloc(mem_ctx, computer_name);
140         if (!name_upper) {
141                 return NT_STATUS_NO_MEMORY;
142         }
143
144         keystr = talloc_asprintf(mem_ctx, "%s/%s",
145                                  SECRETS_SCHANNEL_STATE, name_upper);
146         TALLOC_FREE(name_upper);
147         if (!keystr) {
148                 return NT_STATUS_NO_MEMORY;
149         }
150
151         value = tdb_fetch_bystring(tdb_sc->tdb, keystr);
152         if (!value.dptr) {
153                 DEBUG(10,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
154                         keystr ));
155                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
156                 goto done;
157         }
158
159         creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
160         if (!creds) {
161                 status = NT_STATUS_NO_MEMORY;
162                 goto done;
163         }
164
165         blob = data_blob_const(value.dptr, value.dsize);
166
167         ndr_err = ndr_pull_struct_blob(&blob, creds, creds,
168                         (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
169         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
170                 status = ndr_map_error2ntstatus(ndr_err);
171                 goto done;
172         }
173
174         if (DEBUGLEVEL >= 10) {
175                 NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
176         }
177
178         DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
179                 keystr));
180
181         status = NT_STATUS_OK;
182
183  done:
184
185         talloc_free(keystr);
186         SAFE_FREE(value.dptr);
187
188         if (!NT_STATUS_IS_OK(status)) {
189                 talloc_free(creds);
190                 return status;
191         }
192
193         *pcreds = creds;
194
195         return NT_STATUS_OK;
196 }
197
198 /******************************************************************************
199  Wrapper around schannel_fetch_session_key_tdb()
200  Note we must be root here.
201 *******************************************************************************/
202
203 NTSTATUS schannel_get_creds_state(TALLOC_CTX *mem_ctx,
204                                   const char *db_priv_dir,
205                                   const char *computer_name,
206                                   struct netlogon_creds_CredentialState **_creds)
207 {
208         TALLOC_CTX *tmpctx;
209         struct tdb_wrap *tdb_sc;
210         struct netlogon_creds_CredentialState *creds;
211         NTSTATUS status;
212
213         tmpctx = talloc_named(mem_ctx, 0, "schannel_get_creds_state");
214         if (!tmpctx) {
215                 return NT_STATUS_NO_MEMORY;
216         }
217
218         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
219         if (!tdb_sc) {
220                 return NT_STATUS_ACCESS_DENIED;
221         }
222
223         status = schannel_fetch_session_key_tdb(tdb_sc, tmpctx, 
224                                                 computer_name, &creds);
225         if (NT_STATUS_IS_OK(status)) {
226                 *_creds = talloc_steal(mem_ctx, creds);
227                 if (!*_creds) {
228                         status = NT_STATUS_NO_MEMORY;
229                 }
230         }
231
232         talloc_free(tmpctx);
233         return status;
234 }
235
236 /******************************************************************************
237  Wrapper around schannel_store_session_key_tdb()
238  Note we must be root here.
239 *******************************************************************************/
240
241 NTSTATUS schannel_save_creds_state(TALLOC_CTX *mem_ctx,
242                                    const char *db_priv_dir,
243                                    struct netlogon_creds_CredentialState *creds)
244 {
245         TALLOC_CTX *tmpctx;
246         struct tdb_wrap *tdb_sc;
247         NTSTATUS status;
248
249         tmpctx = talloc_named(mem_ctx, 0, "schannel_save_creds_state");
250         if (!tmpctx) {
251                 return NT_STATUS_NO_MEMORY;
252         }
253
254         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
255         if (!tdb_sc) {
256                 return NT_STATUS_ACCESS_DENIED;
257         }
258
259         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, creds);
260
261         talloc_free(tmpctx);
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 *db_priv_dir,
277                                     const char *computer_name,
278                                     struct netr_Authenticator *received_authenticator,
279                                     struct netr_Authenticator *return_authenticator,
280                                     struct netlogon_creds_CredentialState **creds_out)
281 {
282         TALLOC_CTX *tmpctx;
283         struct tdb_wrap *tdb_sc;
284         struct netlogon_creds_CredentialState *creds;
285         NTSTATUS status;
286         int ret;
287
288         tmpctx = talloc_named(mem_ctx, 0, "schannel_check_creds_state");
289         if (!tmpctx) {
290                 return NT_STATUS_NO_MEMORY;
291         }
292
293         tdb_sc = open_schannel_session_store(tmpctx, db_priv_dir);
294         if (!tdb_sc) {
295                 status = NT_STATUS_ACCESS_DENIED;
296                 goto done;
297         }
298
299         ret = tdb_transaction_start(tdb_sc->tdb);
300         if (ret != 0) {
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_sc, tmpctx, 
310                                                 computer_name, &creds);
311         if (!NT_STATUS_IS_OK(status)) {
312                 tdb_transaction_cancel(tdb_sc->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_sc->tdb);
321                 goto done;
322         }
323
324         status = schannel_store_session_key_tdb(tdb_sc, tmpctx, creds);
325         if (!NT_STATUS_IS_OK(status)) {
326                 tdb_transaction_cancel(tdb_sc->tdb);
327                 goto done;
328         }
329
330         tdb_transaction_commit(tdb_sc->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         return status;
345 }
346