2 Unix SMB/CIFS implementation.
4 module to store/fetch session keys for the schannel server
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/ldb/include/ldb.h"
25 #include "librpc/gen_ndr/ndr_security.h"
27 #include "../lib/util/util_ldb.h"
28 #include "libcli/auth/libcli_auth.h"
29 #include "auth/auth.h"
30 #include "param/param.h"
31 #include "auth/gensec/schannel_state.h"
33 static struct ldb_val
*schannel_dom_sid_ldb_val(TALLOC_CTX
*mem_ctx
,
36 enum ndr_err_code ndr_err
;
39 v
= talloc(mem_ctx
, struct ldb_val
);
42 ndr_err
= ndr_push_struct_blob(v
, mem_ctx
, NULL
, sid
,
43 (ndr_push_flags_fn_t
)ndr_push_dom_sid
);
44 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
52 static struct dom_sid
*schannel_ldb_val_dom_sid(TALLOC_CTX
*mem_ctx
,
53 const struct ldb_val
*v
)
55 enum ndr_err_code ndr_err
;
58 sid
= talloc(mem_ctx
, struct dom_sid
);
59 if (!sid
) return NULL
;
61 ndr_err
= ndr_pull_struct_blob(v
, sid
, NULL
, sid
,
62 (ndr_pull_flags_fn_t
)ndr_pull_dom_sid
);
63 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
72 remember an established session key for a netr server authentication
73 use a simple ldb structure
75 NTSTATUS
schannel_store_session_key(struct ldb_context
*ldb
,
77 struct netlogon_creds_CredentialState
*creds
)
79 struct ldb_message
*msg
;
80 struct ldb_val val
, seed
, client_state
, server_state
;
81 struct ldb_val
*sid_val
;
86 f
= talloc_asprintf(mem_ctx
, "%u", (unsigned int)creds
->negotiate_flags
);
89 return NT_STATUS_NO_MEMORY
;
92 sct
= talloc_asprintf(mem_ctx
, "%u", (unsigned int)creds
->secure_channel_type
);
95 return NT_STATUS_NO_MEMORY
;
98 msg
= ldb_msg_new(ldb
);
100 return NT_STATUS_NO_MEMORY
;
103 msg
->dn
= ldb_dn_new_fmt(msg
, ldb
, "computerName=%s", creds
->computer_name
);
105 return NT_STATUS_NO_MEMORY
;
108 sid_val
= schannel_dom_sid_ldb_val(msg
, creds
->sid
);
109 if (sid_val
== NULL
) {
110 return NT_STATUS_NO_MEMORY
;
113 val
.data
= creds
->session_key
;
114 val
.length
= sizeof(creds
->session_key
);
116 seed
.data
= creds
->seed
.data
;
117 seed
.length
= sizeof(creds
->seed
.data
);
119 client_state
.data
= creds
->client
.data
;
120 client_state
.length
= sizeof(creds
->client
.data
);
121 server_state
.data
= creds
->server
.data
;
122 server_state
.length
= sizeof(creds
->server
.data
);
124 ldb_msg_add_string(msg
, "objectClass", "schannelState");
125 ldb_msg_add_value(msg
, "sessionKey", &val
, NULL
);
126 ldb_msg_add_value(msg
, "seed", &seed
, NULL
);
127 ldb_msg_add_value(msg
, "clientState", &client_state
, NULL
);
128 ldb_msg_add_value(msg
, "serverState", &server_state
, NULL
);
129 ldb_msg_add_string(msg
, "negotiateFlags", f
);
130 ldb_msg_add_string(msg
, "secureChannelType", sct
);
131 ldb_msg_add_string(msg
, "accountName", creds
->account_name
);
132 ldb_msg_add_string(msg
, "computerName", creds
->computer_name
);
133 ldb_msg_add_value(msg
, "objectSid", sid_val
, NULL
);
135 ret
= ldb_add(ldb
, msg
);
136 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
138 /* from samdb_replace() */
139 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
140 for (i
=0;i
<msg
->num_elements
;i
++) {
141 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
144 ret
= ldb_modify(ldb
, msg
);
147 /* We don't need a transaction here, as we either add or
148 * modify records, never delete them, so it must exist */
150 if (ret
!= LDB_SUCCESS
) {
151 DEBUG(0,("Unable to add %s to session key db - %s\n",
152 ldb_dn_get_linearized(msg
->dn
), ldb_errstring(ldb
)));
153 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
160 read back a credentials back for a computer
162 NTSTATUS
schannel_fetch_session_key(struct ldb_context
*ldb
,
164 const char *computer_name
,
165 struct netlogon_creds_CredentialState
**creds
)
167 struct ldb_result
*res
;
169 const struct ldb_val
*val
;
171 *creds
= talloc_zero(mem_ctx
, struct netlogon_creds_CredentialState
);
173 return NT_STATUS_NO_MEMORY
;
176 ret
= ldb_search(ldb
, mem_ctx
, &res
,
177 NULL
, LDB_SCOPE_SUBTREE
, NULL
,
178 "(computerName=%s)", computer_name
);
179 if (ret
!= LDB_SUCCESS
) {
180 DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name
, ldb_errstring(ldb
)));
181 return NT_STATUS_INVALID_HANDLE
;
183 if (res
->count
!= 1) {
184 DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name
, res
->count
));
186 return NT_STATUS_INVALID_HANDLE
;
189 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "sessionKey");
190 if (val
== NULL
|| val
->length
!= 16) {
191 DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name
));
193 return NT_STATUS_INTERNAL_ERROR
;
196 memcpy((*creds
)->session_key
, val
->data
, 16);
198 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "seed");
199 if (val
== NULL
|| val
->length
!= 8) {
200 DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name
));
202 return NT_STATUS_INTERNAL_ERROR
;
205 memcpy((*creds
)->seed
.data
, val
->data
, 8);
207 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "clientState");
208 if (val
== NULL
|| val
->length
!= 8) {
209 DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name
));
211 return NT_STATUS_INTERNAL_ERROR
;
213 memcpy((*creds
)->client
.data
, val
->data
, 8);
215 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "serverState");
216 if (val
== NULL
|| val
->length
!= 8) {
217 DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name
));
219 return NT_STATUS_INTERNAL_ERROR
;
221 memcpy((*creds
)->server
.data
, val
->data
, 8);
223 (*creds
)->negotiate_flags
= ldb_msg_find_attr_as_int(res
->msgs
[0], "negotiateFlags", 0);
225 (*creds
)->secure_channel_type
= ldb_msg_find_attr_as_int(res
->msgs
[0], "secureChannelType", 0);
227 (*creds
)->account_name
= talloc_strdup(*creds
, ldb_msg_find_attr_as_string(res
->msgs
[0], "accountName", NULL
));
228 if ((*creds
)->account_name
== NULL
) {
230 return NT_STATUS_NO_MEMORY
;
233 (*creds
)->computer_name
= talloc_strdup(*creds
, ldb_msg_find_attr_as_string(res
->msgs
[0], "computerName", NULL
));
234 if ((*creds
)->computer_name
== NULL
) {
236 return NT_STATUS_NO_MEMORY
;
239 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "objectSid");
241 (*creds
)->sid
= schannel_ldb_val_dom_sid(*creds
, val
);
242 if ((*creds
)->sid
== NULL
) {
244 return NT_STATUS_INTERNAL_ERROR
;
247 (*creds
)->sid
= NULL
;
255 Validate an incoming authenticator against the credentials for the remote machine.
257 The credentials are (re)read and from the schannel database, and
258 written back after the caclulations are performed.
260 The creds_out parameter (if not NULL) returns the credentials, if
261 the caller needs some of that information.
264 NTSTATUS
schannel_creds_server_step_check(struct ldb_context
*ldb
,
266 const char *computer_name
,
267 bool schannel_required_for_call
,
268 bool schannel_in_use
,
269 struct netr_Authenticator
*received_authenticator
,
270 struct netr_Authenticator
*return_authenticator
,
271 struct netlogon_creds_CredentialState
**creds_out
)
273 struct netlogon_creds_CredentialState
*creds
;
277 ret
= ldb_transaction_start(ldb
);
279 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
282 /* Because this is a shared structure (even across
283 * disconnects) we must update the database every time we
284 * update the structure */
286 nt_status
= schannel_fetch_session_key(ldb
, ldb
, computer_name
,
289 /* If we are flaged that schannel is required for a call, and
290 * it is not in use, then make this an error */
292 /* It would be good to make this mandetory once schannel is
293 * negoiated, bu this is not what windows does */
294 if (schannel_required_for_call
&& !schannel_in_use
) {
295 DEBUG(0,("schannel_creds_server_step_check: client %s not using schannel for netlogon, despite negotiating it\n",
296 creds
->computer_name
));
297 ldb_transaction_cancel(ldb
);
298 return NT_STATUS_ACCESS_DENIED
;
301 if (NT_STATUS_IS_OK(nt_status
)) {
302 nt_status
= netlogon_creds_server_step_check(creds
,
303 received_authenticator
,
304 return_authenticator
);
307 if (NT_STATUS_IS_OK(nt_status
)) {
308 nt_status
= schannel_store_session_key(ldb
, mem_ctx
, creds
);
311 if (NT_STATUS_IS_OK(nt_status
)) {
312 ldb_transaction_commit(ldb
);
315 talloc_steal(mem_ctx
, creds
);
318 ldb_transaction_cancel(ldb
);