s3: Simplify the code a bit: Catch (len==0) early
[Samba.git] / libcli / auth / schannel_state_ldb.c
blob69d6bad22c2de123cacdcce15fa25ebd20e7cd51
1 /*
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/>.
23 #include "includes.h"
24 #include "lib/ldb/include/ldb.h"
25 #include "librpc/gen_ndr/ndr_security.h"
26 #include "ldb_wrap.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,
35 struct dom_sid *sid)
37 enum ndr_err_code ndr_err;
38 struct ldb_val *v;
40 v = talloc(mem_ctx, struct ldb_val);
41 if (!v) return NULL;
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)) {
46 talloc_free(v);
47 return NULL;
50 return v;
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;
57 struct dom_sid *sid;
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)) {
65 talloc_free(sid);
66 return NULL;
68 return sid;
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,
77 TALLOC_CTX *mem_ctx,
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;
83 char *f;
84 char *sct;
85 int ret;
87 f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
89 if (f == NULL) {
90 return NT_STATUS_NO_MEMORY;
93 sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
95 if (sct == NULL) {
96 return NT_STATUS_NO_MEMORY;
99 msg = ldb_msg_new(ldb);
100 if (msg == NULL) {
101 return NT_STATUS_NO_MEMORY;
104 msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
105 if ( ! msg->dn) {
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) {
138 int i;
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;
157 return NT_STATUS_OK;
161 read back a credentials back for a computer
163 NTSTATUS schannel_fetch_session_key_ldb(struct ldb_context *ldb,
164 TALLOC_CTX *mem_ctx,
165 const char *computer_name,
166 struct netlogon_creds_CredentialState **creds)
168 struct ldb_result *res;
169 int ret;
170 const struct ldb_val *val;
172 *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
173 if (!*creds) {
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));
186 talloc_free(res);
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));
193 talloc_free(res);
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));
202 talloc_free(res);
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));
211 talloc_free(res);
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));
219 talloc_free(res);
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) {
230 talloc_free(res);
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) {
236 talloc_free(res);
237 return NT_STATUS_NO_MEMORY;
240 val = ldb_msg_find_ldb_val(res->msgs[0], "objectSid");
241 if (val) {
242 (*creds)->sid = schannel_ldb_val_dom_sid(*creds, val);
243 if ((*creds)->sid == NULL) {
244 talloc_free(res);
245 return NT_STATUS_INTERNAL_ERROR;
247 } else {
248 (*creds)->sid = NULL;
251 talloc_free(res);
252 return NT_STATUS_OK;
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,
266 TALLOC_CTX *mem_ctx,
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;
275 NTSTATUS nt_status;
276 int ret;
278 ret = ldb_transaction_start(ldb);
279 if (ret != 0) {
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,
288 &creds);
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);
314 if (creds_out) {
315 *creds_out = creds;
316 talloc_steal(mem_ctx, creds);
318 } else {
319 ldb_transaction_cancel(ldb);
321 return nt_status;