2 Unix SMB/CIFS implementation.
4 dcerpc schannel operations
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
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/>.
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"
36 struct schannel_state {
39 struct netlogon_creds_CredentialState *creds;
42 #define SETUP_SEQNUM(state, buf, initiator) do { \
43 uint8_t *_buf = buf; \
44 uint32_t _seq_num_low = (state)->seq_num & UINT32_MAX; \
45 uint32_t _seq_num_high = (state)->seq_num >> 32; \
47 _seq_num_high |= 0x80000000; \
49 RSIVAL(_buf, 0, _seq_num_low); \
50 RSIVAL(_buf, 4, _seq_num_high); \
53 static struct schannel_state *netsec_create_state(TALLOC_CTX *mem_ctx,
54 struct netlogon_creds_CredentialState *creds,
57 struct schannel_state *state;
59 state = talloc(mem_ctx, struct schannel_state);
64 state->initiator = initiator;
66 state->creds = netlogon_creds_copy(state, creds);
67 if (state->creds == NULL) {
75 static void netsec_offset_and_sizes(struct schannel_state *state,
77 uint32_t *_min_sig_size,
78 uint32_t *_used_sig_size,
79 uint32_t *_checksum_length,
80 uint32_t *_confounder_ofs)
82 uint32_t min_sig_size;
83 uint32_t used_sig_size;
84 uint32_t checksum_length;
85 uint32_t confounder_ofs;
87 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
91 * Note: windows has a bug here and uses the old values...
93 * checksum_length = 32;
94 * confounder_ofs = 48;
110 *_min_sig_size = min_sig_size;
113 if (_used_sig_size) {
114 *_used_sig_size = used_sig_size;
117 if (_checksum_length) {
118 *_checksum_length = checksum_length;
121 if (_confounder_ofs) {
122 *_confounder_ofs = confounder_ofs;
126 /*******************************************************************
127 Encode or Decode the sequence number (which is symmetric)
128 ********************************************************************/
129 static void netsec_do_seq_num(struct schannel_state *state,
130 const uint8_t *checksum,
131 uint32_t checksum_length,
134 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
136 uint8_t iv[AES_BLOCK_SIZE];
138 AES_set_encrypt_key(state->creds->session_key, 128, &key);
140 memcpy(iv+0, checksum, 8);
141 memcpy(iv+8, checksum, 8);
143 aes_cfb8_encrypt(seq_num, seq_num, 8, &key, iv, AES_ENCRYPT);
145 static const uint8_t zeros[4];
146 uint8_t sequence_key[16];
149 hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
150 hmac_md5(digest1, checksum, checksum_length, sequence_key);
151 arcfour_crypt(seq_num, sequence_key, 8);
157 static void netsec_do_seal(struct schannel_state *state,
158 const uint8_t seq_num[8],
159 uint8_t confounder[8],
160 uint8_t *data, uint32_t length,
163 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
165 uint8_t iv[AES_BLOCK_SIZE];
166 uint8_t sess_kf0[16];
169 for (i = 0; i < 16; i++) {
170 sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
173 AES_set_encrypt_key(sess_kf0, 128, &key);
175 memcpy(iv+0, seq_num, 8);
176 memcpy(iv+8, seq_num, 8);
179 aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_ENCRYPT);
180 aes_cfb8_encrypt(data, data, length, &key, iv, AES_ENCRYPT);
182 aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_DECRYPT);
183 aes_cfb8_encrypt(data, data, length, &key, iv, AES_DECRYPT);
186 uint8_t sealing_key[16];
187 static const uint8_t zeros[4];
189 uint8_t sess_kf0[16];
192 for (i = 0; i < 16; i++) {
193 sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
196 hmac_md5(sess_kf0, zeros, 4, digest2);
197 hmac_md5(digest2, seq_num, 8, sealing_key);
199 arcfour_crypt(confounder, sealing_key, 8);
200 arcfour_crypt(data, sealing_key, length);
204 /*******************************************************************
205 Create a digest over the entire packet (including the data), and
206 MD5 it with the session key.
207 ********************************************************************/
208 static void netsec_do_sign(struct schannel_state *state,
209 const uint8_t *confounder,
210 const uint8_t *data, size_t length,
214 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
215 struct HMACSHA256Context ctx;
217 hmac_sha256_init(state->creds->session_key,
218 sizeof(state->creds->session_key),
222 SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
223 SSVAL(header, 2, NL_SEAL_AES128);
224 SSVAL(header, 4, 0xFFFF);
225 SSVAL(header, 6, 0x0000);
227 hmac_sha256_update(header, 8, &ctx);
228 hmac_sha256_update(confounder, 8, &ctx);
230 SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
231 SSVAL(header, 2, NL_SEAL_NONE);
232 SSVAL(header, 4, 0xFFFF);
233 SSVAL(header, 6, 0x0000);
235 hmac_sha256_update(header, 8, &ctx);
238 hmac_sha256_update(data, length, &ctx);
240 hmac_sha256_final(checksum, &ctx);
242 uint8_t packet_digest[16];
243 static const uint8_t zeros[4];
247 MD5Update(&ctx, zeros, 4);
249 SSVAL(header, 0, NL_SIGN_HMAC_MD5);
250 SSVAL(header, 2, NL_SEAL_RC4);
251 SSVAL(header, 4, 0xFFFF);
252 SSVAL(header, 6, 0x0000);
254 MD5Update(&ctx, header, 8);
255 MD5Update(&ctx, confounder, 8);
257 SSVAL(header, 0, NL_SIGN_HMAC_MD5);
258 SSVAL(header, 2, NL_SEAL_NONE);
259 SSVAL(header, 4, 0xFFFF);
260 SSVAL(header, 6, 0x0000);
262 MD5Update(&ctx, header, 8);
264 MD5Update(&ctx, data, length);
265 MD5Final(packet_digest, &ctx);
267 hmac_md5(state->creds->session_key,
268 packet_digest, sizeof(packet_digest),
273 static NTSTATUS netsec_incoming_packet(struct schannel_state *state,
275 uint8_t *data, size_t length,
276 const DATA_BLOB *sig)
278 uint32_t min_sig_size = 0;
280 uint8_t checksum[32];
281 uint32_t checksum_length = sizeof(checksum_length);
282 uint8_t _confounder[8];
283 uint8_t *confounder = NULL;
284 uint32_t confounder_ofs = 0;
288 netsec_offset_and_sizes(state,
295 if (sig->length < min_sig_size) {
296 return NT_STATUS_ACCESS_DENIED;
300 confounder = _confounder;
301 memcpy(confounder, sig->data+confounder_ofs, 8);
306 SETUP_SEQNUM(state, seq_num, !state->initiator);
309 netsec_do_seal(state, seq_num,
315 netsec_do_sign(state, confounder,
319 ret = memcmp(checksum, sig->data+16, checksum_length);
321 dump_data_pw("calc digest:", checksum, checksum_length);
322 dump_data_pw("wire digest:", sig->data+16, checksum_length);
323 return NT_STATUS_ACCESS_DENIED;
326 netsec_do_seq_num(state, checksum, checksum_length, seq_num);
328 ret = memcmp(seq_num, sig->data+8, 8);
330 dump_data_pw("calc seq num:", seq_num, 8);
331 dump_data_pw("wire seq num:", sig->data+8, 8);
332 return NT_STATUS_ACCESS_DENIED;
338 static uint32_t netsec_outgoing_sig_size(struct schannel_state *state)
340 uint32_t sig_size = 0;
342 netsec_offset_and_sizes(state,
352 static NTSTATUS netsec_outgoing_packet(struct schannel_state *state,
355 uint8_t *data, size_t length,
358 uint32_t min_sig_size = 0;
359 uint32_t used_sig_size = 0;
361 uint8_t checksum[32];
362 uint32_t checksum_length = sizeof(checksum_length);
363 uint8_t _confounder[8];
364 uint8_t *confounder = NULL;
365 uint32_t confounder_ofs = 0;
368 netsec_offset_and_sizes(state,
375 SETUP_SEQNUM(state, seq_num, state->initiator);
378 confounder = _confounder;
379 generate_random_buffer(confounder, 8);
384 netsec_do_sign(state, confounder,
389 netsec_do_seal(state, seq_num,
395 netsec_do_seq_num(state, checksum, checksum_length, seq_num);
397 (*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size);
399 memcpy(sig->data, header, 8);
400 memcpy(sig->data+8, seq_num, 8);
401 memcpy(sig->data+16, checksum, checksum_length);
404 memcpy(sig->data+confounder_ofs, confounder, 8);
407 dump_data_pw("signature:", sig->data+ 0, 8);
408 dump_data_pw("seq_num :", sig->data+ 8, 8);
409 dump_data_pw("digest :", sig->data+16, checksum_length);
410 dump_data_pw("confound :", sig->data+confounder_ofs, 8);
415 _PUBLIC_ NTSTATUS gensec_schannel_init(void);
417 static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
419 struct schannel_state *state =
420 talloc_get_type_abort(gensec_security->private_data,
421 struct schannel_state);
423 return netsec_outgoing_sig_size(state);
426 static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
427 struct tevent_context *ev,
428 const DATA_BLOB in, DATA_BLOB *out)
430 struct schannel_state *state =
431 talloc_get_type(gensec_security->private_data,
432 struct schannel_state);
434 enum ndr_err_code ndr_err;
435 struct NL_AUTH_MESSAGE bind_schannel;
436 struct NL_AUTH_MESSAGE bind_schannel_ack;
437 struct netlogon_creds_CredentialState *creds;
438 const char *workstation;
441 *out = data_blob(NULL, 0);
443 switch (gensec_security->gensec_role) {
446 /* we could parse the bind ack, but we don't know what it is yet */
450 creds = cli_credentials_get_netlogon_creds(gensec_security->credentials);
452 return NT_STATUS_INVALID_PARAMETER_MIX;
455 state = netsec_create_state(gensec_security,
456 creds, true /* initiator */);
458 return NT_STATUS_NO_MEMORY;
460 gensec_security->private_data = state;
462 bind_schannel.MessageType = NL_NEGOTIATE_REQUEST;
464 /* to support this we'd need to have access to the full domain name */
466 bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME |
467 NL_FLAG_OEM_NETBIOS_COMPUTER_NAME |
468 NL_FLAG_UTF8_DNS_DOMAIN_NAME |
469 NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME;
470 bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials);
471 bind_schannel.oem_netbios_computer.a = creds->computer_name;
472 bind_schannel.utf8_dns_domain = cli_credentials_get_realm(gensec_security->credentials);
473 /* w2k3 refuses us if we use the full DNS workstation?
474 why? perhaps because we don't fill in the dNSHostName
475 attribute in the machine account? */
476 bind_schannel.utf8_netbios_computer = creds->computer_name;
478 bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME |
479 NL_FLAG_OEM_NETBIOS_COMPUTER_NAME;
480 bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials);
481 bind_schannel.oem_netbios_computer.a = creds->computer_name;
484 ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
485 (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
486 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
487 status = ndr_map_error2ntstatus(ndr_err);
488 DEBUG(3, ("Could not create schannel bind: %s\n",
493 return NT_STATUS_MORE_PROCESSING_REQUIRED;
497 /* no third leg on this protocol */
498 return NT_STATUS_INVALID_PARAMETER;
501 /* parse the schannel startup blob */
502 ndr_err = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
503 (ndr_pull_flags_fn_t)ndr_pull_NL_AUTH_MESSAGE);
504 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
505 status = ndr_map_error2ntstatus(ndr_err);
506 DEBUG(3, ("Could not parse incoming schannel bind: %s\n",
511 if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_DOMAIN_NAME) {
512 domain = bind_schannel.oem_netbios_domain.a;
513 if (strcasecmp_m(domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)) != 0) {
514 DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n",
515 domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)));
516 return NT_STATUS_LOGON_FAILURE;
518 } else if (bind_schannel.Flags & NL_FLAG_UTF8_DNS_DOMAIN_NAME) {
519 domain = bind_schannel.utf8_dns_domain.u;
520 if (strcasecmp_m(domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)) != 0) {
521 DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n",
522 domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)));
523 return NT_STATUS_LOGON_FAILURE;
526 DEBUG(3, ("Request for schannel to without domain\n"));
527 return NT_STATUS_LOGON_FAILURE;
530 if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_COMPUTER_NAME) {
531 workstation = bind_schannel.oem_netbios_computer.a;
532 } else if (bind_schannel.Flags & NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME) {
533 workstation = bind_schannel.utf8_netbios_computer.u;
535 DEBUG(3, ("Request for schannel to without netbios workstation\n"));
536 return NT_STATUS_LOGON_FAILURE;
539 status = schannel_get_creds_state(out_mem_ctx,
540 gensec_security->settings->lp_ctx,
541 workstation, &creds);
542 if (!NT_STATUS_IS_OK(status)) {
543 DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
544 workstation, nt_errstr(status)));
545 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
546 return NT_STATUS_LOGON_FAILURE;
551 state = netsec_create_state(gensec_security,
552 creds, false /* not initiator */);
554 return NT_STATUS_NO_MEMORY;
556 gensec_security->private_data = state;
558 bind_schannel_ack.MessageType = NL_NEGOTIATE_RESPONSE;
559 bind_schannel_ack.Flags = 0;
560 bind_schannel_ack.Buffer.dummy = 0x6c0000; /* actually I think
565 ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
566 (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
567 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
568 status = ndr_map_error2ntstatus(ndr_err);
569 DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
570 workstation, nt_errstr(status)));
576 return NT_STATUS_INVALID_PARAMETER;
580 * Returns anonymous credentials for schannel, matching Win2k3.
584 static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
586 struct auth_session_info **_session_info)
588 return auth_anonymous_session_info(mem_ctx, gensec_security->settings->lp_ctx, _session_info);
591 static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
596 static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
601 static bool schannel_have_feature(struct gensec_security *gensec_security,
604 if (feature & (GENSEC_FEATURE_SIGN |
605 GENSEC_FEATURE_SEAL)) {
608 if (feature & GENSEC_FEATURE_DCE_STYLE) {
617 static NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
618 uint8_t *data, size_t length,
619 const uint8_t *whole_pdu, size_t pdu_length,
620 const DATA_BLOB *sig)
622 struct schannel_state *state =
623 talloc_get_type_abort(gensec_security->private_data,
624 struct schannel_state);
626 return netsec_incoming_packet(state, true,
627 discard_const_p(uint8_t, data),
632 check the signature on a packet
634 static NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
635 const uint8_t *data, size_t length,
636 const uint8_t *whole_pdu, size_t pdu_length,
637 const DATA_BLOB *sig)
639 struct schannel_state *state =
640 talloc_get_type_abort(gensec_security->private_data,
641 struct schannel_state);
643 return netsec_incoming_packet(state, false,
644 discard_const_p(uint8_t, data),
650 static NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
652 uint8_t *data, size_t length,
653 const uint8_t *whole_pdu, size_t pdu_length,
656 struct schannel_state *state =
657 talloc_get_type_abort(gensec_security->private_data,
658 struct schannel_state);
660 return netsec_outgoing_packet(state, mem_ctx, true,
667 static NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
669 const uint8_t *data, size_t length,
670 const uint8_t *whole_pdu, size_t pdu_length,
673 struct schannel_state *state =
674 talloc_get_type_abort(gensec_security->private_data,
675 struct schannel_state);
677 return netsec_outgoing_packet(state, mem_ctx, false,
678 discard_const_p(uint8_t, data),
682 static const struct gensec_security_ops gensec_schannel_security_ops = {
684 .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
685 .client_start = schannel_client_start,
686 .server_start = schannel_server_start,
687 .update = schannel_update,
688 .seal_packet = schannel_seal_packet,
689 .sign_packet = schannel_sign_packet,
690 .check_packet = schannel_check_packet,
691 .unseal_packet = schannel_unseal_packet,
692 .session_info = schannel_session_info,
693 .sig_size = schannel_sig_size,
694 .have_feature = schannel_have_feature,
696 .priority = GENSEC_SCHANNEL
699 _PUBLIC_ NTSTATUS gensec_schannel_init(void)
702 ret = gensec_register(&gensec_schannel_security_ops);
703 if (!NT_STATUS_IS_OK(ret)) {
704 DEBUG(0,("Failed to register '%s' gensec backend!\n",
705 gensec_schannel_security_ops.name));