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/********************************************************************
30 ********************************************************************/
31
32NTSTATUS schannel_store_session_key_tdb(struct tdb_context *tdb,
33					TALLOC_CTX *mem_ctx,
34					struct netlogon_creds_CredentialState *creds)
35{
36	enum ndr_err_code ndr_err;
37	DATA_BLOB blob;
38	TDB_DATA value;
39	int ret;
40	char *keystr;
41
42	keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s",
43					    SECRETS_SCHANNEL_STATE,
44					    creds->computer_name);
45	if (!keystr) {
46		return NT_STATUS_NO_MEMORY;
47	}
48
49	ndr_err = ndr_push_struct_blob(&blob, mem_ctx, NULL, creds,
50			(ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
51	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
52		talloc_free(keystr);
53		return ndr_map_error2ntstatus(ndr_err);
54	}
55
56	value.dptr = blob.data;
57	value.dsize = blob.length;
58
59	ret = tdb_store_bystring(tdb, keystr, value, TDB_REPLACE);
60	if (ret != TDB_SUCCESS) {
61		DEBUG(0,("Unable to add %s to session key db - %s\n",
62			 keystr, tdb_errorstr(tdb)));
63		talloc_free(keystr);
64		return NT_STATUS_INTERNAL_DB_CORRUPTION;
65	}
66
67	DEBUG(3,("schannel_store_session_key_tdb: stored schannel info with key %s\n",
68		keystr));
69
70	if (DEBUGLEVEL >= 10) {
71		NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
72	}
73
74	talloc_free(keystr);
75
76	return NT_STATUS_OK;
77}
78
79/********************************************************************
80 ********************************************************************/
81
82NTSTATUS schannel_fetch_session_key_tdb(struct tdb_context *tdb,
83					TALLOC_CTX *mem_ctx,
84					const char *computer_name,
85					struct netlogon_creds_CredentialState **pcreds)
86{
87	NTSTATUS status;
88	TDB_DATA value;
89	enum ndr_err_code ndr_err;
90	DATA_BLOB blob;
91	struct netlogon_creds_CredentialState *creds = NULL;
92	char *keystr = NULL;
93
94	*pcreds = NULL;
95
96	keystr = talloc_asprintf_strupper_m(mem_ctx, "%s/%s",
97					    SECRETS_SCHANNEL_STATE,
98					    computer_name);
99	if (!keystr) {
100		status = NT_STATUS_NO_MEMORY;
101		goto done;
102	}
103
104	value = tdb_fetch_bystring(tdb, keystr);
105	if (!value.dptr) {
106		DEBUG(0,("schannel_fetch_session_key_tdb: Failed to find entry with key %s\n",
107			keystr ));
108		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
109		goto done;
110	}
111
112	creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
113	if (!creds) {
114		status = NT_STATUS_NO_MEMORY;
115		goto done;
116	}
117
118	blob = data_blob_const(value.dptr, value.dsize);
119
120	ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, creds,
121			(ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
122	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
123		status = ndr_map_error2ntstatus(ndr_err);
124		goto done;
125	}
126
127	if (DEBUGLEVEL >= 10) {
128		NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds);
129	}
130
131	DEBUG(3,("schannel_fetch_session_key_tdb: restored schannel info key %s\n",
132		keystr));
133
134	status = NT_STATUS_OK;
135
136 done:
137
138	talloc_free(keystr);
139
140	if (!NT_STATUS_IS_OK(status)) {
141		talloc_free(creds);
142		return status;
143	}
144
145	*pcreds = creds;
146
147	return NT_STATUS_OK;
148}
149
150/********************************************************************
151
152  Validate an incoming authenticator against the credentials for the remote
153  machine.
154
155  The credentials are (re)read and from the schannel database, and
156  written back after the caclulations are performed.
157
158  The creds_out parameter (if not NULL) returns the credentials, if
159  the caller needs some of that information.
160
161 ********************************************************************/
162
163NTSTATUS schannel_creds_server_step_check_tdb(struct tdb_context *tdb,
164					      TALLOC_CTX *mem_ctx,
165					      const char *computer_name,
166					      bool schannel_required_for_call,
167					      bool schannel_in_use,
168					      struct netr_Authenticator *received_authenticator,
169					      struct netr_Authenticator *return_authenticator,
170					      struct netlogon_creds_CredentialState **creds_out)
171{
172	struct netlogon_creds_CredentialState *creds;
173	NTSTATUS status;
174	int ret;
175
176	ret = tdb_transaction_start(tdb);
177	if (ret != 0) {
178		return NT_STATUS_INTERNAL_DB_CORRUPTION;
179	}
180
181	/* Because this is a shared structure (even across
182	 * disconnects) we must update the database every time we
183	 * update the structure */
184
185	status = schannel_fetch_session_key_tdb(tdb, mem_ctx, computer_name,
186						&creds);
187
188	/* If we are flaged that schannel is required for a call, and
189	 * it is not in use, then make this an error */
190
191	/* It would be good to make this mandatory once schannel is
192	 * negotiated, but this is not what windows does */
193	if (schannel_required_for_call && !schannel_in_use) {
194		DEBUG(0,("schannel_creds_server_step_check_tdb: "
195			"client %s not using schannel for netlogon, despite negotiating it\n",
196			creds->computer_name ));
197		tdb_transaction_cancel(tdb);
198		return NT_STATUS_ACCESS_DENIED;
199	}
200
201	if (NT_STATUS_IS_OK(status)) {
202		status = netlogon_creds_server_step_check(creds,
203							  received_authenticator,
204							  return_authenticator);
205	}
206
207	if (NT_STATUS_IS_OK(status)) {
208		status = schannel_store_session_key_tdb(tdb, mem_ctx, creds);
209	}
210
211	if (NT_STATUS_IS_OK(status)) {
212		tdb_transaction_commit(tdb);
213		if (creds_out) {
214			*creds_out = creds;
215			talloc_steal(mem_ctx, creds);
216		}
217	} else {
218		tdb_transaction_cancel(tdb);
219	}
220
221	return status;
222}
223