1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 |
|
---|
4 | dcerpc schannel operations
|
---|
5 |
|
---|
6 | Copyright (C) Andrew Tridgell 2004
|
---|
7 | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
---|
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 "librpc/gen_ndr/ndr_schannel.h"
|
---|
25 | #include "auth/auth.h"
|
---|
26 | #include "auth/credentials/credentials.h"
|
---|
27 | #include "auth/gensec/gensec.h"
|
---|
28 | #include "auth/gensec/gensec_internal.h"
|
---|
29 | #include "auth/gensec/gensec_proto.h"
|
---|
30 | #include "../libcli/auth/schannel.h"
|
---|
31 | #include "librpc/gen_ndr/dcerpc.h"
|
---|
32 | #include "param/param.h"
|
---|
33 | #include "auth/gensec/gensec_toplevel_proto.h"
|
---|
34 | #include "lib/crypto/crypto.h"
|
---|
35 |
|
---|
36 | struct schannel_state {
|
---|
37 | struct gensec_security *gensec;
|
---|
38 | uint64_t seq_num;
|
---|
39 | bool initiator;
|
---|
40 | struct netlogon_creds_CredentialState *creds;
|
---|
41 | struct auth_user_info_dc *user_info_dc;
|
---|
42 | };
|
---|
43 |
|
---|
44 | #define SETUP_SEQNUM(state, buf, initiator) do { \
|
---|
45 | uint8_t *_buf = buf; \
|
---|
46 | uint32_t _seq_num_low = (state)->seq_num & UINT32_MAX; \
|
---|
47 | uint32_t _seq_num_high = (state)->seq_num >> 32; \
|
---|
48 | if (initiator) { \
|
---|
49 | _seq_num_high |= 0x80000000; \
|
---|
50 | } \
|
---|
51 | RSIVAL(_buf, 0, _seq_num_low); \
|
---|
52 | RSIVAL(_buf, 4, _seq_num_high); \
|
---|
53 | } while(0)
|
---|
54 |
|
---|
55 | static struct schannel_state *netsec_create_state(
|
---|
56 | struct gensec_security *gensec,
|
---|
57 | struct netlogon_creds_CredentialState *creds,
|
---|
58 | bool initiator)
|
---|
59 | {
|
---|
60 | struct schannel_state *state;
|
---|
61 |
|
---|
62 | state = talloc_zero(gensec, struct schannel_state);
|
---|
63 | if (state == NULL) {
|
---|
64 | return NULL;
|
---|
65 | }
|
---|
66 |
|
---|
67 | state->gensec = gensec;
|
---|
68 | state->initiator = initiator;
|
---|
69 | state->creds = netlogon_creds_copy(state, creds);
|
---|
70 | if (state->creds == NULL) {
|
---|
71 | talloc_free(state);
|
---|
72 | return NULL;
|
---|
73 | }
|
---|
74 |
|
---|
75 | gensec->private_data = state;
|
---|
76 |
|
---|
77 | return state;
|
---|
78 | }
|
---|
79 |
|
---|
80 | static void netsec_offset_and_sizes(struct schannel_state *state,
|
---|
81 | bool do_seal,
|
---|
82 | uint32_t *_min_sig_size,
|
---|
83 | uint32_t *_used_sig_size,
|
---|
84 | uint32_t *_checksum_length,
|
---|
85 | uint32_t *_confounder_ofs)
|
---|
86 | {
|
---|
87 | uint32_t min_sig_size;
|
---|
88 | uint32_t used_sig_size;
|
---|
89 | uint32_t checksum_length;
|
---|
90 | uint32_t confounder_ofs;
|
---|
91 |
|
---|
92 | if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
|
---|
93 | min_sig_size = 48;
|
---|
94 | used_sig_size = 56;
|
---|
95 | /*
|
---|
96 | * Note: windows has a bug here and uses the old values...
|
---|
97 | *
|
---|
98 | * checksum_length = 32;
|
---|
99 | * confounder_ofs = 48;
|
---|
100 | */
|
---|
101 | checksum_length = 8;
|
---|
102 | confounder_ofs = 24;
|
---|
103 | } else {
|
---|
104 | min_sig_size = 24;
|
---|
105 | used_sig_size = 32;
|
---|
106 | checksum_length = 8;
|
---|
107 | confounder_ofs = 24;
|
---|
108 | }
|
---|
109 |
|
---|
110 | if (do_seal) {
|
---|
111 | min_sig_size += 8;
|
---|
112 | }
|
---|
113 |
|
---|
114 | if (_min_sig_size) {
|
---|
115 | *_min_sig_size = min_sig_size;
|
---|
116 | }
|
---|
117 |
|
---|
118 | if (_used_sig_size) {
|
---|
119 | *_used_sig_size = used_sig_size;
|
---|
120 | }
|
---|
121 |
|
---|
122 | if (_checksum_length) {
|
---|
123 | *_checksum_length = checksum_length;
|
---|
124 | }
|
---|
125 |
|
---|
126 | if (_confounder_ofs) {
|
---|
127 | *_confounder_ofs = confounder_ofs;
|
---|
128 | }
|
---|
129 | }
|
---|
130 |
|
---|
131 | /*******************************************************************
|
---|
132 | Encode or Decode the sequence number (which is symmetric)
|
---|
133 | ********************************************************************/
|
---|
134 | static void netsec_do_seq_num(struct schannel_state *state,
|
---|
135 | const uint8_t *checksum,
|
---|
136 | uint32_t checksum_length,
|
---|
137 | uint8_t seq_num[8])
|
---|
138 | {
|
---|
139 | if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
|
---|
140 | AES_KEY key;
|
---|
141 | uint8_t iv[AES_BLOCK_SIZE];
|
---|
142 |
|
---|
143 | AES_set_encrypt_key(state->creds->session_key, 128, &key);
|
---|
144 | ZERO_STRUCT(iv);
|
---|
145 | memcpy(iv+0, checksum, 8);
|
---|
146 | memcpy(iv+8, checksum, 8);
|
---|
147 |
|
---|
148 | aes_cfb8_encrypt(seq_num, seq_num, 8, &key, iv, AES_ENCRYPT);
|
---|
149 | } else {
|
---|
150 | static const uint8_t zeros[4];
|
---|
151 | uint8_t sequence_key[16];
|
---|
152 | uint8_t digest1[16];
|
---|
153 |
|
---|
154 | hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
|
---|
155 | hmac_md5(digest1, checksum, checksum_length, sequence_key);
|
---|
156 | arcfour_crypt(seq_num, sequence_key, 8);
|
---|
157 | }
|
---|
158 |
|
---|
159 | state->seq_num++;
|
---|
160 | }
|
---|
161 |
|
---|
162 | static void netsec_do_seal(struct schannel_state *state,
|
---|
163 | const uint8_t seq_num[8],
|
---|
164 | uint8_t confounder[8],
|
---|
165 | uint8_t *data, uint32_t length,
|
---|
166 | bool forward)
|
---|
167 | {
|
---|
168 | if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
|
---|
169 | AES_KEY key;
|
---|
170 | uint8_t iv[AES_BLOCK_SIZE];
|
---|
171 | uint8_t sess_kf0[16];
|
---|
172 | int i;
|
---|
173 |
|
---|
174 | for (i = 0; i < 16; i++) {
|
---|
175 | sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
|
---|
176 | }
|
---|
177 |
|
---|
178 | AES_set_encrypt_key(sess_kf0, 128, &key);
|
---|
179 | ZERO_STRUCT(iv);
|
---|
180 | memcpy(iv+0, seq_num, 8);
|
---|
181 | memcpy(iv+8, seq_num, 8);
|
---|
182 |
|
---|
183 | if (forward) {
|
---|
184 | aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_ENCRYPT);
|
---|
185 | aes_cfb8_encrypt(data, data, length, &key, iv, AES_ENCRYPT);
|
---|
186 | } else {
|
---|
187 | aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_DECRYPT);
|
---|
188 | aes_cfb8_encrypt(data, data, length, &key, iv, AES_DECRYPT);
|
---|
189 | }
|
---|
190 | } else {
|
---|
191 | uint8_t sealing_key[16];
|
---|
192 | static const uint8_t zeros[4];
|
---|
193 | uint8_t digest2[16];
|
---|
194 | uint8_t sess_kf0[16];
|
---|
195 | int i;
|
---|
196 |
|
---|
197 | for (i = 0; i < 16; i++) {
|
---|
198 | sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
|
---|
199 | }
|
---|
200 |
|
---|
201 | hmac_md5(sess_kf0, zeros, 4, digest2);
|
---|
202 | hmac_md5(digest2, seq_num, 8, sealing_key);
|
---|
203 |
|
---|
204 | arcfour_crypt(confounder, sealing_key, 8);
|
---|
205 | arcfour_crypt(data, sealing_key, length);
|
---|
206 | }
|
---|
207 | }
|
---|
208 |
|
---|
209 | /*******************************************************************
|
---|
210 | Create a digest over the entire packet (including the data), and
|
---|
211 | MD5 it with the session key.
|
---|
212 | ********************************************************************/
|
---|
213 | static void netsec_do_sign(struct schannel_state *state,
|
---|
214 | const uint8_t *confounder,
|
---|
215 | const uint8_t *data, size_t length,
|
---|
216 | uint8_t header[8],
|
---|
217 | uint8_t *checksum)
|
---|
218 | {
|
---|
219 | if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
|
---|
220 | struct HMACSHA256Context ctx;
|
---|
221 |
|
---|
222 | hmac_sha256_init(state->creds->session_key,
|
---|
223 | sizeof(state->creds->session_key),
|
---|
224 | &ctx);
|
---|
225 |
|
---|
226 | if (confounder) {
|
---|
227 | SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
|
---|
228 | SSVAL(header, 2, NL_SEAL_AES128);
|
---|
229 | SSVAL(header, 4, 0xFFFF);
|
---|
230 | SSVAL(header, 6, 0x0000);
|
---|
231 |
|
---|
232 | hmac_sha256_update(header, 8, &ctx);
|
---|
233 | hmac_sha256_update(confounder, 8, &ctx);
|
---|
234 | } else {
|
---|
235 | SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
|
---|
236 | SSVAL(header, 2, NL_SEAL_NONE);
|
---|
237 | SSVAL(header, 4, 0xFFFF);
|
---|
238 | SSVAL(header, 6, 0x0000);
|
---|
239 |
|
---|
240 | hmac_sha256_update(header, 8, &ctx);
|
---|
241 | }
|
---|
242 |
|
---|
243 | hmac_sha256_update(data, length, &ctx);
|
---|
244 |
|
---|
245 | hmac_sha256_final(checksum, &ctx);
|
---|
246 | } else {
|
---|
247 | uint8_t packet_digest[16];
|
---|
248 | static const uint8_t zeros[4];
|
---|
249 | MD5_CTX ctx;
|
---|
250 |
|
---|
251 | MD5Init(&ctx);
|
---|
252 | MD5Update(&ctx, zeros, 4);
|
---|
253 | if (confounder) {
|
---|
254 | SSVAL(header, 0, NL_SIGN_HMAC_MD5);
|
---|
255 | SSVAL(header, 2, NL_SEAL_RC4);
|
---|
256 | SSVAL(header, 4, 0xFFFF);
|
---|
257 | SSVAL(header, 6, 0x0000);
|
---|
258 |
|
---|
259 | MD5Update(&ctx, header, 8);
|
---|
260 | MD5Update(&ctx, confounder, 8);
|
---|
261 | } else {
|
---|
262 | SSVAL(header, 0, NL_SIGN_HMAC_MD5);
|
---|
263 | SSVAL(header, 2, NL_SEAL_NONE);
|
---|
264 | SSVAL(header, 4, 0xFFFF);
|
---|
265 | SSVAL(header, 6, 0x0000);
|
---|
266 |
|
---|
267 | MD5Update(&ctx, header, 8);
|
---|
268 | }
|
---|
269 | MD5Update(&ctx, data, length);
|
---|
270 | MD5Final(packet_digest, &ctx);
|
---|
271 |
|
---|
272 | hmac_md5(state->creds->session_key,
|
---|
273 | packet_digest, sizeof(packet_digest),
|
---|
274 | checksum);
|
---|
275 | }
|
---|
276 | }
|
---|
277 |
|
---|
278 | static NTSTATUS netsec_incoming_packet(struct schannel_state *state,
|
---|
279 | bool do_unseal,
|
---|
280 | uint8_t *data, size_t length,
|
---|
281 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
282 | const DATA_BLOB *sig)
|
---|
283 | {
|
---|
284 | uint32_t min_sig_size = 0;
|
---|
285 | uint8_t header[8];
|
---|
286 | uint8_t checksum[32];
|
---|
287 | uint32_t checksum_length = sizeof(checksum_length);
|
---|
288 | uint8_t _confounder[8];
|
---|
289 | uint8_t *confounder = NULL;
|
---|
290 | uint32_t confounder_ofs = 0;
|
---|
291 | uint8_t seq_num[8];
|
---|
292 | int ret;
|
---|
293 | const uint8_t *sign_data = NULL;
|
---|
294 | size_t sign_length = 0;
|
---|
295 |
|
---|
296 | netsec_offset_and_sizes(state,
|
---|
297 | do_unseal,
|
---|
298 | &min_sig_size,
|
---|
299 | NULL,
|
---|
300 | &checksum_length,
|
---|
301 | &confounder_ofs);
|
---|
302 |
|
---|
303 | if (sig->length < min_sig_size) {
|
---|
304 | return NT_STATUS_ACCESS_DENIED;
|
---|
305 | }
|
---|
306 |
|
---|
307 | if (do_unseal) {
|
---|
308 | confounder = _confounder;
|
---|
309 | memcpy(confounder, sig->data+confounder_ofs, 8);
|
---|
310 | } else {
|
---|
311 | confounder = NULL;
|
---|
312 | }
|
---|
313 |
|
---|
314 | SETUP_SEQNUM(state, seq_num, !state->initiator);
|
---|
315 |
|
---|
316 | if (do_unseal) {
|
---|
317 | netsec_do_seal(state, seq_num,
|
---|
318 | confounder,
|
---|
319 | data, length,
|
---|
320 | false);
|
---|
321 | }
|
---|
322 |
|
---|
323 | if (state->gensec->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
324 | sign_data = whole_pdu;
|
---|
325 | sign_length = pdu_length;
|
---|
326 | } else {
|
---|
327 | sign_data = data;
|
---|
328 | sign_length = length;
|
---|
329 | }
|
---|
330 |
|
---|
331 | netsec_do_sign(state, confounder,
|
---|
332 | sign_data, sign_length,
|
---|
333 | header, checksum);
|
---|
334 |
|
---|
335 | ret = memcmp(checksum, sig->data+16, checksum_length);
|
---|
336 | if (ret != 0) {
|
---|
337 | dump_data_pw("calc digest:", checksum, checksum_length);
|
---|
338 | dump_data_pw("wire digest:", sig->data+16, checksum_length);
|
---|
339 | return NT_STATUS_ACCESS_DENIED;
|
---|
340 | }
|
---|
341 |
|
---|
342 | netsec_do_seq_num(state, checksum, checksum_length, seq_num);
|
---|
343 |
|
---|
344 | ret = memcmp(seq_num, sig->data+8, 8);
|
---|
345 | if (ret != 0) {
|
---|
346 | dump_data_pw("calc seq num:", seq_num, 8);
|
---|
347 | dump_data_pw("wire seq num:", sig->data+8, 8);
|
---|
348 | return NT_STATUS_ACCESS_DENIED;
|
---|
349 | }
|
---|
350 |
|
---|
351 | return NT_STATUS_OK;
|
---|
352 | }
|
---|
353 |
|
---|
354 | static uint32_t netsec_outgoing_sig_size(struct schannel_state *state)
|
---|
355 | {
|
---|
356 | uint32_t sig_size = 0;
|
---|
357 |
|
---|
358 | netsec_offset_and_sizes(state,
|
---|
359 | true,
|
---|
360 | NULL,
|
---|
361 | &sig_size,
|
---|
362 | NULL,
|
---|
363 | NULL);
|
---|
364 |
|
---|
365 | return sig_size;
|
---|
366 | }
|
---|
367 |
|
---|
368 | static NTSTATUS netsec_outgoing_packet(struct schannel_state *state,
|
---|
369 | TALLOC_CTX *mem_ctx,
|
---|
370 | bool do_seal,
|
---|
371 | uint8_t *data, size_t length,
|
---|
372 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
373 | DATA_BLOB *sig)
|
---|
374 | {
|
---|
375 | uint32_t min_sig_size = 0;
|
---|
376 | uint32_t used_sig_size = 0;
|
---|
377 | uint8_t header[8];
|
---|
378 | uint8_t checksum[32];
|
---|
379 | uint32_t checksum_length = sizeof(checksum_length);
|
---|
380 | uint8_t _confounder[8];
|
---|
381 | uint8_t *confounder = NULL;
|
---|
382 | uint32_t confounder_ofs = 0;
|
---|
383 | uint8_t seq_num[8];
|
---|
384 | const uint8_t *sign_data = NULL;
|
---|
385 | size_t sign_length = 0;
|
---|
386 |
|
---|
387 | netsec_offset_and_sizes(state,
|
---|
388 | do_seal,
|
---|
389 | &min_sig_size,
|
---|
390 | &used_sig_size,
|
---|
391 | &checksum_length,
|
---|
392 | &confounder_ofs);
|
---|
393 |
|
---|
394 | SETUP_SEQNUM(state, seq_num, state->initiator);
|
---|
395 |
|
---|
396 | if (do_seal) {
|
---|
397 | confounder = _confounder;
|
---|
398 | generate_random_buffer(confounder, 8);
|
---|
399 | } else {
|
---|
400 | confounder = NULL;
|
---|
401 | }
|
---|
402 |
|
---|
403 | if (state->gensec->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
404 | sign_data = whole_pdu;
|
---|
405 | sign_length = pdu_length;
|
---|
406 | } else {
|
---|
407 | sign_data = data;
|
---|
408 | sign_length = length;
|
---|
409 | }
|
---|
410 |
|
---|
411 | netsec_do_sign(state, confounder,
|
---|
412 | sign_data, sign_length,
|
---|
413 | header, checksum);
|
---|
414 |
|
---|
415 | if (do_seal) {
|
---|
416 | netsec_do_seal(state, seq_num,
|
---|
417 | confounder,
|
---|
418 | data, length,
|
---|
419 | true);
|
---|
420 | }
|
---|
421 |
|
---|
422 | netsec_do_seq_num(state, checksum, checksum_length, seq_num);
|
---|
423 |
|
---|
424 | (*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size);
|
---|
425 |
|
---|
426 | memcpy(sig->data, header, 8);
|
---|
427 | memcpy(sig->data+8, seq_num, 8);
|
---|
428 | memcpy(sig->data+16, checksum, checksum_length);
|
---|
429 |
|
---|
430 | if (confounder) {
|
---|
431 | memcpy(sig->data+confounder_ofs, confounder, 8);
|
---|
432 | }
|
---|
433 |
|
---|
434 | dump_data_pw("signature:", sig->data+ 0, 8);
|
---|
435 | dump_data_pw("seq_num :", sig->data+ 8, 8);
|
---|
436 | dump_data_pw("digest :", sig->data+16, checksum_length);
|
---|
437 | dump_data_pw("confound :", sig->data+confounder_ofs, 8);
|
---|
438 |
|
---|
439 | return NT_STATUS_OK;
|
---|
440 | }
|
---|
441 |
|
---|
442 | _PUBLIC_ NTSTATUS gensec_schannel_init(void);
|
---|
443 |
|
---|
444 | static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
|
---|
445 | {
|
---|
446 | struct schannel_state *state =
|
---|
447 | talloc_get_type_abort(gensec_security->private_data,
|
---|
448 | struct schannel_state);
|
---|
449 |
|
---|
450 | return netsec_outgoing_sig_size(state);
|
---|
451 | }
|
---|
452 |
|
---|
453 | static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
---|
454 | struct tevent_context *ev,
|
---|
455 | const DATA_BLOB in, DATA_BLOB *out)
|
---|
456 | {
|
---|
457 | struct schannel_state *state =
|
---|
458 | talloc_get_type(gensec_security->private_data,
|
---|
459 | struct schannel_state);
|
---|
460 | NTSTATUS status;
|
---|
461 | enum ndr_err_code ndr_err;
|
---|
462 | struct NL_AUTH_MESSAGE bind_schannel = {};
|
---|
463 | struct NL_AUTH_MESSAGE bind_schannel_ack;
|
---|
464 | struct netlogon_creds_CredentialState *creds;
|
---|
465 | const char *workstation;
|
---|
466 | const char *domain;
|
---|
467 |
|
---|
468 | *out = data_blob(NULL, 0);
|
---|
469 |
|
---|
470 | if (gensec_security->dcerpc_auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
|
---|
471 | switch (gensec_security->gensec_role) {
|
---|
472 | case GENSEC_CLIENT:
|
---|
473 | return NT_STATUS_INVALID_PARAMETER_MIX;
|
---|
474 | case GENSEC_SERVER:
|
---|
475 | return NT_STATUS_INVALID_PARAMETER;
|
---|
476 | }
|
---|
477 | return NT_STATUS_INTERNAL_ERROR;
|
---|
478 | }
|
---|
479 |
|
---|
480 | switch (gensec_security->gensec_role) {
|
---|
481 | case GENSEC_CLIENT:
|
---|
482 | if (state != NULL) {
|
---|
483 | /* we could parse the bind ack, but we don't know what it is yet */
|
---|
484 | return NT_STATUS_OK;
|
---|
485 | }
|
---|
486 |
|
---|
487 | creds = cli_credentials_get_netlogon_creds(gensec_security->credentials);
|
---|
488 | if (creds == NULL) {
|
---|
489 | return NT_STATUS_INVALID_PARAMETER_MIX;
|
---|
490 | }
|
---|
491 |
|
---|
492 | state = netsec_create_state(gensec_security,
|
---|
493 | creds, true /* initiator */);
|
---|
494 | if (state == NULL) {
|
---|
495 | return NT_STATUS_NO_MEMORY;
|
---|
496 | }
|
---|
497 |
|
---|
498 | bind_schannel.MessageType = NL_NEGOTIATE_REQUEST;
|
---|
499 |
|
---|
500 | bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME |
|
---|
501 | NL_FLAG_OEM_NETBIOS_COMPUTER_NAME;
|
---|
502 | bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials);
|
---|
503 | bind_schannel.oem_netbios_computer.a = creds->computer_name;
|
---|
504 |
|
---|
505 | if (creds->secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
|
---|
506 | bind_schannel.Flags |= NL_FLAG_UTF8_DNS_DOMAIN_NAME;
|
---|
507 | bind_schannel.utf8_dns_domain.u = cli_credentials_get_realm(gensec_security->credentials);
|
---|
508 |
|
---|
509 | bind_schannel.Flags |= NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME;
|
---|
510 | bind_schannel.utf8_netbios_computer.u = creds->computer_name;
|
---|
511 | }
|
---|
512 |
|
---|
513 | ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
|
---|
514 | (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
|
---|
515 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
516 | status = ndr_map_error2ntstatus(ndr_err);
|
---|
517 | DEBUG(3, ("Could not create schannel bind: %s\n",
|
---|
518 | nt_errstr(status)));
|
---|
519 | return status;
|
---|
520 | }
|
---|
521 |
|
---|
522 | return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
---|
523 | case GENSEC_SERVER:
|
---|
524 |
|
---|
525 | if (state != NULL) {
|
---|
526 | /* no third leg on this protocol */
|
---|
527 | return NT_STATUS_INVALID_PARAMETER;
|
---|
528 | }
|
---|
529 |
|
---|
530 | /* parse the schannel startup blob */
|
---|
531 | ndr_err = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
|
---|
532 | (ndr_pull_flags_fn_t)ndr_pull_NL_AUTH_MESSAGE);
|
---|
533 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
534 | status = ndr_map_error2ntstatus(ndr_err);
|
---|
535 | DEBUG(3, ("Could not parse incoming schannel bind: %s\n",
|
---|
536 | nt_errstr(status)));
|
---|
537 | return status;
|
---|
538 | }
|
---|
539 |
|
---|
540 | if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_DOMAIN_NAME) {
|
---|
541 | domain = bind_schannel.oem_netbios_domain.a;
|
---|
542 | if (strcasecmp_m(domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)) != 0) {
|
---|
543 | DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n",
|
---|
544 | domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)));
|
---|
545 | return NT_STATUS_LOGON_FAILURE;
|
---|
546 | }
|
---|
547 | } else if (bind_schannel.Flags & NL_FLAG_UTF8_DNS_DOMAIN_NAME) {
|
---|
548 | domain = bind_schannel.utf8_dns_domain.u;
|
---|
549 | if (strcasecmp_m(domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)) != 0) {
|
---|
550 | DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n",
|
---|
551 | domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)));
|
---|
552 | return NT_STATUS_LOGON_FAILURE;
|
---|
553 | }
|
---|
554 | } else {
|
---|
555 | DEBUG(3, ("Request for schannel to without domain\n"));
|
---|
556 | return NT_STATUS_LOGON_FAILURE;
|
---|
557 | }
|
---|
558 |
|
---|
559 | if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_COMPUTER_NAME) {
|
---|
560 | workstation = bind_schannel.oem_netbios_computer.a;
|
---|
561 | } else if (bind_schannel.Flags & NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME) {
|
---|
562 | workstation = bind_schannel.utf8_netbios_computer.u;
|
---|
563 | } else {
|
---|
564 | DEBUG(3, ("Request for schannel to without netbios workstation\n"));
|
---|
565 | return NT_STATUS_LOGON_FAILURE;
|
---|
566 | }
|
---|
567 |
|
---|
568 | status = schannel_get_creds_state(out_mem_ctx,
|
---|
569 | gensec_security->settings->lp_ctx,
|
---|
570 | workstation, &creds);
|
---|
571 | if (!NT_STATUS_IS_OK(status)) {
|
---|
572 | DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
|
---|
573 | workstation, nt_errstr(status)));
|
---|
574 | if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
|
---|
575 | return NT_STATUS_LOGON_FAILURE;
|
---|
576 | }
|
---|
577 | return status;
|
---|
578 | }
|
---|
579 |
|
---|
580 | state = netsec_create_state(gensec_security,
|
---|
581 | creds, false /* not initiator */);
|
---|
582 | if (state == NULL) {
|
---|
583 | return NT_STATUS_NO_MEMORY;
|
---|
584 | }
|
---|
585 |
|
---|
586 | status = auth_anonymous_user_info_dc(state,
|
---|
587 | lpcfg_netbios_name(gensec_security->settings->lp_ctx),
|
---|
588 | &state->user_info_dc);
|
---|
589 | if (!NT_STATUS_IS_OK(status)) {
|
---|
590 | return status;
|
---|
591 | }
|
---|
592 |
|
---|
593 | bind_schannel_ack.MessageType = NL_NEGOTIATE_RESPONSE;
|
---|
594 | bind_schannel_ack.Flags = 0;
|
---|
595 | bind_schannel_ack.Buffer.dummy = 0x6c0000; /* actually I think
|
---|
596 | * this does not have
|
---|
597 | * any meaning here
|
---|
598 | * - gd */
|
---|
599 |
|
---|
600 | ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
|
---|
601 | (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
|
---|
602 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
603 | status = ndr_map_error2ntstatus(ndr_err);
|
---|
604 | DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
|
---|
605 | workstation, nt_errstr(status)));
|
---|
606 | return status;
|
---|
607 | }
|
---|
608 |
|
---|
609 | return NT_STATUS_OK;
|
---|
610 | }
|
---|
611 | return NT_STATUS_INVALID_PARAMETER;
|
---|
612 | }
|
---|
613 |
|
---|
614 | /**
|
---|
615 | * Returns anonymous credentials for schannel, matching Win2k3.
|
---|
616 | *
|
---|
617 | */
|
---|
618 |
|
---|
619 | static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
|
---|
620 | TALLOC_CTX *mem_ctx,
|
---|
621 | struct auth_session_info **_session_info)
|
---|
622 | {
|
---|
623 | struct schannel_state *state =
|
---|
624 | talloc_get_type(gensec_security->private_data,
|
---|
625 | struct schannel_state);
|
---|
626 | struct auth4_context *auth_ctx = gensec_security->auth_context;
|
---|
627 | struct auth_session_info *session_info = NULL;
|
---|
628 | uint32_t session_info_flags = 0;
|
---|
629 | NTSTATUS status;
|
---|
630 |
|
---|
631 | if (auth_ctx == NULL) {
|
---|
632 | DEBUG(0, ("Cannot generate a session_info without the auth_context\n"));
|
---|
633 | return NT_STATUS_INTERNAL_ERROR;
|
---|
634 | }
|
---|
635 |
|
---|
636 | if (auth_ctx->generate_session_info == NULL) {
|
---|
637 | DEBUG(0, ("Cannot generate a session_info without the generate_session_info hook\n"));
|
---|
638 | return NT_STATUS_INTERNAL_ERROR;
|
---|
639 | }
|
---|
640 |
|
---|
641 | if (gensec_security->want_features & GENSEC_FEATURE_UNIX_TOKEN) {
|
---|
642 | session_info_flags |= AUTH_SESSION_INFO_UNIX_TOKEN;
|
---|
643 | }
|
---|
644 |
|
---|
645 | session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
|
---|
646 |
|
---|
647 | status = auth_ctx->generate_session_info(
|
---|
648 | auth_ctx,
|
---|
649 | mem_ctx,
|
---|
650 | state->user_info_dc,
|
---|
651 | state->user_info_dc->info->account_name,
|
---|
652 | session_info_flags,
|
---|
653 | &session_info);
|
---|
654 | if (!NT_STATUS_IS_OK(status)) {
|
---|
655 | return status;
|
---|
656 | }
|
---|
657 |
|
---|
658 | *_session_info = session_info;
|
---|
659 | return NT_STATUS_OK;
|
---|
660 | }
|
---|
661 |
|
---|
662 | static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
|
---|
663 | {
|
---|
664 | return NT_STATUS_OK;
|
---|
665 | }
|
---|
666 |
|
---|
667 | static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
|
---|
668 | {
|
---|
669 | return NT_STATUS_OK;
|
---|
670 | }
|
---|
671 |
|
---|
672 | static bool schannel_have_feature(struct gensec_security *gensec_security,
|
---|
673 | uint32_t feature)
|
---|
674 | {
|
---|
675 | if (gensec_security->dcerpc_auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
|
---|
676 | if (feature & GENSEC_FEATURE_SIGN) {
|
---|
677 | return true;
|
---|
678 | }
|
---|
679 | }
|
---|
680 | if (gensec_security->dcerpc_auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
|
---|
681 | if (feature & GENSEC_FEATURE_SEAL) {
|
---|
682 | return true;
|
---|
683 | }
|
---|
684 | }
|
---|
685 | if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
---|
686 | return true;
|
---|
687 | }
|
---|
688 | if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
|
---|
689 | return true;
|
---|
690 | }
|
---|
691 | return false;
|
---|
692 | }
|
---|
693 |
|
---|
694 | /*
|
---|
695 | unseal a packet
|
---|
696 | */
|
---|
697 | static NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
|
---|
698 | uint8_t *data, size_t length,
|
---|
699 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
700 | const DATA_BLOB *sig)
|
---|
701 | {
|
---|
702 | struct schannel_state *state =
|
---|
703 | talloc_get_type_abort(gensec_security->private_data,
|
---|
704 | struct schannel_state);
|
---|
705 |
|
---|
706 | return netsec_incoming_packet(state, true,
|
---|
707 | discard_const_p(uint8_t, data),
|
---|
708 | length,
|
---|
709 | whole_pdu, pdu_length,
|
---|
710 | sig);
|
---|
711 | }
|
---|
712 |
|
---|
713 | /*
|
---|
714 | check the signature on a packet
|
---|
715 | */
|
---|
716 | static NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
|
---|
717 | const uint8_t *data, size_t length,
|
---|
718 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
719 | const DATA_BLOB *sig)
|
---|
720 | {
|
---|
721 | struct schannel_state *state =
|
---|
722 | talloc_get_type_abort(gensec_security->private_data,
|
---|
723 | struct schannel_state);
|
---|
724 |
|
---|
725 | return netsec_incoming_packet(state, false,
|
---|
726 | discard_const_p(uint8_t, data),
|
---|
727 | length,
|
---|
728 | whole_pdu, pdu_length,
|
---|
729 | sig);
|
---|
730 | }
|
---|
731 | /*
|
---|
732 | seal a packet
|
---|
733 | */
|
---|
734 | static NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
|
---|
735 | TALLOC_CTX *mem_ctx,
|
---|
736 | uint8_t *data, size_t length,
|
---|
737 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
738 | DATA_BLOB *sig)
|
---|
739 | {
|
---|
740 | struct schannel_state *state =
|
---|
741 | talloc_get_type_abort(gensec_security->private_data,
|
---|
742 | struct schannel_state);
|
---|
743 |
|
---|
744 | return netsec_outgoing_packet(state, mem_ctx, true,
|
---|
745 | data, length,
|
---|
746 | whole_pdu, pdu_length,
|
---|
747 | sig);
|
---|
748 | }
|
---|
749 |
|
---|
750 | /*
|
---|
751 | sign a packet
|
---|
752 | */
|
---|
753 | static NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
|
---|
754 | TALLOC_CTX *mem_ctx,
|
---|
755 | const uint8_t *data, size_t length,
|
---|
756 | const uint8_t *whole_pdu, size_t pdu_length,
|
---|
757 | DATA_BLOB *sig)
|
---|
758 | {
|
---|
759 | struct schannel_state *state =
|
---|
760 | talloc_get_type_abort(gensec_security->private_data,
|
---|
761 | struct schannel_state);
|
---|
762 |
|
---|
763 | return netsec_outgoing_packet(state, mem_ctx, false,
|
---|
764 | discard_const_p(uint8_t, data),
|
---|
765 | length,
|
---|
766 | whole_pdu, pdu_length,
|
---|
767 | sig);
|
---|
768 | }
|
---|
769 |
|
---|
770 | static const struct gensec_security_ops gensec_schannel_security_ops = {
|
---|
771 | .name = "schannel",
|
---|
772 | .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
|
---|
773 | .client_start = schannel_client_start,
|
---|
774 | .server_start = schannel_server_start,
|
---|
775 | .update = schannel_update,
|
---|
776 | .seal_packet = schannel_seal_packet,
|
---|
777 | .sign_packet = schannel_sign_packet,
|
---|
778 | .check_packet = schannel_check_packet,
|
---|
779 | .unseal_packet = schannel_unseal_packet,
|
---|
780 | .session_info = schannel_session_info,
|
---|
781 | .sig_size = schannel_sig_size,
|
---|
782 | .have_feature = schannel_have_feature,
|
---|
783 | .enabled = true,
|
---|
784 | .priority = GENSEC_SCHANNEL
|
---|
785 | };
|
---|
786 |
|
---|
787 | _PUBLIC_ NTSTATUS gensec_schannel_init(void)
|
---|
788 | {
|
---|
789 | NTSTATUS ret;
|
---|
790 | ret = gensec_register(&gensec_schannel_security_ops);
|
---|
791 | if (!NT_STATUS_IS_OK(ret)) {
|
---|
792 | DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
---|
793 | gensec_schannel_security_ops.name));
|
---|
794 | return ret;
|
---|
795 | }
|
---|
796 |
|
---|
797 | return ret;
|
---|
798 | }
|
---|