heimdal: don't include <ifaddrs.h> without knowing it's there
[Samba/bb.git] / libcli / auth / schannel_state.c
blobe01330010b21b8955877964649c79d6aef9e6f55
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"
33 static struct ldb_val *schannel_dom_sid_ldb_val(TALLOC_CTX *mem_ctx,
34 struct dom_sid *sid)
36 enum ndr_err_code ndr_err;
37 struct ldb_val *v;
39 v = talloc(mem_ctx, struct ldb_val);
40 if (!v) return NULL;
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)) {
45 talloc_free(v);
46 return NULL;
49 return v;
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;
56 struct dom_sid *sid;
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)) {
64 talloc_free(sid);
65 return NULL;
67 return sid;
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,
76 TALLOC_CTX *mem_ctx,
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;
82 char *f;
83 char *sct;
84 int ret;
86 f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
88 if (f == NULL) {
89 return NT_STATUS_NO_MEMORY;
92 sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
94 if (sct == NULL) {
95 return NT_STATUS_NO_MEMORY;
98 msg = ldb_msg_new(ldb);
99 if (msg == NULL) {
100 return NT_STATUS_NO_MEMORY;
103 msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
104 if ( ! msg->dn) {
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) {
137 int i;
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;
156 return NT_STATUS_OK;
160 read back a credentials back for a computer
162 NTSTATUS schannel_fetch_session_key(struct ldb_context *ldb,
163 TALLOC_CTX *mem_ctx,
164 const char *computer_name,
165 struct netlogon_creds_CredentialState **creds)
167 struct ldb_result *res;
168 int ret;
169 const struct ldb_val *val;
171 *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
172 if (!*creds) {
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));
185 talloc_free(res);
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));
192 talloc_free(res);
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));
201 talloc_free(res);
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));
210 talloc_free(res);
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));
218 talloc_free(res);
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) {
229 talloc_free(res);
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) {
235 talloc_free(res);
236 return NT_STATUS_NO_MEMORY;
239 val = ldb_msg_find_ldb_val(res->msgs[0], "objectSid");
240 if (val) {
241 (*creds)->sid = schannel_ldb_val_dom_sid(*creds, val);
242 if ((*creds)->sid == NULL) {
243 talloc_free(res);
244 return NT_STATUS_INTERNAL_ERROR;
246 } else {
247 (*creds)->sid = NULL;
250 talloc_free(res);
251 return NT_STATUS_OK;
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,
265 TALLOC_CTX *mem_ctx,
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;
274 NTSTATUS nt_status;
275 int ret;
277 ret = ldb_transaction_start(ldb);
278 if (ret != 0) {
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,
287 &creds);
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);
313 if (creds_out) {
314 *creds_out = creds;
315 talloc_steal(mem_ctx, creds);
317 } else {
318 ldb_transaction_cancel(ldb);
320 return nt_status;