/* unseal a packet */ NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, const uint8_t *whole_pdu, size_t pdu_length, const DATA_BLOB *sig) { struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state); uint8_t digest_final[16]; uint8_t confounder[8]; uint8_t seq_num[8]; uint8_t sealing_key[16]; static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; if (sig->length != 32) { return NT_STATUS_ACCESS_DENIED; } memcpy(confounder, sig->data+24, 8); RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0:0x80); netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); arcfour_crypt(confounder, sealing_key, 8); arcfour_crypt(data, sealing_key, length); schannel_digest(state->creds->session_key, netsec_sig, confounder, data, length, digest_final); if (memcmp(digest_final, sig->data+16, 8) != 0) { dump_data_pw("calc digest:", digest_final, 8); dump_data_pw("wire digest:", sig->data+16, 8); return NT_STATUS_ACCESS_DENIED; } netsec_deal_with_seq_num(state, digest_final, seq_num); if (memcmp(seq_num, sig->data+8, 8) != 0) { dump_data_pw("calc seq num:", seq_num, 8); dump_data_pw("wire seq num:", sig->data+8, 8); return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
/* seal a packet */ NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, const uint8_t *whole_pdu, size_t pdu_length, DATA_BLOB *sig) { struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state); uint8_t digest_final[16]; uint8_t confounder[8]; uint8_t seq_num[8]; uint8_t sealing_key[16]; static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; generate_random_buffer(confounder, 8); RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); schannel_digest(state->creds->session_key, netsec_sig, confounder, data, length, digest_final); netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); arcfour_crypt(confounder, sealing_key, 8); arcfour_crypt(data, sealing_key, length); netsec_deal_with_seq_num(state, digest_final, seq_num); (*sig) = data_blob_talloc(mem_ctx, NULL, 32); memcpy(sig->data, netsec_sig, 8); memcpy(sig->data+8, seq_num, 8); memcpy(sig->data+16, digest_final, 8); memcpy(sig->data+24, confounder, 8); dump_data_pw("signature:", sig->data+ 0, 8); dump_data_pw("seq_num :", sig->data+ 8, 8); dump_data_pw("digest :", sig->data+16, 8); dump_data_pw("confound :", sig->data+24, 8); return NT_STATUS_OK; }
BOOL netsec_decode(struct netsec_auth_struct *a, int auth_flags, enum netsec_direction direction, RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len) { uchar digest_final[16]; static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE; static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE; const uchar *netsec_sig = NULL; uchar seq_num[8]; DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len)); if (auth_flags & AUTH_PIPE_SEAL) { netsec_sig = netsec_seal_sig; } else if (auth_flags & AUTH_PIPE_SIGN) { netsec_sig = netsec_sign_sig; } /* Create the expected sequence number for comparison */ RSIVAL(seq_num, 0, a->seq_num); switch (direction) { case SENDER_IS_INITIATOR: SIVAL(seq_num, 4, 0x80); break; case SENDER_IS_ACCEPTOR: SIVAL(seq_num, 4, 0x0); break; } DEBUG(10,("SCHANNEL: netsec_decode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len)); dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key)); dump_data_pw("seq_num:\n", seq_num, sizeof(seq_num)); /* extract the sequence number (key based on supplied packet digest) */ /* needs to be done before the sealing, as the original version is used in the sealing stuff... */ netsec_deal_with_seq_num(a, verf); if (memcmp(verf->seq_num, seq_num, sizeof(seq_num))) { /* don't even bother with the below if the sequence number is out */ /* The sequence number is MD5'ed with a key based on the whole-packet digest, as supplied by the client. We check that it's a valid checksum after the decode, below */ DEBUG(2, ("netsec_decode: FAILED: packet sequence number:\n")); dump_data(2, (const char*)verf->seq_num, sizeof(verf->seq_num)); DEBUG(2, ("should be:\n")); dump_data(2, (const char*)seq_num, sizeof(seq_num)); return False; } if (memcmp(verf->sig, netsec_sig, sizeof(verf->sig))) { /* Validate that the other end sent the expected header */ DEBUG(2, ("netsec_decode: FAILED: packet header:\n")); dump_data(2, (const char*)verf->sig, sizeof(verf->sig)); DEBUG(2, ("should be:\n")); dump_data(2, (const char*)netsec_sig, sizeof(netsec_sig)); return False; } if (auth_flags & AUTH_PIPE_SEAL) { uchar sealing_key[16]; /* get the key to extract the data with */ netsec_get_sealing_key(a, verf, sealing_key); /* extract the verification data */ dump_data_pw("verf->confounder:\n", verf->confounder, sizeof(verf->confounder)); SamOEMhash(verf->confounder, sealing_key, 8); dump_data_pw("verf->confounder_dec:\n", verf->confounder, sizeof(verf->confounder)); /* extract the packet payload */ dump_data_pw("data :\n", (const unsigned char *)data, data_len); SamOEMhash((unsigned char *)data, sealing_key, data_len); dump_data_pw("datadec:\n", (const unsigned char *)data, data_len); } /* digest includes 'data' after unsealing */ netsec_digest(a, auth_flags, verf, data, data_len, digest_final); dump_data_pw("Calculated digest:\n", digest_final, sizeof(digest_final)); dump_data_pw("verf->packet_digest:\n", verf->packet_digest, sizeof(verf->packet_digest)); /* compare - if the client got the same result as us, then it must know the session key */ return (memcmp(digest_final, verf->packet_digest, sizeof(verf->packet_digest)) == 0); }
/******************************************************************* Encode a blob of data using the netsec (schannel) alogrithm, also produceing a checksum over the original data. We currently only support signing and sealing togeather - the signing-only code is close, but not quite compatible with what MS does. ********************************************************************/ void netsec_encode(struct netsec_auth_struct *a, int auth_flags, enum netsec_direction direction, RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len) { uchar digest_final[16]; uchar confounder[8]; uchar seq_num[8]; static const uchar nullbytes[8]; static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE; static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE; const uchar *netsec_sig = NULL; DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len)); if (auth_flags & AUTH_PIPE_SEAL) { netsec_sig = netsec_seal_sig; } else if (auth_flags & AUTH_PIPE_SIGN) { netsec_sig = netsec_sign_sig; } /* fill the 'confounder' with random data */ generate_random_buffer(confounder, sizeof(confounder), False); dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key)); RSIVAL(seq_num, 0, a->seq_num); switch (direction) { case SENDER_IS_INITIATOR: SIVAL(seq_num, 4, 0x80); break; case SENDER_IS_ACCEPTOR: SIVAL(seq_num, 4, 0x0); break; } dump_data_pw("verf->seq_num:\n", seq_num, sizeof(verf->seq_num)); init_rpc_auth_netsec_chk(verf, netsec_sig, nullbytes, seq_num, confounder); /* produce a digest of the packet to prove it's legit (before we seal it) */ netsec_digest(a, auth_flags, verf, data, data_len, digest_final); memcpy(verf->packet_digest, digest_final, sizeof(verf->packet_digest)); if (auth_flags & AUTH_PIPE_SEAL) { uchar sealing_key[16]; /* get the key to encode the data with */ netsec_get_sealing_key(a, verf, sealing_key); /* encode the verification data */ dump_data_pw("verf->confounder:\n", verf->confounder, sizeof(verf->confounder)); SamOEMhash(verf->confounder, sealing_key, 8); dump_data_pw("verf->confounder_enc:\n", verf->confounder, sizeof(verf->confounder)); /* encode the packet payload */ dump_data_pw("data:\n", (const unsigned char *)data, data_len); SamOEMhash((unsigned char *)data, sealing_key, data_len); dump_data_pw("data_enc:\n", (const unsigned char *)data, data_len); } /* encode the sequence number (key based on packet digest) */ /* needs to be done after the sealing, as the original version is used in the sealing stuff... */ netsec_deal_with_seq_num(a, verf); return; }