/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- schannel_dom_sid_ldb_val
- schannel_ldb_val_dom_sid
- schannel_db_connect
- schannel_store_session_key_ldb
- schannel_store_session_key
- schannel_fetch_session_key_ldb
- schannel_fetch_session_key
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/ldb/include/ldb.h"
24 #include "librpc/gen_ndr/ndr_security.h"
25 #include "ldb_wrap.h"
26 #include "../lib/util/util_ldb.h"
27 #include "libcli/auth/libcli_auth.h"
28 #include "auth/auth.h"
29 #include "param/param.h"
30 #include "auth/gensec/schannel_state.h"
31
32 static struct ldb_val *schannel_dom_sid_ldb_val(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
33 struct smb_iconv_convenience *smbiconv,
34 struct dom_sid *sid)
35 {
36 enum ndr_err_code ndr_err;
37 struct ldb_val *v;
38
39 v = talloc(mem_ctx, struct ldb_val);
40 if (!v) return NULL;
41
42 ndr_err = ndr_push_struct_blob(v, mem_ctx, smbiconv, 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;
47 }
48
49 return v;
50 }
51
52 static struct dom_sid *schannel_ldb_val_dom_sid(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
53 const struct ldb_val *v)
54 {
55 enum ndr_err_code ndr_err;
56 struct dom_sid *sid;
57
58 sid = talloc(mem_ctx, struct dom_sid);
59 if (!sid) return NULL;
60
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;
66 }
67 return sid;
68 }
69
70
71 /**
72 connect to the schannel ldb
73 */
74 struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
/* [<][>][^][v][top][bottom][index][help] */
75 struct loadparm_context *lp_ctx)
76 {
77 char *path;
78 struct ldb_context *ldb;
79 bool existed;
80 const char *init_ldif =
81 "dn: @ATTRIBUTES\n" \
82 "computerName: CASE_INSENSITIVE\n" \
83 "flatname: CASE_INSENSITIVE\n";
84
85 path = private_path(mem_ctx, lp_ctx, "schannel.ldb");
86 if (!path) {
87 return NULL;
88 }
89
90 existed = file_exist(path);
91
92 ldb = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, path,
93 system_session(mem_ctx, lp_ctx),
94 NULL, LDB_FLG_NOSYNC, NULL);
95 talloc_free(path);
96 if (!ldb) {
97 return NULL;
98 }
99
100 if (!existed) {
101 gendb_add_ldif(ldb, init_ldif);
102 }
103
104 return ldb;
105 }
106
107 /*
108 remember an established session key for a netr server authentication
109 use a simple ldb structure
110 */
111 NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
112 struct ldb_context *ldb,
113 struct creds_CredentialState *creds)
114 {
115 struct ldb_message *msg;
116 struct ldb_val val, seed, client_state, server_state;
117 struct smb_iconv_convenience *smbiconv;
118 struct ldb_val *sid_val;
119 char *f;
120 char *sct;
121 int ret;
122
123 f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
124
125 if (f == NULL) {
126 return NT_STATUS_NO_MEMORY;
127 }
128
129 sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
130
131 if (sct == NULL) {
132 return NT_STATUS_NO_MEMORY;
133 }
134
135 msg = ldb_msg_new(ldb);
136 if (msg == NULL) {
137 return NT_STATUS_NO_MEMORY;
138 }
139
140 msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
141 if ( ! msg->dn) {
142 return NT_STATUS_NO_MEMORY;
143 }
144
145 smbiconv = lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm"));
146 sid_val = schannel_dom_sid_ldb_val(msg, smbiconv, creds->sid);
147 if (sid_val == NULL) {
148 return NT_STATUS_NO_MEMORY;
149 }
150
151 val.data = creds->session_key;
152 val.length = sizeof(creds->session_key);
153
154 seed.data = creds->seed.data;
155 seed.length = sizeof(creds->seed.data);
156
157 client_state.data = creds->client.data;
158 client_state.length = sizeof(creds->client.data);
159 server_state.data = creds->server.data;
160 server_state.length = sizeof(creds->server.data);
161
162 ldb_msg_add_string(msg, "objectClass", "schannelState");
163 ldb_msg_add_value(msg, "sessionKey", &val, NULL);
164 ldb_msg_add_value(msg, "seed", &seed, NULL);
165 ldb_msg_add_value(msg, "clientState", &client_state, NULL);
166 ldb_msg_add_value(msg, "serverState", &server_state, NULL);
167 ldb_msg_add_string(msg, "negotiateFlags", f);
168 ldb_msg_add_string(msg, "secureChannelType", sct);
169 ldb_msg_add_string(msg, "accountName", creds->account_name);
170 ldb_msg_add_string(msg, "computerName", creds->computer_name);
171 ldb_msg_add_string(msg, "flatname", creds->domain);
172 ldb_msg_add_value(msg, "objectSid", sid_val, NULL);
173
174 ldb_delete(ldb, msg->dn);
175
176 ret = ldb_add(ldb, msg);
177
178 if (ret != 0) {
179 DEBUG(0,("Unable to add %s to session key db - %s\n",
180 ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
181 return NT_STATUS_INTERNAL_DB_CORRUPTION;
182 }
183
184 return NT_STATUS_OK;
185 }
186
187 NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
188 struct tevent_context *ev_ctx,
189 struct loadparm_context *lp_ctx,
190 struct creds_CredentialState *creds)
191 {
192 struct ldb_context *ldb;
193 NTSTATUS nt_status;
194 int ret;
195
196 ldb = schannel_db_connect(mem_ctx, ev_ctx, lp_ctx);
197 if (!ldb) {
198 return NT_STATUS_ACCESS_DENIED;
199 }
200
201 ret = ldb_transaction_start(ldb);
202 if (ret != 0) {
203 talloc_free(ldb);
204 return NT_STATUS_INTERNAL_DB_CORRUPTION;
205 }
206
207 nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
208
209 if (NT_STATUS_IS_OK(nt_status)) {
210 ret = ldb_transaction_commit(ldb);
211 } else {
212 ret = ldb_transaction_cancel(ldb);
213 }
214
215 if (ret != 0) {
216 DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
217 creds->computer_name, ldb_errstring(ldb)));
218 talloc_free(ldb);
219 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220 }
221
222 talloc_free(ldb);
223 return nt_status;
224 }
225
226 /*
227 read back a credentials back for a computer
228 */
229 NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
230 struct ldb_context *ldb,
231 const char *computer_name,
232 const char *domain,
233 struct creds_CredentialState **creds)
234 {
235 struct ldb_result *res;
236 int ret;
237 const struct ldb_val *val;
238
239 *creds = talloc_zero(mem_ctx, struct creds_CredentialState);
240 if (!*creds) {
241 return NT_STATUS_NO_MEMORY;
242 }
243
244 ret = ldb_search(ldb, mem_ctx, &res,
245 NULL, LDB_SCOPE_SUBTREE, NULL,
246 "(&(computerName=%s)(flatname=%s))", computer_name, domain);
247 if (ret != LDB_SUCCESS) {
248 DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
249 return NT_STATUS_INVALID_HANDLE;
250 }
251 if (res->count != 1) {
252 DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
253 talloc_free(res);
254 return NT_STATUS_INVALID_HANDLE;
255 }
256
257 val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
258 if (val == NULL || val->length != 16) {
259 DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
260 talloc_free(res);
261 return NT_STATUS_INTERNAL_ERROR;
262 }
263
264 memcpy((*creds)->session_key, val->data, 16);
265
266 val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
267 if (val == NULL || val->length != 8) {
268 DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
269 talloc_free(res);
270 return NT_STATUS_INTERNAL_ERROR;
271 }
272
273 memcpy((*creds)->seed.data, val->data, 8);
274
275 val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
276 if (val == NULL || val->length != 8) {
277 DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
278 talloc_free(res);
279 return NT_STATUS_INTERNAL_ERROR;
280 }
281 memcpy((*creds)->client.data, val->data, 8);
282
283 val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
284 if (val == NULL || val->length != 8) {
285 DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
286 talloc_free(res);
287 return NT_STATUS_INTERNAL_ERROR;
288 }
289 memcpy((*creds)->server.data, val->data, 8);
290
291 (*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
292
293 (*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
294
295 (*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
296 if ((*creds)->account_name == NULL) {
297 talloc_free(res);
298 return NT_STATUS_NO_MEMORY;
299 }
300
301 (*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
302 if ((*creds)->computer_name == NULL) {
303 talloc_free(res);
304 return NT_STATUS_NO_MEMORY;
305 }
306
307 (*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
308 if ((*creds)->domain == NULL) {
309 talloc_free(res);
310 return NT_STATUS_NO_MEMORY;
311 }
312
313 val = ldb_msg_find_ldb_val(res->msgs[0], "objectSid");
314 if (val == NULL) {
315 DEBUG(1,("schannel: missing ObjectSid for client: %s\n", computer_name));
316 talloc_free(res);
317 return NT_STATUS_INTERNAL_ERROR;
318 }
319 (*creds)->sid = schannel_ldb_val_dom_sid(*creds, val);
320 if ((*creds)->sid == NULL) {
321 talloc_free(res);
322 return NT_STATUS_INTERNAL_ERROR;
323 }
324
325 talloc_free(res);
326 return NT_STATUS_OK;
327 }
328
329 NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
330 struct tevent_context *ev_ctx,
331 struct loadparm_context *lp_ctx,
332 const char *computer_name,
333 const char *domain,
334 struct creds_CredentialState **creds)
335 {
336 NTSTATUS nt_status;
337 struct ldb_context *ldb;
338
339 ldb = schannel_db_connect(mem_ctx, ev_ctx, lp_ctx);
340 if (!ldb) {
341 return NT_STATUS_ACCESS_DENIED;
342 }
343
344 nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
345 computer_name, domain,
346 creds);
347 talloc_free(ldb);
348 return nt_status;
349 }