/* sign a packet */ NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const 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 seq_num[8]; static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); schannel_digest(state->creds->session_key, netsec_sig, NULL, data, length, digest_final); 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); memset(sig->data+24, 0, 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; }
/* initialise the credentials chain and return the first client credentials */ void creds_client_init(struct creds_CredentialState *creds, const struct netr_Credential *client_challenge, const struct netr_Credential *server_challenge, const struct samr_Password *machine_password, struct netr_Credential *initial_credential, uint32_t negotiate_flags) { creds->sequence = time(NULL); creds->negotiate_flags = negotiate_flags; dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data)); dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); if (negotiate_flags & NETLOGON_NEG_128BIT) { creds_init_128bit(creds, client_challenge, server_challenge, machine_password); } else { creds_init_64bit(creds, client_challenge, server_challenge, machine_password); } dump_data_pw("Session key", creds->session_key, 16); dump_data_pw("Credential ", creds->client.data, 8); *initial_credential = creds->client; }
NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, uchar *whole_pdu, size_t pdu_length, DATA_BLOB *sig) { if (!(ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL)) { DEBUG(3, ("NTLMSSP Sealing not negotiated - cannot seal packet!\n")); return NT_STATUS_INVALID_PARAMETER; } if (!ntlmssp_state->session_key.length) { DEBUG(3, ("NO session key, cannot seal packet\n")); return NT_STATUS_NO_USER_SESSION_KEY; } DEBUG(10,("ntlmssp_seal_data: seal\n")); dump_data_pw("ntlmssp clear data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { /* The order of these two operations matters - we must first seal the packet, then seal the sequence number - this is becouse the send_seal_hash is not constant, but is is rather updated with each iteration */ NTSTATUS nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, whole_pdu, pdu_length, NTLMSSP_SEND, sig, False); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } arcfour_crypt_sbox(&ntlmssp_state->send_seal_arc4_state, data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { arcfour_crypt_sbox(&ntlmssp_state->send_seal_arc4_state, sig->data+4, 8); } } else { uint32 crc; crc = crc32_calc_buffer(data, length); if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmv1_seq_num)) { return NT_STATUS_NO_MEMORY; } /* The order of these two operations matters - we must first seal the packet, then seal the sequence number - this is becouse the ntlmv1_arc4_state is not constant, but is is rather updated with each iteration */ dump_arc4_state("ntlmv1 arc4 state:\n", &ntlmssp_state->ntlmv1_arc4_state); arcfour_crypt_sbox(&ntlmssp_state->ntlmv1_arc4_state, data, length); dump_arc4_state("ntlmv1 arc4 state:\n", &ntlmssp_state->ntlmv1_arc4_state); arcfour_crypt_sbox(&ntlmssp_state->ntlmv1_arc4_state, sig->data+4, sig->length-4); ntlmssp_state->ntlmv1_seq_num++; } dump_data_pw("ntlmssp signature\n", sig->data, sig->length); dump_data_pw("ntlmssp sealed data\n", data, length); return NT_STATUS_OK; }
void creds_client_init(uint32 neg_flags, struct dcinfo *dc, DOM_CHAL *clnt_chal, DOM_CHAL *srv_chal, const unsigned char mach_pw[16], DOM_CHAL *init_chal_out) { dc->sequence = time(NULL); DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags)); DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) )); DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) )); dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16); /* Generate the session key and the next client and server creds. */ if (neg_flags & NETLOGON_NEG_128BIT) { creds_init_128(dc, clnt_chal, srv_chal, mach_pw); } else { creds_init_64(dc, clnt_chal, srv_chal, mach_pw); } dump_data_pw("creds_client_init: session key", dc->sess_key, 16); DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) )); DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) )); DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) )); memcpy(init_chal_out->data, dc->clnt_chal.data, 8); }
/* check that a credentials reply from a server is correct */ BOOL creds_server_check(const struct creds_CredentialState *creds, const struct netr_Credential *received_credentials) { if (memcmp(received_credentials->data, creds->client.data, 8) != 0) { DEBUG(2,("credentials check failed\n")); dump_data_pw("client creds", creds->client.data, 8); dump_data_pw("calc creds", received_credentials->data, 8); return False; } return True; }
/* check that a credentials reply from a server is correct */ static bool netlogon_creds_server_check_internal(const struct netlogon_creds_CredentialState *creds, const struct netr_Credential *received_credentials) { if (memcmp(received_credentials->data, creds->client.data, 8) != 0) { DEBUG(2,("credentials check failed\n")); dump_data_pw("client creds", creds->client.data, 8); dump_data_pw("calc creds", received_credentials->data, 8); return false; } return true; }
/******************************************************************* Create a digest over the entire packet (including the data), and MD5 it with the session key. ********************************************************************/ static void netsec_digest(struct netsec_auth_struct *a, int auth_flags, RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len, uchar digest_final[16]) { uchar whole_packet_digest[16]; static uchar zeros[4]; struct MD5Context ctx3; /* verfiy the signature on the packet by MD5 over various bits */ MD5Init(&ctx3); /* use our sequence number, which ensures the packet is not out of order */ MD5Update(&ctx3, zeros, sizeof(zeros)); MD5Update(&ctx3, verf->sig, sizeof(verf->sig)); if (auth_flags & AUTH_PIPE_SEAL) { MD5Update(&ctx3, verf->confounder, sizeof(verf->confounder)); } MD5Update(&ctx3, (const unsigned char *)data, data_len); MD5Final(whole_packet_digest, &ctx3); dump_data_pw("whole_packet_digest:\n", whole_packet_digest, sizeof(whole_packet_digest)); /* MD5 this result and the session key, to prove that only a valid client could had produced this */ hmac_md5(a->sess_key, whole_packet_digest, sizeof(whole_packet_digest), digest_final); }
/*********************************************************** SMB signing - Simple implementation - setup the MAC key. ************************************************************/ bool smbcli_simple_set_signing(TALLOC_CTX *mem_ctx, struct smb_signing_context *sign_info, const DATA_BLOB *user_session_key, const DATA_BLOB *response) { if (sign_info->mandatory_signing) { DEBUG(5, ("Mandatory SMB signing enabled!\n")); } DEBUG(5, ("SMB signing enabled!\n")); if (response && response->length) { sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, response->length + user_session_key->length); } else { sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, user_session_key->length); } memcpy(&sign_info->mac_key.data[0], user_session_key->data, user_session_key->length); if (response && response->length) { memcpy(&sign_info->mac_key.data[user_session_key->length],response->data, response->length); } dump_data_pw("Started Signing with key:\n", sign_info->mac_key.data, sign_info->mac_key.length); sign_info->signing_state = SMB_SIGNING_ENGINE_ON; sign_info->next_seq_num = 2; return true; }
/* 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; }
struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *mem_ctx, const char *client_account, const char *client_computer_name, const struct netr_Credential *client_challenge, const struct netr_Credential *server_challenge, const struct samr_Password *machine_password, struct netr_Credential *initial_credential, uint32_t negotiate_flags) { struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); if (!creds) { return NULL; } creds->sequence = time(NULL); creds->negotiate_flags = negotiate_flags; creds->computer_name = talloc_strdup(creds, client_computer_name); if (!creds->computer_name) { talloc_free(creds); return NULL; } creds->account_name = talloc_strdup(creds, client_account); if (!creds->account_name) { talloc_free(creds); return NULL; } dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data)); dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); if (negotiate_flags & NETLOGON_NEG_128BIT) { netlogon_creds_init_128bit(creds, client_challenge, server_challenge, machine_password); } else { netlogon_creds_init_64bit(creds, client_challenge, server_challenge, machine_password); } netlogon_creds_first_step(creds, client_challenge, server_challenge); dump_data_pw("Session key", creds->session_key, 16); dump_data_pw("Credential ", creds->client.data, 8); *initial_credential = creds->client; return creds; }
/******************************************************************* Encode or Decode the sequence number (which is symmetric) ********************************************************************/ static void netsec_deal_with_seq_num(struct netsec_auth_struct *a, RPC_AUTH_NETSEC_CHK *verf) { static uchar zeros[4]; uchar sequence_key[16]; uchar digest1[16]; hmac_md5(a->sess_key, zeros, sizeof(zeros), digest1); dump_data_pw("(sequence key) digest1:\n", digest1, sizeof(digest1)); hmac_md5(digest1, verf->packet_digest, 8, sequence_key); dump_data_pw("sequence_key:\n", sequence_key, sizeof(sequence_key)); dump_data_pw("seq_num (before):\n", verf->seq_num, sizeof(verf->seq_num)); SamOEMhash(verf->seq_num, sequence_key, 8); dump_data_pw("seq_num (after):\n", verf->seq_num, sizeof(verf->seq_num)); }
NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, DATA_BLOB *sig) { if (!ntlmssp_state->session_key.length) { DEBUG(3, ("NO session key, cannot unseal packet\n")); return NT_STATUS_NO_USER_SESSION_KEY; } DEBUG(10,("ntlmssp__unseal_data: seal\n")); dump_data_pw("ntlmssp sealed data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { NTLMSSPcalc_ap(ntlmssp_state->recv_seal_hash, data, length); } else { dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length); } dump_data_pw("ntlmssp clear data\n", data, length); return ntlmssp_check_packet(ntlmssp_state, data, length, sig); }
bool smb_signing_activate(struct smb_signing_state *si, const DATA_BLOB user_session_key, const DATA_BLOB response) { size_t len; off_t ofs; if (!user_session_key.length) { return false; } if (!si->negotiated) { return false; } if (si->active) { return false; } if (si->mac_key.length > 0) { return false; } smb_signing_reset_info(si); len = response.length + user_session_key.length; si->mac_key = data_blob_talloc(si, NULL, len); ofs = 0; memcpy(&si->mac_key.data[ofs], user_session_key.data, user_session_key.length); DEBUG(10, ("smb_signing_activate: user_session_key\n")); dump_data(10, user_session_key.data, user_session_key.length); if (response.length) { ofs = user_session_key.length; memcpy(&si->mac_key.data[ofs], response.data, response.length); DEBUG(10, ("smb_signing_activate: response_data\n")); dump_data(10, response.data, response.length); } else { DEBUG(10, ("smb_signing_activate: NULL response_data\n")); } dump_data_pw("smb_signing_activate: mac key is:\n", si->mac_key.data, si->mac_key.length); /* Initialise the sequence number */ si->seqnum = 2; return true; }
NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, uchar *whole_pdu, size_t pdu_length, DATA_BLOB *sig) { if (!ntlmssp_state->session_key.length) { DEBUG(3, ("NO session key, cannot unseal packet\n")); return NT_STATUS_NO_USER_SESSION_KEY; } DEBUG(10,("ntlmssp_unseal_packet: seal\n")); dump_data_pw("ntlmssp sealed data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { /* First unseal the data. */ arcfour_crypt_sbox(&ntlmssp_state->recv_seal_arc4_state, data, length); dump_data_pw("ntlmv2 clear data\n", data, length); } else { arcfour_crypt_sbox(&ntlmssp_state->ntlmv1_arc4_state, data, length); dump_data_pw("ntlmv1 clear data\n", data, length); } return ntlmssp_check_packet(ntlmssp_state, data, length, whole_pdu, pdu_length, sig); }
/******************************************************************* Calculate the key with which to encode the data payload ********************************************************************/ static void netsec_get_sealing_key(struct netsec_auth_struct *a, RPC_AUTH_NETSEC_CHK *verf, uchar sealing_key[16]) { static uchar zeros[4]; uchar digest2[16]; uchar sess_kf0[16]; int i; for (i = 0; i < sizeof(sess_kf0); i++) { sess_kf0[i] = a->sess_key[i] ^ 0xf0; } dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0)); /* MD5 of sess_kf0 and 4 zero bytes */ hmac_md5(sess_kf0, zeros, 0x4, digest2); dump_data_pw("digest2:\n", digest2, sizeof(digest2)); /* MD5 of the above result, plus 8 bytes of sequence number */ hmac_md5(digest2, verf->seq_num, sizeof(verf->seq_num), sealing_key); dump_data_pw("sealing_key:\n", sealing_key, 16); }
static void creds_init_128(struct dcinfo *dc, const DOM_CHAL *clnt_chal_in, const DOM_CHAL *srv_chal_in, const unsigned char mach_pw[16]) { unsigned char zero[4], tmp[16]; HMACMD5Context ctx; struct MD5Context md5; /* Just in case this isn't already there */ memcpy(dc->mach_pw, mach_pw, 16); ZERO_STRUCT(dc->sess_key); memset(zero, 0, sizeof(zero)); hmac_md5_init_rfc2104(mach_pw, 16, &ctx); MD5Init(&md5); MD5Update(&md5, zero, sizeof(zero)); MD5Update(&md5, clnt_chal_in->data, 8); MD5Update(&md5, srv_chal_in->data, 8); MD5Final(tmp, &md5); hmac_md5_update(tmp, sizeof(tmp), &ctx); hmac_md5_final(dc->sess_key, &ctx); /* debug output */ DEBUG(5,("creds_init_128\n")); DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data))); DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data))); dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16); /* Generate the next client and server creds. */ des_crypt112(dc->clnt_chal.data, /* output */ clnt_chal_in->data, /* input */ dc->sess_key, /* input */ 1); des_crypt112(dc->srv_chal.data, /* output */ srv_chal_in->data, /* input */ dc->sess_key, /* input */ 1); /* Seed is the client chal. */ memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8); }
static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state, const uchar *data, size_t length, enum ntlmssp_direction direction, DATA_BLOB *sig) { if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { HMACMD5Context ctx; uchar seq_num[4]; uchar digest[16]; SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx); hmac_md5_update(seq_num, 4, &ctx); hmac_md5_update(data, length, &ctx); hmac_md5_final(digest, &ctx); if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */ , ntlmssp_state->ntlmssp_seq_num)) { return NT_STATUS_NO_MEMORY; } if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { switch (direction) { case NTLMSSP_SEND: NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); break; case NTLMSSP_RECEIVE: NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4); break; } } } else { uint32 crc; crc = crc32_calc_buffer((const char *)data, length); if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) { return NT_STATUS_NO_MEMORY; } dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); } return NT_STATUS_OK; }
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, DATA_BLOB *session_key) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; krb5_auth_context auth_context = gensec_krb5_state->auth_context; krb5_keyblock *skey; krb5_error_code err = -1; if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) { return NT_STATUS_NO_USER_SESSION_KEY; } if (gensec_krb5_state->session_key.data) { *session_key = gensec_krb5_state->session_key; return NT_STATUS_OK; } switch (gensec_security->gensec_role) { case GENSEC_CLIENT: err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); break; case GENSEC_SERVER: err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); break; } if (err == 0 && skey != NULL) { DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey))); gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); *session_key = gensec_krb5_state->session_key; dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); krb5_free_keyblock(context, skey); return NT_STATUS_OK; } else { DEBUG(10, ("KRB5 error getting session key %d\n", err)); return NT_STATUS_NO_USER_SESSION_KEY; } }
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote) { krb5_keyblock *skey; krb5_error_code err; BOOL ret = False; if (remote) err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); else err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); if (err == 0 && skey != NULL) { DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey))); *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); ret = True; krb5_free_keyblock(context, skey); } else { DEBUG(10, ("KRB5 error getting session key %d\n", err)); } return ret; }
/* check the signature on a packet */ NTSTATUS schannel_check_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const 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 seq_num[8]; static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; /* w2k sends just 24 bytes and skip the confounder */ if (sig->length != 32 && sig->length != 24) { return NT_STATUS_ACCESS_DENIED; } RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0:0x80); dump_data_pw("seq_num:\n", seq_num, 8); dump_data_pw("sess_key:\n", state->creds->session_key, 16); schannel_digest(state->creds->session_key, netsec_sig, NULL, data, length, digest_final); 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; } 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; } return NT_STATUS_OK; }
static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, const DATA_BLOB reply, DATA_BLOB *next_request) { uint32 chal_flags, ntlmssp_command, unkn1, unkn2; DATA_BLOB server_domain_blob; DATA_BLOB challenge_blob; DATA_BLOB struct_blob = data_blob_null; char *server_domain; const char *chal_parse_string; const char *auth_gen_string; DATA_BLOB lm_response = data_blob_null; DATA_BLOB nt_response = data_blob_null; DATA_BLOB session_key = data_blob_null; DATA_BLOB encrypted_session_key = data_blob_null; NTSTATUS nt_status = NT_STATUS_OK; if (!msrpc_parse(&reply, "CdBd", "NTLMSSP", &ntlmssp_command, &server_domain_blob, &chal_flags)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); dump_data(2, reply.data, reply.length); return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&server_domain_blob); DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, lp_client_lanman_auth()); if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; } auth_gen_string = "CdBBUUUBd"; } else { if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { chal_parse_string = "CdAdbdd"; } auth_gen_string = "CdBBAAABd"; } DEBUG(3, ("NTLMSSP: Set final flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); if (!msrpc_parse(&reply, chal_parse_string, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8, &unkn1, &unkn2, &struct_blob)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); dump_data(2, reply.data, reply.length); return NT_STATUS_INVALID_PARAMETER; } ntlmssp_state->server_domain = talloc_strdup(ntlmssp_state->mem_ctx, server_domain); SAFE_FREE(server_domain); if (challenge_blob.length != 8) { data_blob_free(&struct_blob); return NT_STATUS_INVALID_PARAMETER; } if (!ntlmssp_state->nt_hash || !ntlmssp_state->lm_hash) { uchar zeros[16]; /* do nothing - blobs are zero length */ ZERO_STRUCT(zeros); /* session key is all zeros */ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, zeros, 16); /* not doing NLTM2 without a password */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; } else if (ntlmssp_state->use_ntlmv2) { if (!struct_blob.length) { /* be lazy, match win2k - we can't do NTLMv2 without it */ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); return NT_STATUS_INVALID_PARAMETER; } /* TODO: if the remote server is standalone, then we should replace 'domain' with the server name as supplied above */ if (!SMBNTLMv2encrypt_hash(ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->nt_hash, &challenge_blob, &struct_blob, &lm_response, &nt_response, &session_key)) { data_blob_free(&challenge_blob); data_blob_free(&struct_blob); return NT_STATUS_NO_MEMORY; } } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { struct MD5Context md5_session_nonce_ctx; uchar session_nonce[16]; uchar session_nonce_hash[16]; uchar user_session_key[16]; lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); generate_random_buffer(lm_response.data, 8); memset(lm_response.data+8, 0, 16); memcpy(session_nonce, challenge_blob.data, 8); memcpy(&session_nonce[8], lm_response.data, 8); MD5Init(&md5_session_nonce_ctx); MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); MD5Final(session_nonce_hash, &md5_session_nonce_ctx); DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); DEBUG(5, ("challenge is: \n")); dump_data(5, session_nonce_hash, 8); nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBNTencrypt_hash(ntlmssp_state->nt_hash, session_nonce_hash, nt_response.data); session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); SMBsesskeygen_ntv1(ntlmssp_state->nt_hash, NULL, user_session_key); hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); } else { /* lanman auth is insecure, it may be disabled */ if (lp_client_lanman_auth()) { lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBencrypt_hash(ntlmssp_state->lm_hash,challenge_blob.data, lm_response.data); } nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBNTencrypt_hash(ntlmssp_state->nt_hash,challenge_blob.data, nt_response.data); session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lp_client_lanman_auth()) { SMBsesskeygen_lm_sess_key(ntlmssp_state->lm_hash, lm_response.data, session_key.data); dump_data_pw("LM session key\n", session_key.data, session_key.length); } else { SMBsesskeygen_ntv1(ntlmssp_state->nt_hash, NULL, session_key.data); dump_data_pw("NT session key:\n", session_key.data, session_key.length); } } data_blob_free(&struct_blob); /* Key exchange encryptes a new client-generated session key with the password-derived key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* Make up a new session key */ uint8 client_session_key[16]; generate_random_buffer(client_session_key, sizeof(client_session_key)); /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); /* Mark the new session key as the 'real' session key */ data_blob_free(&session_key); session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key)); } /* this generates the actual auth packet */ if (!msrpc_gen(next_request, auth_gen_string, "NTLMSSP", NTLMSSP_AUTH, lm_response.data, lm_response.length, nt_response.data, nt_response.length, ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->get_global_myname(), encrypted_session_key.data, encrypted_session_key.length, ntlmssp_state->neg_flags)) { return NT_STATUS_NO_MEMORY; } data_blob_free(&encrypted_session_key); data_blob_free(&ntlmssp_state->chal); ntlmssp_state->session_key = session_key; ntlmssp_state->chal = challenge_blob; ntlmssp_state->lm_resp = lm_response; ntlmssp_state->nt_resp = nt_response; ntlmssp_state->expected_state = NTLMSSP_DONE; if (!NT_STATUS_IS_OK(nt_status = ntlmssp_sign_init(ntlmssp_state))) { DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status))); } return nt_status; }
static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, const DATA_BLOB request, DATA_BLOB *reply) { DATA_BLOB encrypted_session_key = data_blob_null; DATA_BLOB user_session_key = data_blob_null; DATA_BLOB lm_session_key = data_blob_null; DATA_BLOB session_key = data_blob_null; uint32 ntlmssp_command, auth_flags; NTSTATUS nt_status = NT_STATUS_OK; /* used by NTLM2 */ bool doing_ntlm2 = False; uchar session_nonce[16]; uchar session_nonce_hash[16]; const char *parse_string; char *domain = NULL; char *user = NULL; char *workstation = NULL; /* parse the NTLMSSP packet */ *reply = data_blob_null; #if 0 file_save("ntlmssp_auth.dat", request.data, request.length); #endif if (ntlmssp_state->unicode) { parse_string = "CdBBUUUBd"; } else { parse_string = "CdBBAAABd"; } data_blob_free(&ntlmssp_state->lm_resp); data_blob_free(&ntlmssp_state->nt_resp); ntlmssp_state->user = NULL; ntlmssp_state->domain = NULL; ntlmssp_state->workstation = NULL; /* now the NTLMSSP encoded auth hashes */ if (!msrpc_parse(&request, parse_string, "NTLMSSP", &ntlmssp_command, &ntlmssp_state->lm_resp, &ntlmssp_state->nt_resp, &domain, &user, &workstation, &encrypted_session_key, &auth_flags)) { SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); data_blob_free(&encrypted_session_key); auth_flags = 0; /* Try again with a shorter string (Win9X truncates this packet) */ if (ntlmssp_state->unicode) { parse_string = "CdBBUUU"; } else { parse_string = "CdBBAAA"; } /* now the NTLMSSP encoded auth hashes */ if (!msrpc_parse(&request, parse_string, "NTLMSSP", &ntlmssp_command, &ntlmssp_state->lm_resp, &ntlmssp_state->nt_resp, &domain, &user, &workstation)) { DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP (tried both formats):\n")); dump_data(2, request.data, request.length); SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); return NT_STATUS_INVALID_PARAMETER; } } if (auth_flags) ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, lp_lanman_auth()); if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); data_blob_free(&encrypted_session_key); return nt_status; } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); data_blob_free(&encrypted_session_key); return nt_status; } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); data_blob_free(&encrypted_session_key); return nt_status; } SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); #if 0 file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length); file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); #endif /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a client challenge However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { struct MD5Context md5_session_nonce_ctx; SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8); doing_ntlm2 = True; memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8); memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8); MD5Init(&md5_session_nonce_ctx); MD5Update(&md5_session_nonce_ctx, session_nonce, 16); MD5Final(session_nonce_hash, &md5_session_nonce_ctx); ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, session_nonce_hash, 8); /* LM response is no longer useful */ data_blob_free(&ntlmssp_state->lm_resp); /* We changed the effective challenge - set it */ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) { data_blob_free(&encrypted_session_key); return nt_status; } /* LM Key is incompatible. */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } } /* * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth * is required (by "ntlm auth = no" and "lm auth = no" being set in the * smb.conf file) and no NTLMv2 response was sent then the password check * will fail here. JRA. */ /* Finally, actually ask if the password is OK */ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, &user_session_key, &lm_session_key))) { data_blob_free(&encrypted_session_key); return nt_status; } dump_data_pw("NT session key:\n", user_session_key.data, user_session_key.length); dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length); /* Handle the different session key derivation for NTLM2 */ if (doing_ntlm2) { if (user_session_key.data && user_session_key.length == 16) { session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); hmac_md5(user_session_key.data, session_nonce, sizeof(session_nonce), session_key.data); DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n")); dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); } else { DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n")); session_key = data_blob_null; } } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { if (lm_session_key.data && lm_session_key.length >= 8) { if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); if (session_key.data == NULL) { return NT_STATUS_NO_MEMORY; } SMBsesskeygen_lm_sess_key(lm_session_key.data, ntlmssp_state->lm_resp.data, session_key.data); DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); } else { uint8 zeros[24]; ZERO_STRUCT(zeros); session_key = data_blob_talloc( ntlmssp_state->mem_ctx, NULL, 16); if (session_key.data == NULL) { return NT_STATUS_NO_MEMORY; } SMBsesskeygen_lm_sess_key( lm_session_key.data, zeros, session_key.data); } dump_data_pw("LM session key:\n", session_key.data, session_key.length); } else { DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); session_key = data_blob_null; } } else if (user_session_key.data) { session_key = user_session_key; DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n")); dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); } else if (lm_session_key.data) { session_key = lm_session_key; DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n")); dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); } else { DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); session_key = data_blob_null; } /* With KEY_EXCH, the client supplies the proposed session key, but encrypts it with the long-term key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { if (!encrypted_session_key.data || encrypted_session_key.length != 16) { data_blob_free(&encrypted_session_key); DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", (unsigned int)encrypted_session_key.length)); return NT_STATUS_INVALID_PARAMETER; } else if (!session_key.data || session_key.length != 16) { DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", (unsigned int)session_key.length)); ntlmssp_state->session_key = session_key; } else { dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length); ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx, encrypted_session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); } } else { ntlmssp_state->session_key = session_key; } if (!NT_STATUS_IS_OK(nt_status)) { ntlmssp_state->session_key = data_blob_null; } else if (ntlmssp_state->session_key.length) { nt_status = ntlmssp_sign_init(ntlmssp_state); } data_blob_free(&encrypted_session_key); /* Only one authentication allowed per server state. */ ntlmssp_state->expected_state = NTLMSSP_DONE; return nt_status; }
_PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, int *flags, DATA_BLOB challenge, const NTTIME *server_timestamp, DATA_BLOB target_info, DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) { const char *user, *domain; DATA_BLOB lm_response, nt_response; DATA_BLOB lm_session_key, session_key; const struct samr_Password *nt_hash; lm_session_key = data_blob(NULL, 0); /* We may already have an NTLM response we prepared earlier. * This is used for NTLM pass-though authentication */ if (cred->nt_response.data || cred->lm_response.data) { *_nt_response = cred->nt_response; *_lm_response = cred->lm_response; if (!cred->lm_response.data) { *flags = *flags & ~CLI_CRED_LANMAN_AUTH; } *_lm_session_key = data_blob(NULL, 0); *_session_key = data_blob(NULL, 0); return NT_STATUS_OK; } nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx); cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain); /* If we are sending a username@realm login (see function * above), then we will not send LM, it will not be * accepted */ if (cred->principal_obtained > cred->username_obtained) { *flags = *flags & ~CLI_CRED_LANMAN_AUTH; } /* Likewise if we are a machine account (avoid protocol downgrade attacks) */ if (cred->machine_account) { *flags = *flags & ~CLI_CRED_LANMAN_AUTH; } if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) { return NT_STATUS_ACCESS_DENIED; } if (!nt_hash) { static const uint8_t zeros[16]; /* do nothing - blobs are zero length */ /* session key is all zeros */ session_key = data_blob_talloc(mem_ctx, zeros, 16); lm_session_key = data_blob_talloc(mem_ctx, zeros, 16); lm_response = data_blob(NULL, 0); nt_response = data_blob(NULL, 0); /* not doing NTLM2 without a password */ *flags &= ~CLI_CRED_NTLM2; } else if (*flags & CLI_CRED_NTLMv2_AUTH) { if (!target_info.length) { /* be lazy, match win2k - we can't do NTLMv2 without it */ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); return NT_STATUS_INVALID_PARAMETER; } /* TODO: if the remote server is standalone, then we should replace 'domain' with the server name as supplied above */ if (!SMBNTLMv2encrypt_hash(mem_ctx, user, domain, nt_hash->hash, &challenge, server_timestamp, &target_info, &lm_response, &nt_response, NULL, &session_key)) { return NT_STATUS_NO_MEMORY; } /* LM Key is incompatible... */ *flags &= ~CLI_CRED_LANMAN_AUTH; if (lm_response.length != 0) { /* * We should not expose the lm key. */ memset(lm_response.data, 0, lm_response.length); } } else if (*flags & CLI_CRED_NTLM2) { MD5_CTX md5_session_nonce_ctx; uint8_t session_nonce[16]; uint8_t session_nonce_hash[16]; uint8_t user_session_key[16]; lm_response = data_blob_talloc(mem_ctx, NULL, 24); generate_random_buffer(lm_response.data, 8); memset(lm_response.data+8, 0, 16); memcpy(session_nonce, challenge.data, 8); memcpy(&session_nonce[8], lm_response.data, 8); MD5Init(&md5_session_nonce_ctx); MD5Update(&md5_session_nonce_ctx, challenge.data, 8); MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); MD5Final(session_nonce_hash, &md5_session_nonce_ctx); DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); DEBUG(5, ("challenge is: \n")); dump_data(5, session_nonce_hash, 8); nt_response = data_blob_talloc(mem_ctx, NULL, 24); SMBOWFencrypt(nt_hash->hash, session_nonce_hash, nt_response.data); session_key = data_blob_talloc(mem_ctx, NULL, 16); SMBsesskeygen_ntv1(nt_hash->hash, user_session_key); hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); /* LM Key is incompatible... */ *flags &= ~CLI_CRED_LANMAN_AUTH; } else { uint8_t lm_hash[16]; nt_response = data_blob_talloc(mem_ctx, NULL, 24); SMBOWFencrypt(nt_hash->hash, challenge.data, nt_response.data); session_key = data_blob_talloc(mem_ctx, NULL, 16); SMBsesskeygen_ntv1(nt_hash->hash, session_key.data); dump_data_pw("NT session key:\n", session_key.data, session_key.length); /* lanman auth is insecure, it may be disabled. We may also not have a password */ if (*flags & CLI_CRED_LANMAN_AUTH) { const char *password; password = cli_credentials_get_password(cred); if (!password) { lm_response = nt_response; } else { lm_response = data_blob_talloc(mem_ctx, NULL, 24); if (!SMBencrypt(password,challenge.data, lm_response.data)) { /* If the LM password was too long (and therefore the LM hash being of the first 14 chars only), don't send it. We don't have any better options but to send the NT response */ data_blob_free(&lm_response); lm_response = nt_response; /* LM Key is incompatible with 'long' passwords */ *flags &= ~CLI_CRED_LANMAN_AUTH; } else if (E_deshash(password, lm_hash)) { lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); memcpy(lm_session_key.data, lm_hash, 8); memset(&lm_session_key.data[8], '\0', 8); if (!(*flags & CLI_CRED_NTLM_AUTH)) { session_key = lm_session_key; } } } } else { const char *password; /* LM Key is incompatible... */ lm_response = nt_response; *flags &= ~CLI_CRED_LANMAN_AUTH; password = cli_credentials_get_password(cred); if (password && E_deshash(password, lm_hash)) { lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); memcpy(lm_session_key.data, lm_hash, 8); memset(&lm_session_key.data[8], '\0', 8); } } } if (_lm_response) { *_lm_response = lm_response; } if (_nt_response) { *_nt_response = nt_response; } if (_lm_session_key) { *_lm_session_key = lm_session_key; } if (_session_key) { *_session_key = session_key; } return NT_STATUS_OK; }
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_ntlmssp_context *gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, struct gensec_ntlmssp_context); struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state; uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; DATA_BLOB server_domain_blob; DATA_BLOB challenge_blob; DATA_BLOB target_info = data_blob(NULL, 0); char *server_domain; const char *chal_parse_string; const char *auth_gen_string; DATA_BLOB lm_response = data_blob(NULL, 0); DATA_BLOB nt_response = data_blob(NULL, 0); DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB lm_session_key = data_blob(NULL, 0); DATA_BLOB encrypted_session_key = data_blob(NULL, 0); NTSTATUS nt_status; int flags = 0; const char *user, *domain; TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } if (!msrpc_parse(mem_ctx, &in, "CdBd", "NTLMSSP", &ntlmssp_command, &server_domain_blob, &chal_flags)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&server_domain_blob); DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; } auth_gen_string = "CdBBUUUBd"; } else { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { chal_parse_string = "CdAdbdd"; } auth_gen_string = "CdBBAAABd"; } if (!msrpc_parse(mem_ctx, &in, chal_parse_string, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8, &unkn1, &unkn2, &target_info)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) { ntlmssp_state->server.is_standalone = true; } else { ntlmssp_state->server.is_standalone = false; } /* TODO: parse struct_blob and fill in the rest */ ntlmssp_state->server.netbios_name = ""; ntlmssp_state->server.netbios_domain = server_domain; ntlmssp_state->server.dns_name = ""; ntlmssp_state->server.dns_domain = ""; if (challenge_blob.length != 8) { talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx, &user, &domain); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { flags |= CLI_CRED_NTLM2; } if (ntlmssp_state->use_ntlmv2) { flags |= CLI_CRED_NTLMv2_AUTH; } if (ntlmssp_state->use_nt_response) { flags |= CLI_CRED_NTLM_AUTH; } if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx, &flags, challenge_blob, target_info, &lm_response, &nt_response, &lm_session_key, &session_key); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (!(flags & CLI_CRED_LANMAN_AUTH)) { /* LM Key is still possible, just silly, so we do not * allow it. Fortunetly all LM crypto is off by * default and we require command line options to end * up here */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(flags & CLI_CRED_NTLM2)) { /* NTLM2 is incompatible... */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; } if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) { DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16); if (lm_response.length == 24) { SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, new_session_key.data); } else { static const uint8_t zeros[24]; SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, new_session_key.data); } session_key = new_session_key; dump_data_pw("LM session key\n", session_key.data, session_key.length); } /* Key exchange encryptes a new client-generated session key with the password-derived key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* Make up a new session key */ uint8_t client_session_key[16]; generate_secret_buffer(client_session_key, sizeof(client_session_key)); /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); /* Mark the new session key as the 'real' session key */ session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key)); } DEBUG(3, ("NTLMSSP: Set final flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); /* this generates the actual auth packet */ nt_status = msrpc_gen(mem_ctx, out, auth_gen_string, "NTLMSSP", NTLMSSP_AUTH, lm_response.data, lm_response.length, nt_response.data, nt_response.length, domain, user, cli_credentials_get_workstation(gensec_security->credentials), encrypted_session_key.data, encrypted_session_key.length, ntlmssp_state->neg_flags); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } ntlmssp_state->session_key = session_key; talloc_steal(ntlmssp_state, session_key.data); talloc_steal(out_mem_ctx, out->data); ntlmssp_state->expected_state = NTLMSSP_DONE; if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { nt_status = ntlmssp_sign_init(ntlmssp_state); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status))); talloc_free(mem_ctx); return nt_status; } } talloc_free(mem_ctx); return NT_STATUS_OK; }
/** Initialise the state for NTLMSSP signing. */ NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state) { unsigned char p24[24]; ZERO_STRUCT(p24); DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); if (!ntlmssp_state->session_key.length) { DEBUG(3, ("NO session key, cannot intialise signing\n")); return NT_STATUS_NO_USER_SESSION_KEY; } if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { const char *send_sign_const; const char *send_seal_const; const char *recv_sign_const; const char *recv_seal_const; switch (ntlmssp_state->role) { case NTLMSSP_CLIENT: send_sign_const = CLI_SIGN; send_seal_const = CLI_SEAL; recv_sign_const = SRV_SIGN; recv_seal_const = SRV_SEAL; break; case NTLMSSP_SERVER: send_sign_const = SRV_SIGN; send_seal_const = SRV_SEAL; recv_sign_const = CLI_SIGN; recv_seal_const = CLI_SEAL; break; } calc_ntlmv2_hash(ntlmssp_state->send_sign_hash, ntlmssp_state->send_sign_const, ntlmssp_state->session_key, send_sign_const); dump_data_pw("NTLMSSP send sign hash:\n", ntlmssp_state->send_sign_hash, sizeof(ntlmssp_state->send_sign_hash)); calc_ntlmv2_hash(ntlmssp_state->send_seal_hash, ntlmssp_state->send_seal_const, ntlmssp_state->session_key, send_seal_const); dump_data_pw("NTLMSSP send sesl hash:\n", ntlmssp_state->send_seal_hash, sizeof(ntlmssp_state->send_seal_hash)); calc_ntlmv2_hash(ntlmssp_state->recv_sign_hash, ntlmssp_state->recv_sign_const, ntlmssp_state->session_key, recv_sign_const); dump_data_pw("NTLMSSP receive sign hash:\n", ntlmssp_state->recv_sign_hash, sizeof(ntlmssp_state->recv_sign_hash)); calc_ntlmv2_hash(ntlmssp_state->recv_seal_hash, ntlmssp_state->recv_seal_const, ntlmssp_state->session_key, recv_seal_const); dump_data_pw("NTLMSSP receive seal hash:\n", ntlmssp_state->recv_sign_hash, sizeof(ntlmssp_state->recv_sign_hash)); } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 8) { /* can't sign or check signatures yet */ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use LM KEY yet\n")); return NT_STATUS_UNSUCCESSFUL; } DEBUG(5, ("NTLMSSP Sign/Seal - using LM KEY\n")); calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 8); dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); } else { if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 16) { /* can't sign or check signatures yet */ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use NT KEY yet\n")); return NT_STATUS_UNSUCCESSFUL; } DEBUG(5, ("NTLMSSP Sign/Seal - using NT KEY\n")); calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 16); dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); } ntlmssp_state->ntlmssp_seq_num = 0; return NT_STATUS_OK; }
NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, DATA_BLOB *sig) { if (!ntlmssp_state->session_key.length) { DEBUG(3, ("NO session key, cannot seal packet\n")); return NT_STATUS_NO_USER_SESSION_KEY; } DEBUG(10,("ntlmssp_seal_data: seal\n")); dump_data_pw("ntlmssp clear data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { HMACMD5Context ctx; char seq_num[4]; uchar digest[16]; SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx); hmac_md5_update((const unsigned char *)seq_num, 4, &ctx); hmac_md5_update(data, length, &ctx); hmac_md5_final(digest, &ctx); if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */ , ntlmssp_state->ntlmssp_seq_num)) { return NT_STATUS_NO_MEMORY; } dump_data_pw("ntlmssp client sealing hash:\n", ntlmssp_state->send_seal_hash, sizeof(ntlmssp_state->send_seal_hash)); NTLMSSPcalc_ap(ntlmssp_state->send_seal_hash, data, length); dump_data_pw("ntlmssp client signing hash:\n", ntlmssp_state->send_sign_hash, sizeof(ntlmssp_state->send_sign_hash)); NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); } else { uint32 crc; crc = crc32_calc_buffer((const char *)data, length); if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) { return NT_STATUS_NO_MEMORY; } /* The order of these two operations matters - we must first seal the packet, then seal the sequence number - this is becouse the ntlmssp_hash is not constant, but is is rather updated with each iteration */ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length); dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, sizeof(ntlmssp_state->ntlmssp_hash)); NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); } dump_data_pw("ntlmssp sealed data\n", data, length); /* increment counter on send */ ntlmssp_state->ntlmssp_seq_num++; return NT_STATUS_OK; }
static bool torture_ntlmssp_self_check(struct torture_context *tctx) { struct gensec_security *gensec_security; struct gensec_ntlmssp_state *gensec_ntlmssp_state; DATA_BLOB data; DATA_BLOB sig, expected_sig; TALLOC_CTX *mem_ctx = tctx; torture_assert_ntstatus_ok(tctx, gensec_client_start(mem_ctx, &gensec_security, tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx)), "gensec client start"); gensec_set_credentials(gensec_security, cmdline_credentials); gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); torture_assert_ntstatus_ok(tctx, gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP), "Failed to start GENSEC for NTLMSSP"); gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data; gensec_ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405060708090a0b0c0d0e0f00"); dump_data_pw("NTLMSSP session key: \n", gensec_ntlmssp_state->session_key.data, gensec_ntlmssp_state->session_key.length); gensec_ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_NTLM2; torture_assert_ntstatus_ok(tctx, ntlmssp_sign_init(gensec_ntlmssp_state), "Failed to sign_init"); data = strhex_to_data_blob(tctx, "6a43494653"); gensec_ntlmssp_sign_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig); expected_sig = strhex_to_data_blob(tctx, "01000000e37f97f2544f4d7e00000000"); dump_data_pw("NTLMSSP calc sig: ", sig.data, sig.length); dump_data_pw("NTLMSSP expected sig: ", expected_sig.data, expected_sig.length); torture_assert_int_equal(tctx, sig.length, expected_sig.length, "Wrong sig length"); torture_assert_mem_equal(tctx, sig.data, expected_sig.data, sig.length, "data mismatch"); torture_assert_ntstatus_equal(tctx, gensec_ntlmssp_check_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig), NT_STATUS_ACCESS_DENIED, "Check of just signed packet (should fail, wrong end)"); gensec_ntlmssp_state->session_key = data_blob(NULL, 0); torture_assert_ntstatus_equal(tctx, gensec_ntlmssp_check_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig), NT_STATUS_NO_USER_SESSION_KEY, "Check of just signed packet without a session key should fail"); talloc_free(gensec_security); torture_assert_ntstatus_ok(tctx, gensec_client_start(mem_ctx, &gensec_security, tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx)), "Failed to start GENSEC for NTLMSSP"); gensec_set_credentials(gensec_security, cmdline_credentials); gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); torture_assert_ntstatus_ok(tctx, gensec_start_mech_by_oid(gensec_security, GENSEC_OID_NTLMSSP), "GENSEC start mech by oid"); gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data; gensec_ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405e538b0"); dump_data_pw("NTLMSSP session key: \n", gensec_ntlmssp_state->session_key.data, gensec_ntlmssp_state->session_key.length); gensec_ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_KEY_EXCH; torture_assert_ntstatus_ok(tctx, ntlmssp_sign_init(gensec_ntlmssp_state), "Failed to sign_init"); data = strhex_to_data_blob(tctx, "6a43494653"); gensec_ntlmssp_sign_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig); expected_sig = strhex_to_data_blob(tctx, "0100000078010900397420fe0e5a0f89"); dump_data_pw("NTLMSSP calc sig: ", sig.data, sig.length); dump_data_pw("NTLMSSP expected sig: ", expected_sig.data, expected_sig.length); torture_assert_int_equal(tctx, sig.length, expected_sig.length, "Wrong sig length"); torture_assert_mem_equal(tctx, sig.data+8, expected_sig.data+8, sig.length-8, "data mismatch"); torture_assert_ntstatus_equal(tctx, gensec_ntlmssp_check_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig), NT_STATUS_ACCESS_DENIED, "Check of just signed packet (should fail, wrong end)"); sig.length /= 2; torture_assert_ntstatus_equal(tctx, gensec_ntlmssp_check_packet(gensec_security, gensec_security, data.data, data.length, data.data, data.length, &sig), NT_STATUS_ACCESS_DENIED, "Check of just signed packet with short sig"); talloc_free(gensec_security); return true; }
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_error_code ret = 0; NTSTATUS nt_status; switch (gensec_krb5_state->state_position) { case GENSEC_KRB5_CLIENT_START: { DATA_BLOB unwrapped_out; nt_status = gensec_krb5_common_client_creds(gensec_security, ev, gensec_krb5_state->gssapi); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (gensec_krb5_state->gssapi) { unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); /* wrap that up in a nice GSS-API wrapping */ *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ); } else { *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); } if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) { gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_krb5_state->state_position = GENSEC_KRB5_DONE; nt_status = NT_STATUS_OK; } return nt_status; } case GENSEC_KRB5_CLIENT_MUTUAL_AUTH: { DATA_BLOB unwrapped_in; krb5_data inbuf; krb5_ap_rep_enc_part *repl = NULL; uint8_t tok_id[2]; if (gensec_krb5_state->gssapi) { if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n")); dump_data_pw("Mutual authentication message:\n", in.data, in.length); return NT_STATUS_INVALID_PARAMETER; } } else { unwrapped_in = in; } /* TODO: check the tok_id */ inbuf.data = unwrapped_in.data; inbuf.length = unwrapped_in.length; ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, &inbuf, &repl); if (ret) { DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx))); dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length); nt_status = NT_STATUS_ACCESS_DENIED; } else { *out = data_blob(NULL, 0); nt_status = NT_STATUS_OK; gensec_krb5_state->state_position = GENSEC_KRB5_DONE; } if (repl) { krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl); } return nt_status; } case GENSEC_KRB5_SERVER_START: { DATA_BLOB unwrapped_in; DATA_BLOB unwrapped_out = data_blob(NULL, 0); krb5_data inbuf, outbuf; uint8_t tok_id[2]; struct keytab_container *keytab; krb5_principal server_in_keytab; const char *error_string; enum credentials_obtained obtained; if (!in.data) { return NT_STATUS_INVALID_PARAMETER; } /* Grab the keytab, however generated */ ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), gensec_security->settings->lp_ctx, &keytab); if (ret) { return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } /* This ensures we lookup the correct entry in that keytab */ ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), gensec_krb5_state->smb_krb5_context, &server_in_keytab, &obtained, &error_string); if (ret) { DEBUG(2,("Failed to make credentials from principal: %s\n", error_string)); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ if (gensec_krb5_state->gssapi && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { inbuf.data = unwrapped_in.data; inbuf.length = unwrapped_in.length; } else { inbuf.data = in.data; inbuf.length = in.length; } ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, &inbuf, keytab->keytab, server_in_keytab, &outbuf, &gensec_krb5_state->ticket, &gensec_krb5_state->keyblock); if (ret) { return NT_STATUS_LOGON_FAILURE; } unwrapped_out.data = (uint8_t *)outbuf.data; unwrapped_out.length = outbuf.length; gensec_krb5_state->state_position = GENSEC_KRB5_DONE; /* wrap that up in a nice GSS-API wrapping */ if (gensec_krb5_state->gssapi) { *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP); } else { *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length); } krb5_data_free(&outbuf); return NT_STATUS_OK; } case GENSEC_KRB5_DONE: default: /* Asking too many times... */ return NT_STATUS_INVALID_PARAMETER; } }
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, const char *realm, time_t time_offset, const DATA_BLOB *ticket, char **principal, struct PAC_DATA **pac_data, DATA_BLOB *ap_rep, DATA_BLOB *session_key, bool use_replay_cache) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; NTSTATUS pac_ret; DATA_BLOB auth_data; krb5_context context = NULL; krb5_auth_context auth_context = NULL; krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; krb5_keyblock *keyblock = NULL; time_t authtime; krb5_error_code ret = 0; int flags = 0; krb5_principal host_princ = NULL; krb5_const_principal client_principal = NULL; char *host_princ_s = NULL; bool auth_ok = False; bool got_auth_data = False; struct named_mutex *mutex = NULL; ZERO_STRUCT(packet); ZERO_STRUCT(auth_data); *principal = NULL; *pac_data = NULL; *ap_rep = data_blob_null; *session_key = data_blob_null; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret))); return NT_STATUS_LOGON_FAILURE; } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_set_default_realm(context, realm); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret))); goto out; } /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ ret = krb5_auth_con_init(context, &auth_context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret))); goto out; } krb5_auth_con_getflags( context, auth_context, &flags ); if ( !use_replay_cache ) { /* Disable default use of a replay cache */ flags &= ~KRB5_AUTH_CONTEXT_DO_TIME; krb5_auth_con_setflags( context, auth_context, flags ); } if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) { goto out; } strlower_m(host_princ_s); ret = smb_krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); goto out; } if ( use_replay_cache ) { /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 code surrounding the replay cache... */ mutex = grab_named_mutex(talloc_tos(), "replay cache mutex", 10); if (mutex == NULL) { DEBUG(1,("ads_verify_ticket: unable to protect " "replay cache with mutex.\n")); ret = KRB5_CC_IO; goto out; } /* JRA. We must set the rcache here. This will prevent replay attacks. */ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache " "failed (%s)\n", error_message(ret))); goto out; } ret = krb5_auth_con_setrcache(context, auth_context, rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache " "failed (%s)\n", error_message(ret))); goto out; } } /* Try secrets.tdb first and fallback to the krb5.keytab if necessary */ auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, ticket, &tkt, &keyblock, &ret); if (!auth_ok && (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW)) { goto auth_failed; } if (!auth_ok && lp_use_kerberos_keytab()) { auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &tkt, &keyblock, &ret); } if ( use_replay_cache ) { TALLOC_FREE(mutex); #if 0 /* Heimdal leaks here, if we fix the leak, MIT crashes */ if (rcache) { krb5_rc_close(context, rcache); } #endif } auth_failed: if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); /* Try map the error return in case it's something like * a clock skew error. */ sret = krb5_to_nt_status(ret); if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) { sret = NT_STATUS_LOGON_FAILURE; } DEBUG(10,("ads_verify_ticket: returning error %s\n", nt_errstr(sret) )); goto out; } authtime = get_authtime_from_tkt(tkt); client_principal = get_principal_from_tkt(tkt); ret = krb5_mk_rep(context, auth_context, &packet); if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", error_message(ret))); goto out; } *ap_rep = data_blob(packet.data, packet.length); if (packet.data) { kerberos_free_data_contents(context, &packet); ZERO_STRUCT(packet); } get_krb5_smb_session_key(context, auth_context, session_key, True); dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length); #if 0 file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif /* continue when no PAC is retrieved or we couldn't decode the PAC (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or Kerberos tickets encrypted using a DES key) - Guenther */ got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt); if (!got_auth_data) { DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n")); } if (got_auth_data) { pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data); if (!NT_STATUS_IS_OK(pac_ret)) { DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret))); *pac_data = NULL; } data_blob_free(&auth_data); } #if 0 #if defined(HAVE_KRB5_TKT_ENC_PART2) /* MIT */ if (tkt->enc_part2) { file_save("/tmp/authdata.dat", tkt->enc_part2->authorization_data[0]->contents, tkt->enc_part2->authorization_data[0]->length); } #else /* Heimdal */ if (tkt->ticket.authorization_data) { file_save("/tmp/authdata.dat", tkt->ticket.authorization_data->val->ad_data.data, tkt->ticket.authorization_data->val->ad_data.length); } #endif #endif if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) { DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); sret = NT_STATUS_LOGON_FAILURE; goto out; } sret = NT_STATUS_OK; out: TALLOC_FREE(mutex); if (!NT_STATUS_IS_OK(sret)) { data_blob_free(&auth_data); } if (!NT_STATUS_IS_OK(sret)) { data_blob_free(ap_rep); } if (host_princ) { krb5_free_principal(context, host_princ); } if (keyblock) { krb5_free_keyblock(context, keyblock); } if (tkt != NULL) { krb5_free_ticket(context, tkt); } SAFE_FREE(host_princ_s); if (auth_context) { krb5_auth_con_free(context, auth_context); } if (context) { krb5_free_context(context); } return sret; }
NTSTATUS gssapi_unseal_packet(gss_ctx_id_t gssapi_context, const gss_OID mech, bool hdr_signing, uint8_t *data, size_t length, const uint8_t *whole_pdu, size_t pdu_length, const DATA_BLOB *sig) { OM_uint32 maj_stat, min_stat; gss_iov_buffer_desc iov[4]; gss_qop_t qop_state; int sealed = 0; const uint8_t *pre_sign_ptr = NULL; size_t pre_sign_len = 0; const uint8_t *post_sign_ptr = NULL; size_t post_sign_len = 0; if (hdr_signing) { const uint8_t *de = data + length; const uint8_t *we = whole_pdu + pdu_length; if (data < whole_pdu) { return NT_STATUS_INVALID_PARAMETER; } if (de > we) { return NT_STATUS_INVALID_PARAMETER; } pre_sign_len = data - whole_pdu; if (pre_sign_len > 0) { pre_sign_ptr = whole_pdu; } post_sign_len = we - de; if (post_sign_len > 0) { post_sign_ptr = de; } } dump_data_pw("gssapi_unseal_packet: sig\n", sig->data, sig->length); dump_data_pw("gssapi_unseal_packet: sealed\n", data, length); iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; iov[0].buffer.length = sig->length; iov[0].buffer.value = sig->data; if (pre_sign_ptr != NULL) { iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[1].buffer.length = pre_sign_len; iov[1].buffer.value = discard_const(pre_sign_ptr); } else { iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY; iov[1].buffer.length = 0; iov[1].buffer.value = NULL; } /* data is encrypted in place, which is ok */ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; iov[2].buffer.length = length; iov[2].buffer.value = data; if (post_sign_ptr != NULL) { iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[3].buffer.length = post_sign_len; iov[3].buffer.value = discard_const(post_sign_ptr); } else { iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY; iov[3].buffer.length = 0; iov[3].buffer.value = NULL; } maj_stat = gss_unwrap_iov(&min_stat, gssapi_context, &sealed, &qop_state, iov, ARRAY_SIZE(iov)); if (GSS_ERROR(maj_stat)) { char *error_string = gssapi_error_string(NULL, maj_stat, min_stat, mech); DEBUG(1, ("gss_unwrap_iov failed: %s\n", error_string)); talloc_free(error_string); return NT_STATUS_ACCESS_DENIED; } if (sealed == 0) { DEBUG(0, ("gss_unwrap_iov says data was not sealed!\n")); return NT_STATUS_ACCESS_DENIED; } DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n", (int)iov[2].buffer.length, (int)iov[0].buffer.length)); return NT_STATUS_OK; }