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