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"
32 #include "../libcli/auth/schannel_state_proto.h"
34 static struct ldb_val
*schannel_dom_sid_ldb_val(TALLOC_CTX
*mem_ctx
,
37 enum ndr_err_code ndr_err
;
40 v
= talloc(mem_ctx
, struct ldb_val
);
43 ndr_err
= ndr_push_struct_blob(v
, mem_ctx
, NULL
, sid
,
44 (ndr_push_flags_fn_t
)ndr_push_dom_sid
);
45 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
53 static struct dom_sid
*schannel_ldb_val_dom_sid(TALLOC_CTX
*mem_ctx
,
54 const struct ldb_val
*v
)
56 enum ndr_err_code ndr_err
;
59 sid
= talloc(mem_ctx
, struct dom_sid
);
60 if (!sid
) return NULL
;
62 ndr_err
= ndr_pull_struct_blob(v
, sid
, NULL
, sid
,
63 (ndr_pull_flags_fn_t
)ndr_pull_dom_sid
);
64 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
73 remember an established session key for a netr server authentication
74 use a simple ldb structure
76 NTSTATUS
schannel_store_session_key_ldb(struct ldb_context
*ldb
,
78 struct netlogon_creds_CredentialState
*creds
)
80 struct ldb_message
*msg
;
81 struct ldb_val val
, seed
, client_state
, server_state
;
82 struct ldb_val
*sid_val
;
87 f
= talloc_asprintf(mem_ctx
, "%u", (unsigned int)creds
->negotiate_flags
);
90 return NT_STATUS_NO_MEMORY
;
93 sct
= talloc_asprintf(mem_ctx
, "%u", (unsigned int)creds
->secure_channel_type
);
96 return NT_STATUS_NO_MEMORY
;
99 msg
= ldb_msg_new(ldb
);
101 return NT_STATUS_NO_MEMORY
;
104 msg
->dn
= ldb_dn_new_fmt(msg
, ldb
, "computerName=%s", creds
->computer_name
);
106 return NT_STATUS_NO_MEMORY
;
109 sid_val
= schannel_dom_sid_ldb_val(msg
, creds
->sid
);
110 if (sid_val
== NULL
) {
111 return NT_STATUS_NO_MEMORY
;
114 val
.data
= creds
->session_key
;
115 val
.length
= sizeof(creds
->session_key
);
117 seed
.data
= creds
->seed
.data
;
118 seed
.length
= sizeof(creds
->seed
.data
);
120 client_state
.data
= creds
->client
.data
;
121 client_state
.length
= sizeof(creds
->client
.data
);
122 server_state
.data
= creds
->server
.data
;
123 server_state
.length
= sizeof(creds
->server
.data
);
125 ldb_msg_add_string(msg
, "objectClass", "schannelState");
126 ldb_msg_add_value(msg
, "sessionKey", &val
, NULL
);
127 ldb_msg_add_value(msg
, "seed", &seed
, NULL
);
128 ldb_msg_add_value(msg
, "clientState", &client_state
, NULL
);
129 ldb_msg_add_value(msg
, "serverState", &server_state
, NULL
);
130 ldb_msg_add_string(msg
, "negotiateFlags", f
);
131 ldb_msg_add_string(msg
, "secureChannelType", sct
);
132 ldb_msg_add_string(msg
, "accountName", creds
->account_name
);
133 ldb_msg_add_string(msg
, "computerName", creds
->computer_name
);
134 ldb_msg_add_value(msg
, "objectSid", sid_val
, NULL
);
136 ret
= ldb_add(ldb
, msg
);
137 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
139 /* from samdb_replace() */
140 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
141 for (i
=0;i
<msg
->num_elements
;i
++) {
142 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
145 ret
= ldb_modify(ldb
, msg
);
148 /* We don't need a transaction here, as we either add or
149 * modify records, never delete them, so it must exist */
151 if (ret
!= LDB_SUCCESS
) {
152 DEBUG(0,("Unable to add %s to session key db - %s\n",
153 ldb_dn_get_linearized(msg
->dn
), ldb_errstring(ldb
)));
154 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
161 read back a credentials back for a computer
163 NTSTATUS
schannel_fetch_session_key_ldb(struct ldb_context
*ldb
,
165 const char *computer_name
,
166 struct netlogon_creds_CredentialState
**creds
)
168 struct ldb_result
*res
;
170 const struct ldb_val
*val
;
172 *creds
= talloc_zero(mem_ctx
, struct netlogon_creds_CredentialState
);
174 return NT_STATUS_NO_MEMORY
;
177 ret
= ldb_search(ldb
, mem_ctx
, &res
,
178 NULL
, LDB_SCOPE_SUBTREE
, NULL
,
179 "(computerName=%s)", computer_name
);
180 if (ret
!= LDB_SUCCESS
) {
181 DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name
, ldb_errstring(ldb
)));
182 return NT_STATUS_INVALID_HANDLE
;
184 if (res
->count
!= 1) {
185 DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name
, res
->count
));
187 return NT_STATUS_INVALID_HANDLE
;
190 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "sessionKey");
191 if (val
== NULL
|| val
->length
!= 16) {
192 DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name
));
194 return NT_STATUS_INTERNAL_ERROR
;
197 memcpy((*creds
)->session_key
, val
->data
, 16);
199 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "seed");
200 if (val
== NULL
|| val
->length
!= 8) {
201 DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name
));
203 return NT_STATUS_INTERNAL_ERROR
;
206 memcpy((*creds
)->seed
.data
, val
->data
, 8);
208 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "clientState");
209 if (val
== NULL
|| val
->length
!= 8) {
210 DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name
));
212 return NT_STATUS_INTERNAL_ERROR
;
214 memcpy((*creds
)->client
.data
, val
->data
, 8);
216 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "serverState");
217 if (val
== NULL
|| val
->length
!= 8) {
218 DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name
));
220 return NT_STATUS_INTERNAL_ERROR
;
222 memcpy((*creds
)->server
.data
, val
->data
, 8);
224 (*creds
)->negotiate_flags
= ldb_msg_find_attr_as_int(res
->msgs
[0], "negotiateFlags", 0);
226 (*creds
)->secure_channel_type
= ldb_msg_find_attr_as_int(res
->msgs
[0], "secureChannelType", 0);
228 (*creds
)->account_name
= talloc_strdup(*creds
, ldb_msg_find_attr_as_string(res
->msgs
[0], "accountName", NULL
));
229 if ((*creds
)->account_name
== NULL
) {
231 return NT_STATUS_NO_MEMORY
;
234 (*creds
)->computer_name
= talloc_strdup(*creds
, ldb_msg_find_attr_as_string(res
->msgs
[0], "computerName", NULL
));
235 if ((*creds
)->computer_name
== NULL
) {
237 return NT_STATUS_NO_MEMORY
;
240 val
= ldb_msg_find_ldb_val(res
->msgs
[0], "objectSid");
242 (*creds
)->sid
= schannel_ldb_val_dom_sid(*creds
, val
);
243 if ((*creds
)->sid
== NULL
) {
245 return NT_STATUS_INTERNAL_ERROR
;
248 (*creds
)->sid
= NULL
;
256 Validate an incoming authenticator against the credentials for the remote machine.
258 The credentials are (re)read and from the schannel database, and
259 written back after the caclulations are performed.
261 The creds_out parameter (if not NULL) returns the credentials, if
262 the caller needs some of that information.
265 NTSTATUS
schannel_creds_server_step_check_ldb(struct ldb_context
*ldb
,
267 const char *computer_name
,
268 bool schannel_required_for_call
,
269 bool schannel_in_use
,
270 struct netr_Authenticator
*received_authenticator
,
271 struct netr_Authenticator
*return_authenticator
,
272 struct netlogon_creds_CredentialState
**creds_out
)
274 struct netlogon_creds_CredentialState
*creds
;
278 ret
= ldb_transaction_start(ldb
);
280 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
283 /* Because this is a shared structure (even across
284 * disconnects) we must update the database every time we
285 * update the structure */
287 nt_status
= schannel_fetch_session_key_ldb(ldb
, ldb
, computer_name
,
290 /* If we are flaged that schannel is required for a call, and
291 * it is not in use, then make this an error */
293 /* It would be good to make this mandetory once schannel is
294 * negoiated, bu this is not what windows does */
295 if (schannel_required_for_call
&& !schannel_in_use
) {
296 DEBUG(0,("schannel_creds_server_step_check: client %s not using schannel for netlogon, despite negotiating it\n",
297 creds
->computer_name
));
298 ldb_transaction_cancel(ldb
);
299 return NT_STATUS_ACCESS_DENIED
;
302 if (NT_STATUS_IS_OK(nt_status
)) {
303 nt_status
= netlogon_creds_server_step_check(creds
,
304 received_authenticator
,
305 return_authenticator
);
308 if (NT_STATUS_IS_OK(nt_status
)) {
309 nt_status
= schannel_store_session_key_ldb(ldb
, mem_ctx
, creds
);
312 if (NT_STATUS_IS_OK(nt_status
)) {
313 ldb_transaction_commit(ldb
);
316 talloc_steal(mem_ctx
, creds
);
319 ldb_transaction_cancel(ldb
);