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