source: branches/samba-3.5.x/libcli/auth/schannel_state_ldb.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 9.5 KB
Line 
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
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.
13
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.
18
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/>.
21*/
22
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"
33
34static struct ldb_val *schannel_dom_sid_ldb_val(TALLOC_CTX *mem_ctx,
35 struct dom_sid *sid)
36{
37 enum ndr_err_code ndr_err;
38 struct ldb_val *v;
39
40 v = talloc(mem_ctx, struct ldb_val);
41 if (!v) return NULL;
42
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;
48 }
49
50 return v;
51}
52
53static struct dom_sid *schannel_ldb_val_dom_sid(TALLOC_CTX *mem_ctx,
54 const struct ldb_val *v)
55{
56 enum ndr_err_code ndr_err;
57 struct dom_sid *sid;
58
59 sid = talloc(mem_ctx, struct dom_sid);
60 if (!sid) return NULL;
61
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;
67 }
68 return sid;
69}
70
71
72/*
73 remember an established session key for a netr server authentication
74 use a simple ldb structure
75*/
76NTSTATUS schannel_store_session_key_ldb(struct ldb_context *ldb,
77 TALLOC_CTX *mem_ctx,
78 struct netlogon_creds_CredentialState *creds)
79{
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;
86
87 f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
88
89 if (f == NULL) {
90 return NT_STATUS_NO_MEMORY;
91 }
92
93 sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
94
95 if (sct == NULL) {
96 return NT_STATUS_NO_MEMORY;
97 }
98
99 msg = ldb_msg_new(ldb);
100 if (msg == NULL) {
101 return NT_STATUS_NO_MEMORY;
102 }
103
104 msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
105 if ( ! msg->dn) {
106 return NT_STATUS_NO_MEMORY;
107 }
108
109 sid_val = schannel_dom_sid_ldb_val(msg, creds->sid);
110 if (sid_val == NULL) {
111 return NT_STATUS_NO_MEMORY;
112 }
113
114 val.data = creds->session_key;
115 val.length = sizeof(creds->session_key);
116
117 seed.data = creds->seed.data;
118 seed.length = sizeof(creds->seed.data);
119
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);
124
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);
135
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;
143 }
144
145 ret = ldb_modify(ldb, msg);
146 }
147
148 /* We don't need a transaction here, as we either add or
149 * modify records, never delete them, so it must exist */
150
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;
155 }
156
157 return NT_STATUS_OK;
158}
159
160/*
161 read back a credentials back for a computer
162*/
163NTSTATUS 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)
167{
168 struct ldb_result *res;
169 int ret;
170 const struct ldb_val *val;
171
172 *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
173 if (!*creds) {
174 return NT_STATUS_NO_MEMORY;
175 }
176
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;
183 }
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;
188 }
189
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;
195 }
196
197 memcpy((*creds)->session_key, val->data, 16);
198
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;
204 }
205
206 memcpy((*creds)->seed.data, val->data, 8);
207
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;
213 }
214 memcpy((*creds)->client.data, val->data, 8);
215
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;
221 }
222 memcpy((*creds)->server.data, val->data, 8);
223
224 (*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
225
226 (*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
227
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;
232 }
233
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;
238 }
239
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;
246 }
247 } else {
248 (*creds)->sid = NULL;
249 }
250
251 talloc_free(res);
252 return NT_STATUS_OK;
253}
254
255/*
256 Validate an incoming authenticator against the credentials for the remote machine.
257
258 The credentials are (re)read and from the schannel database, and
259 written back after the caclulations are performed.
260
261 The creds_out parameter (if not NULL) returns the credentials, if
262 the caller needs some of that information.
263
264*/
265NTSTATUS 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)
273{
274 struct netlogon_creds_CredentialState *creds;
275 NTSTATUS nt_status;
276 int ret;
277
278 ret = ldb_transaction_start(ldb);
279 if (ret != 0) {
280 return NT_STATUS_INTERNAL_DB_CORRUPTION;
281 }
282
283 /* Because this is a shared structure (even across
284 * disconnects) we must update the database every time we
285 * update the structure */
286
287 nt_status = schannel_fetch_session_key_ldb(ldb, ldb, computer_name,
288 &creds);
289
290 /* If we are flaged that schannel is required for a call, and
291 * it is not in use, then make this an error */
292
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;
300 }
301
302 if (NT_STATUS_IS_OK(nt_status)) {
303 nt_status = netlogon_creds_server_step_check(creds,
304 received_authenticator,
305 return_authenticator);
306 }
307
308 if (NT_STATUS_IS_OK(nt_status)) {
309 nt_status = schannel_store_session_key_ldb(ldb, mem_ctx, creds);
310 }
311
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);
317 }
318 } else {
319 ldb_transaction_cancel(ldb);
320 }
321 return nt_status;
322}
Note: See TracBrowser for help on using the repository browser.