void aes_ccm_128_init(struct aes_ccm_128_context *ctx, const uint8_t K[AES_BLOCK_SIZE], const uint8_t N[AES_CCM_128_NONCE_SIZE], size_t a_total, size_t m_total) { uint8_t B_0[AES_BLOCK_SIZE]; ZERO_STRUCTP(ctx); AES_set_encrypt_key(K, 128, &ctx->aes_key); memcpy(ctx->nonce, N, AES_CCM_128_NONCE_SIZE); ctx->a_remain = a_total; ctx->m_remain = m_total; /* * prepare B_0 */ B_0[0] = L_; B_0[0] += 8 * M_; if (a_total > 0) { B_0[0] += 64; } memcpy(&B_0[1], ctx->nonce, AES_CCM_128_NONCE_SIZE); RSIVAL(B_0, (AES_BLOCK_SIZE - AES_CCM_128_L), m_total); /* * prepare X_1 */ AES_encrypt(B_0, ctx->X_i, &ctx->aes_key); /* * prepare B_1 */ if (a_total >= UINT32_MAX) { RSSVAL(ctx->B_i, 0, 0xFFFF); RSBVAL(ctx->B_i, 2, (uint64_t)a_total); ctx->B_i_ofs = 10; } else if (a_total >= 0xFF00) { RSSVAL(ctx->B_i, 0, 0xFFFE); RSIVAL(ctx->B_i, 2, a_total); ctx->B_i_ofs = 6; } else if (a_total > 0) { RSSVAL(ctx->B_i, 0, a_total); ctx->B_i_ofs = 2; } ctx->S_i_ofs = AES_BLOCK_SIZE; }
bool prs_dcerpc_status(const char *name, prs_struct *ps, int depth, NTSTATUS *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = NT_STATUS(RIVAL(q,0)); else *status = NT_STATUS(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,NT_STATUS_V(*status)); else SIVAL(q,0,NT_STATUS_V(*status)); } DEBUGADD(5,("%s%04x %s: %s\n", tab_depth(5,depth), ps->data_offset, name, dcerpc_errstr(talloc_tos(), NT_STATUS_V(*status)))); ps->data_offset += sizeof(uint32); return True; }
int buffer_add_uint32(xrtp_buffer_t * buf, uint32 dword){ if(buf->len_data + sizeof(uint32) > buf->len) { buffer_log(("buffer_add_uint32: buffer.len(%d) full! Can't add (%d)", buf->len, dword)); return OS_EREFUSE; } if(buf->byte_order == BIGEND_ORDER) { buffer_log(("buffer_add_uint32: Add BE(%u) to buf[%d]@%d\n", dword, buf->pos, (int)(buf->data))); RSIVAL(buf->data, buf->len_data, dword); } else { buffer_log(("buffer_add_uint32: Add LE(%u) to buf[%d]@%d\n", dword, buf->pos, (int)(buf->data))); SIVAL(buf->data, buf->len_data, dword); } buf->len_data += sizeof(uint32); buf->pos += sizeof(uint32); return OS_OK; }
/* 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; }
BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = NT_STATUS(RIVAL(q,0)); else *status = NT_STATUS(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,NT_STATUS_V(*status)); else SIVAL(q,0,NT_STATUS_V(*status)); } DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, get_nt_error_msg(*status))); ps->data_offset += sizeof(uint32); return True; }
BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = W_ERROR(RIVAL(q,0)); else *status = W_ERROR(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,W_ERROR_V(*status)); else SIVAL(q,0,W_ERROR_V(*status)); } DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, dos_errstr(*status))); ps->data_offset += sizeof(uint32); return True; }
/* * These functions are for use in the deprecated * gensec_socket code (public because SPNEGO must * use them for recursion) */ _PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out, size_t *len_processed) { if (!gensec_security->ops->wrap_packets) { NTSTATUS nt_status; size_t max_input_size; DATA_BLOB unwrapped, wrapped; max_input_size = gensec_max_input_size(gensec_security); unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length)); nt_status = gensec_wrap(gensec_security, mem_ctx, &unwrapped, &wrapped); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } *out = data_blob_talloc(mem_ctx, NULL, 4); if (!out->data) { return NT_STATUS_NO_MEMORY; } RSIVAL(out->data, 0, wrapped.length); if (!data_blob_append(mem_ctx, out, wrapped.data, wrapped.length)) { return NT_STATUS_NO_MEMORY; } *len_processed = unwrapped.length; return NT_STATUS_OK; } return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out, len_processed); }
/* this function is due to be replaced */ void initrpcreply(char *inbuf, char *q) { uint32 callid; SCVAL(q, 0, 5); q++; /* RPC version 5 */ SCVAL(q, 0, 0); q++; /* minor version 0 */ SCVAL(q, 0, 2); q++; /* RPC response packet */ SCVAL(q, 0, 3); q++; /* first frag + last frag */ RSIVAL(q, 0, 0x10000000); q += 4; /* packed data representation */ RSSVAL(q, 0, 0); q += 2; /* fragment length, fill in later */ SSVAL(q, 0, 0); q += 2; /* authentication length */ callid = RIVAL(inbuf, 12); RSIVAL(q, 0, callid); q += 4; /* call identifier - match incoming RPC */ SIVAL(q, 0, 0x18); q += 4; /* allocation hint (no idea) */ SSVAL(q, 0, 0); q += 2; /* presentation context identifier */ SCVAL(q, 0, 0); q++; /* cancel count */ SCVAL(q, 0, 0); q++; /* reserved */ }
static TDB_DATA smbXsrv_session_local_id_to_key(uint32_t id, uint8_t *key_buf) { TDB_DATA key; RSIVAL(key_buf, 0, id); key = make_tdb_data(key_buf, SMBXSRV_SESSION_LOCAL_TDB_KEY_SIZE); return key; }
static TDB_DATA smbXsrv_tcon_global_id_to_key(uint32_t id, uint8_t *key_buf) { TDB_DATA key; RSIVAL(key_buf, 0, id); key = make_tdb_data(key_buf, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE); return key; }
static void aes_ccm_128_S_i(struct aes_ccm_128_context *ctx, uint8_t S_i[AES_BLOCK_SIZE], size_t i) { uint8_t A_i[AES_BLOCK_SIZE]; A_i[0] = L_; memcpy(&A_i[1], ctx->nonce, AES_CCM_128_NONCE_SIZE); RSIVAL(A_i, (AES_BLOCK_SIZE - AES_CCM_128_L), i); AES_encrypt(A_i, S_i, &ctx->aes_key); }
/* 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; }
/* receive a full packet on a KDC connection */ static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob) { struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data, struct kdc_tcp_connection); NTSTATUS status = NT_STATUS_UNSUCCESSFUL; TALLOC_CTX *tmp_ctx = talloc_new(kdcconn); int ret; DATA_BLOB input, reply; talloc_steal(tmp_ctx, blob.data); /* Call krb5 */ input = data_blob_const(blob.data + 4, blob.length - 4); ret = kdcconn->kdc_socket->process(kdcconn->kdc_socket->kdc, tmp_ctx, &input, &reply, kdcconn->remote_address, kdcconn->local_address, 0 /* Not datagram */); if (!ret) { talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_ERROR; } /* and now encode the reply */ blob = data_blob_talloc(kdcconn, NULL, reply.length + 4); if (!blob.data) { talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } RSIVAL(blob.data, 0, reply.length); memcpy(blob.data + 4, reply.data, reply.length); status = packet_send(kdcconn->packet, blob); if (!NT_STATUS_IS_OK(status)) { talloc_free(tmp_ctx); return status; } /* the call isn't needed any more */ talloc_free(tmp_ctx); return NT_STATUS_OK; }
/* 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; }
BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len) { int i; char *q = prs_mem_get(ps, len * sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) { for (i = 0; i < len; i++) data32s[i] = RIVAL(q, 4*i); } else { for (i = 0; i < len; i++) data32s[i] = IVAL(q, 4*i); } } else { if (ps->bigendian_data) { for (i = 0; i < len; i++) RSIVAL(q, 4*i, data32s[i]); } else { for (i = 0; i < len; i++) SIVAL(q, 4*i, data32s[i]); } } DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name)); if (charmode) print_asc(5, (unsigned char*)data32s, 4*len); else { for (i = 0; i < len; i++) DEBUG(5,("%08x ", data32s[i])); } DEBUG(5,("\n")); ps->data_offset += (len * sizeof(uint32)); return True; }
BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *data32 = RIVAL(q,0); else *data32 = IVAL(q,0); } else { if (ps->bigendian_data) RSIVAL(q,0,*data32); else SIVAL(q,0,*data32); } DEBUG(5,("%s%04x %s: %08x\n", tab_depth(depth), ps->data_offset, name, *data32)); ps->data_offset += sizeof(uint32); return True; }
NTSTATUS netsec_outgoing_packet(struct schannel_state *state, TALLOC_CTX *mem_ctx, bool do_seal, uint8_t *data, size_t length, DATA_BLOB *sig) { uint32_t min_sig_size = 0; uint32_t used_sig_size = 0; uint8_t header[8]; uint8_t checksum[32]; uint32_t checksum_length = sizeof(checksum_length); uint8_t _confounder[8]; uint8_t *confounder = NULL; uint32_t confounder_ofs = 0; uint8_t seq_num[8]; netsec_offset_and_sizes(state, do_seal, &min_sig_size, &used_sig_size, &checksum_length, &confounder_ofs); RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0x80:0); if (do_seal) { confounder = _confounder; generate_random_buffer(confounder, 8); } else { confounder = NULL; } netsec_do_sign(state, confounder, data, length, header, checksum); if (do_seal) { netsec_do_seal(state, seq_num, confounder, data, length, true); } netsec_do_seq_num(state, checksum, checksum_length, seq_num); (*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size); memcpy(sig->data, header, 8); memcpy(sig->data+8, seq_num, 8); memcpy(sig->data+16, checksum, checksum_length); if (confounder) { memcpy(sig->data+confounder_ofs, 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, checksum_length); dump_data_pw("confound :", sig->data+confounder_ofs, 8); return NT_STATUS_OK; }
static krb5_error_code build_kpasswd_request(uint16 pversion, krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, const char *princ, const char *passwd, bool use_tcp, krb5_data *packet) { krb5_error_code ret; krb5_data cipherpw; krb5_data encoded_setpw; krb5_replay_data replay; char *p, *msg_start; DATA_BLOB setpw; unsigned int msg_length; ret = krb5_auth_con_setflags(context, auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { DEBUG(1,("krb5_auth_con_setflags failed (%s)\n", error_message(ret))); return ret; } /* handle protocol differences in chpw and setpw */ if (pversion == KRB5_KPASSWD_VERS_CHANGEPW) setpw = data_blob(passwd, strlen(passwd)); else if (pversion == KRB5_KPASSWD_VERS_SETPW || pversion == KRB5_KPASSWD_VERS_SETPW_ALT) setpw = encode_krb5_setpw(princ, passwd); else return EINVAL; if (setpw.data == NULL || setpw.length == 0) { return EINVAL; } encoded_setpw.data = (char *)setpw.data; encoded_setpw.length = setpw.length; ret = krb5_mk_priv(context, auth_context, &encoded_setpw, &cipherpw, &replay); data_blob_free(&setpw); /*from 'encode_krb5_setpw(...)' */ if (ret) { DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret))); return ret; } packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 )); if (!packet->data) return -1; /* see the RFC for details */ msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0); p += 2; RSSVAL(p, 0, pversion); p += 2; RSSVAL(p, 0, ap_req->length); p += 2; memcpy(p, ap_req->data, ap_req->length); p += ap_req->length; memcpy(p, cipherpw.data, cipherpw.length); p += cipherpw.length; packet->length = PTR_DIFF(p,packet->data); msg_length = PTR_DIFF(p,msg_start); if (use_tcp) { RSIVAL(packet->data, 0, msg_length); } RSSVAL(msg_start, 0, msg_length); free(cipherpw.data); /* from 'krb5_mk_priv(...)' */ return 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; }
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); }
static void kdc_tcp_call_loop(struct tevent_req *subreq) { struct kdc_tcp_connection *kdc_conn = tevent_req_callback_data(subreq, struct kdc_tcp_connection); struct kdc_tcp_call *call; NTSTATUS status; enum kdc_process_ret ret; call = talloc(kdc_conn, struct kdc_tcp_call); if (call == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " "no memory for kdc_tcp_call"); return; } call->kdc_conn = kdc_conn; status = tstream_read_pdu_blob_recv(subreq, call, &call->in); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { const char *reason; reason = talloc_asprintf(call, "kdc_tcp_call_loop: " "tstream_read_pdu_blob_recv() - %s", nt_errstr(status)); if (!reason) { reason = nt_errstr(status); } kdc_tcp_terminate_connection(kdc_conn, reason); return; } DEBUG(10,("Received krb5 TCP packet of length %lu from %s\n", (long) call->in.length, tsocket_address_string(kdc_conn->conn->remote_address, call))); /* skip length header */ call->in.data +=4; call->in.length -= 4; /* Call krb5 */ ret = kdc_conn->kdc_socket->process(kdc_conn->kdc_socket->kdc, call, &call->in, &call->out, kdc_conn->conn->remote_address, kdc_conn->conn->local_address, 0 /* Stream */); if (ret == KDC_PROCESS_FAILED) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: process function failed"); return; } if (ret == KDC_PROCESS_PROXY) { uint16_t port; if (!kdc_conn->kdc_socket->kdc->am_rodc) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: proxying requested when not RODC"); return; } port = tsocket_address_inet_port(kdc_conn->conn->local_address); subreq = kdc_tcp_proxy_send(call, kdc_conn->conn->event.ctx, kdc_conn->kdc_socket->kdc, port, call->in); if (subreq == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: kdc_tcp_proxy_send failed"); return; } tevent_req_set_callback(subreq, kdc_tcp_call_proxy_done, call); return; } /* First add the length of the out buffer */ RSIVAL(call->out_hdr, 0, call->out.length); call->out_iov[0].iov_base = (char *) call->out_hdr; call->out_iov[0].iov_len = 4; call->out_iov[1].iov_base = (char *) call->out.data; call->out_iov[1].iov_len = call->out.length; subreq = tstream_writev_queue_send(call, kdc_conn->conn->event.ctx, kdc_conn->tstream, kdc_conn->send_queue, call->out_iov, 2); if (subreq == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " "no memory for tstream_writev_queue_send"); return; } tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call); /* * The krb5 tcp pdu's has the length as 4 byte (initial_read_size), * packet_full_request_u32 provides the pdu length then. */ subreq = tstream_read_pdu_blob_send(kdc_conn, kdc_conn->conn->event.ctx, kdc_conn->tstream, 4, /* initial_read_size */ packet_full_request_u32, kdc_conn); if (subreq == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " "no memory for tstream_read_pdu_blob_send"); return; } tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn); }
static void kdc_tcp_call_proxy_done(struct tevent_req *subreq) { struct kdc_tcp_call *call = tevent_req_callback_data(subreq, struct kdc_tcp_call); struct kdc_tcp_connection *kdc_conn = call->kdc_conn; NTSTATUS status; status = kdc_tcp_proxy_recv(subreq, call, &call->out); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { /* generate an error packet */ status = kdc_proxy_unavailable_error(kdc_conn->kdc_socket->kdc, call, &call->out); } if (!NT_STATUS_IS_OK(status)) { const char *reason; reason = talloc_asprintf(call, "kdc_tcp_call_proxy_done: " "kdc_proxy_unavailable_error - %s", nt_errstr(status)); if (!reason) { reason = "kdc_tcp_call_proxy_done: kdc_proxy_unavailable_error() failed"; } kdc_tcp_terminate_connection(call->kdc_conn, reason); return; } /* First add the length of the out buffer */ RSIVAL(call->out_hdr, 0, call->out.length); call->out_iov[0].iov_base = (char *) call->out_hdr; call->out_iov[0].iov_len = 4; call->out_iov[1].iov_base = (char *) call->out.data; call->out_iov[1].iov_len = call->out.length; subreq = tstream_writev_queue_send(call, kdc_conn->conn->event.ctx, kdc_conn->tstream, kdc_conn->send_queue, call->out_iov, 2); if (subreq == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " "no memory for tstream_writev_queue_send"); return; } tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call); /* * The krb5 tcp pdu's has the length as 4 byte (initial_read_size), * packet_full_request_u32 provides the pdu length then. */ subreq = tstream_read_pdu_blob_send(kdc_conn, kdc_conn->conn->event.ctx, kdc_conn->tstream, 4, /* initial_read_size */ packet_full_request_u32, kdc_conn); if (subreq == NULL) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " "no memory for tstream_read_pdu_blob_send"); return; } tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn); }
static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; OM_uint32 maj_stat, min_stat; OM_uint32 min_stat2; gss_buffer_desc input_token = { 0, NULL }; gss_buffer_desc output_token = { 0, NULL }; gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; struct timeval tv; time_req = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "requested_life_time", time_req); input_token.length = in.length; input_token.value = in.data; switch (gensec_gssapi_state->sasl_state) { case STAGE_GSS_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { #ifdef SAMBA4_USES_HEIMDAL struct gsskrb5_send_to_kdc send_to_kdc; krb5_error_code ret; #endif nt_status = gensec_gssapi_client_creds(gensec_security, ev); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = ev; min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc); if (min_stat) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif maj_stat = gss_init_sec_context(&min_stat, gensec_gssapi_state->client_cred->creds, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_name, gensec_gssapi_state->gss_oid, gensec_gssapi_state->gss_want_flags, time_req, gensec_gssapi_state->input_chan_bindings, &input_token, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, /* ret flags */ &time_rec); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = NULL; ret = gsskrb5_set_send_to_kdc(&send_to_kdc); if (ret) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif break; } case GENSEC_SERVER: { maj_stat = gss_accept_sec_context(&min_stat, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_cred->creds, &input_token, gensec_gssapi_state->input_chan_bindings, &gensec_gssapi_state->client_name, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, &time_rec, &gensec_gssapi_state->delegated_cred_handle); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } break; } default: return NT_STATUS_INVALID_PARAMETER; } gensec_gssapi_state->gss_exchange_count++; if (maj_stat == GSS_S_COMPLETE) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { DEBUG(5, ("gensec_gssapi: credentials were delegated\n")); } else { DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n")); } tv = timeval_current_ofs(time_rec, 0); gensec_gssapi_state->expire_time = timeval_to_nttime(&tv); /* We may have been invoked as SASL, so there * is more work to do */ if (gensec_gssapi_state->sasl) { gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG; return NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n")); } else { DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n")); } return NT_STATUS_OK; } } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); return NT_STATUS_MORE_PROCESSING_REQUIRED; } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gss_cred_id_t creds = NULL; gss_name_t name; gss_buffer_desc buffer; OM_uint32 lifetime = 0; gss_cred_usage_t usage; const char *role = NULL; DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n", role, gensec_gssapi_state->gss_exchange_count)); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: creds = gensec_gssapi_state->client_cred->creds; role = "client"; break; case GENSEC_SERVER: creds = gensec_gssapi_state->server_cred->creds; role = "server"; break; } maj_stat = gss_inquire_cred(&min_stat, creds, &name, &lifetime, &usage, NULL); if (maj_stat == GSS_S_COMPLETE) { const char *usage_string = NULL; switch (usage) { case GSS_C_BOTH: usage_string = "GSS_C_BOTH"; break; case GSS_C_ACCEPT: usage_string = "GSS_C_ACCEPT"; break; case GSS_C_INITIATE: usage_string = "GSS_C_INITIATE"; break; } maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); if (maj_stat) { buffer.value = NULL; buffer.length = 0; } if (lifetime > 0) { DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, lifetime, usage_string)); } else { DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, usage_string)); } gss_release_buffer(&min_stat, &buffer); gss_release_name(&min_stat, &name); } else if (maj_stat != GSS_S_COMPLETE) { DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); } return NT_STATUS_INVALID_PARAMETER; } else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) { switch (min_stat) { case KRB5KRB_AP_ERR_TKT_NYV: DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_TKT_EXPIRED: DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server %s is not registered with our KDC: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_MSG_TYPE: /* garbage input, possibly from the auto-mech detection */ return NT_STATUS_INVALID_PARAMETER; default: DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(1, ("GSS %s Update(%d) failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } break; } /* These last two stages are only done if we were invoked as SASL */ case STAGE_SASL_SSF_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { uint8_t maxlength_proposed[4]; uint8_t maxlength_accepted[4]; uint8_t security_supported; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; /* As a client, we have just send a * zero-length blob to the server (after the * normal GSSAPI exchange), and it has replied * with it's SASL negotiation */ maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_proposed, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_supported = maxlength_proposed[0]; maxlength_proposed[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_supported & NEG_SEAL) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { gensec_gssapi_state->sasl_protection |= NEG_SEAL; } } if (security_supported & NEG_SIGN) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { gensec_gssapi_state->sasl_protection |= NEG_SIGN; } } if (security_supported & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } if (gensec_gssapi_state->sasl_protection == 0) { DEBUG(1, ("Remote server does not support unprotected connections\n")); return NT_STATUS_ACCESS_DENIED; } /* Send back the negotiated max length */ RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; input_token.value = maxlength_accepted; input_token.length = sizeof(maxlength_accepted); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n")); } else { DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n")); } return NT_STATUS_OK; } case GENSEC_SERVER: { uint8_t maxlength_proposed[4]; uint8_t security_supported = 0x0; int conf_state; /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ if (in.length != 0) { DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); } /* Give the client some idea what we will support */ RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); /* first byte is the proposed security */ maxlength_proposed[0] = '\0'; gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { security_supported |= NEG_SEAL; } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { security_supported |= NEG_SIGN; } if (security_supported == 0) { /* If we don't support anything, this must be 0 */ RSIVAL(maxlength_proposed, 0, 0x0); } /* TODO: We may not wish to support this */ security_supported |= NEG_NONE; maxlength_proposed[0] = security_supported; input_token.value = maxlength_proposed; input_token.length = sizeof(maxlength_proposed); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT; return NT_STATUS_MORE_PROCESSING_REQUIRED; } default: return NT_STATUS_INVALID_PARAMETER; } } /* This is s server-only stage */ case STAGE_SASL_SSF_ACCEPT: { uint8_t maxlength_accepted[4]; uint8_t security_accepted; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_accepted, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_accepted = maxlength_accepted[0]; maxlength_accepted[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_accepted & NEG_SEAL) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(1, ("Remote client wanted seal, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SEAL; } if (security_accepted & NEG_SIGN) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(1, ("Remote client wanted sign, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SIGN; } if (security_accepted & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n")); } else { DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n")); } *out = data_blob(NULL, 0); return NT_STATUS_OK; } default: return NT_STATUS_INVALID_PARAMETER; } }
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, void *data, krb5_krbhst_info *hi, time_t timeout, const krb5_data *send_buf, krb5_data *recv_buf) { krb5_error_code ret; NTSTATUS status; const char *name; struct addrinfo *ai, *a; struct smb_krb5_socket *smb_krb5; DATA_BLOB send_blob; struct tevent_context *ev; TALLOC_CTX *tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } if (!data) { /* If no event context was available, then create one for this loop */ ev = samba_tevent_context_init(tmp_ctx); if (!ev) { talloc_free(tmp_ctx); return ENOMEM; } } else { ev = talloc_get_type_abort(data, struct tevent_context); } send_blob = data_blob_const(send_buf->data, send_buf->length); ret = krb5_krbhst_get_addrinfo(context, hi, &ai); if (ret) { talloc_free(tmp_ctx); return ret; } for (a = ai; a; a = a->ai_next) { struct socket_address *remote_addr; smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket); if (!smb_krb5) { talloc_free(tmp_ctx); return ENOMEM; } smb_krb5->hi = hi; switch (a->ai_family) { case PF_INET: name = "ipv4"; break; #ifdef HAVE_IPV6 case PF_INET6: name = "ipv6"; break; #endif default: talloc_free(tmp_ctx); return EINVAL; } status = NT_STATUS_INVALID_PARAMETER; switch (hi->proto) { case KRB5_KRBHST_UDP: status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_TCP: status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_HTTP: talloc_free(tmp_ctx); return EINVAL; } if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_steal(smb_krb5, smb_krb5->sock); remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen); if (!remote_addr) { talloc_free(smb_krb5); continue; } status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev); if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } /* Setup the FDE, start listening for read events * from the start (otherwise we may miss a socket * drop) and mark as AUTOCLOSE along with the fde */ /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock, socket_get_fd(smb_krb5->sock), TEVENT_FD_READ, smb_krb5_socket_handler, smb_krb5); /* its now the job of the event layer to close the socket */ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn); socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE); tevent_add_timer(ev, smb_krb5, timeval_current_ofs(timeout, 0), smb_krb5_request_timeout, smb_krb5); smb_krb5->status = NT_STATUS_OK; smb_krb5->reply = data_blob(NULL, 0); switch (hi->proto) { case KRB5_KRBHST_UDP: TEVENT_FD_WRITEABLE(smb_krb5->fde); smb_krb5->request = send_blob; break; case KRB5_KRBHST_TCP: smb_krb5->packet = packet_init(smb_krb5); if (smb_krb5->packet == NULL) { talloc_free(smb_krb5); return ENOMEM; } packet_set_private(smb_krb5->packet, smb_krb5); packet_set_socket(smb_krb5->packet, smb_krb5->sock); packet_set_callback(smb_krb5->packet, smb_krb5_full_packet); packet_set_full_request(smb_krb5->packet, packet_full_request_u32); packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler); packet_set_event_context(smb_krb5->packet, ev); packet_set_fde(smb_krb5->packet, smb_krb5->fde); smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4); RSIVAL(smb_krb5->request.data, 0, send_blob.length); memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length); packet_send(smb_krb5->packet, smb_krb5->request); break; case KRB5_KRBHST_HTTP: talloc_free(tmp_ctx); return EINVAL; } while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { if (tevent_loop_once(ev) != 0) { talloc_free(tmp_ctx); return EINVAL; } /* After each and every event loop, reset the * send_to_kdc pointers to what they were when * we entered this loop. That way, if a * nested event has invalidated them, we put * it back before we return to the heimdal * code */ ret = krb5_set_send_to_kdc_func(context, smb_krb5_send_and_recv_func, data); if (ret != 0) { talloc_free(tmp_ctx); return ret; } } if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) { talloc_free(smb_krb5); continue; } if (!NT_STATUS_IS_OK(smb_krb5->status)) { struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr); const char *addr_string = NULL; if (addr) { addr_string = tsocket_address_inet_addr_string(addr, smb_krb5); } else { addr_string = NULL; } DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status), addr_string)); talloc_free(smb_krb5); continue; } ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length); if (ret) { talloc_free(tmp_ctx); return ret; } talloc_free(smb_krb5); break; } talloc_free(tmp_ctx); if (a) { return 0; } return KRB5_KDC_UNREACH; }
NTSTATUS netsec_incoming_packet(struct schannel_state *state, bool do_unseal, uint8_t *data, size_t length, const DATA_BLOB *sig) { uint32_t min_sig_size = 0; uint8_t header[8]; uint8_t checksum[32]; uint32_t checksum_length = sizeof(checksum_length); uint8_t _confounder[8]; uint8_t *confounder = NULL; uint32_t confounder_ofs = 0; uint8_t seq_num[8]; int ret; netsec_offset_and_sizes(state, do_unseal, &min_sig_size, NULL, &checksum_length, &confounder_ofs); if (sig->length < min_sig_size) { return NT_STATUS_ACCESS_DENIED; } if (do_unseal) { confounder = _confounder; memcpy(confounder, sig->data+confounder_ofs, 8); } else { confounder = NULL; } RSIVAL(seq_num, 0, state->seq_num); SIVAL(seq_num, 4, state->initiator?0:0x80); if (do_unseal) { netsec_do_seal(state, seq_num, confounder, data, length, false); } netsec_do_sign(state, confounder, data, length, header, checksum); ret = memcmp(checksum, sig->data+16, checksum_length); if (ret != 0) { dump_data_pw("calc digest:", checksum, checksum_length); dump_data_pw("wire digest:", sig->data+16, checksum_length); return NT_STATUS_ACCESS_DENIED; } netsec_do_seq_num(state, checksum, checksum_length, seq_num); ret = memcmp(seq_num, sig->data+8, 8); if (ret != 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; }
/* this performs a SASL/gssapi bind we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl is very dependent on correctly configured DNS whereas this routine is much less fragile see RFC2078 and RFC2222 for details */ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { uint32_t minor_status; gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NULL_OID; gss_buffer_desc output_token, input_token; uint32_t req_flags, ret_flags; int conf_state; struct berval cred; struct berval *scred = NULL; int i=0; int gss_rc, rc; uint8_t *p; uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED; uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN; ADS_STATUS status; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; input_token.value = NULL; input_token.length = 0; status = ads_init_gssapi_cred(ads, &gss_cred); if (!ADS_ERR_OK(status)) { goto failed; } /* * Note: here we always ask the gssapi for sign and seal * as this is negotiated later after the mutal * authentication */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; for (i=0; i < MAX_GSS_PASSES; i++) { gss_rc = gss_init_sec_context(&minor_status, gss_cred, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, NULL, &output_token, &ret_flags, NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } cred.bv_val = (char *)output_token.value; cred.bv_len = output_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto failed; } if (output_token.value) { gss_release_buffer(&minor_status, &output_token); } if (scred) { input_token.value = scred->bv_val; input_token.length = scred->bv_len; } else { input_token.value = NULL; input_token.length = 0; } if (gss_rc == 0) break; } gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token, &conf_state,NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } p = (uint8_t *)output_token.value; #if 0 file_save("sasl_gssapi.dat", output_token.value, output_token.length); #endif if (p) { wrap_type = CVAL(p,0); SCVAL(p,0,0); max_msg_size = RIVAL(p,0); } gss_release_buffer(&minor_status, &output_token); if (!(wrap_type & wrap->wrap_type)) { /* * the server doesn't supports the wrap * type we want :-( */ DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n", wrap->wrap_type, wrap_type)); DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n")); status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } /* 0x58 is the minimum windows accepts */ if (max_msg_size < 0x58) { max_msg_size = 0x58; } output_token.length = 4; output_token.value = SMB_MALLOC(output_token.length); if (!output_token.value) { output_token.length = 0; status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto failed; } p = (uint8_t *)output_token.value; RSIVAL(p,0,max_msg_size); SCVAL(p,0,wrap->wrap_type); /* * we used to add sprintf("dn:%s", ads->config.bind_path) here. * but using ads->config.bind_path is the wrong! It should be * the DN of the user object! * * w2k3 gives an error when we send an incorrect DN, but sending nothing * is ok and matches the information flow used in GSS-SPNEGO. */ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &output_token, /* used as *input* here. */ &conf_state, &input_token); /* Used as *output* here. */ if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); output_token.length = 0; SAFE_FREE(output_token.value); goto failed; } /* We've finished with output_token. */ SAFE_FREE(output_token.value); output_token.length = 0; cred.bv_val = (char *)input_token.value; cred.bv_len = input_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); gss_release_buffer(&minor_status, &input_token); status = ADS_ERROR(rc); if (!ADS_ERR_OK(status)) { goto failed; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { gss_rc = gss_wrap_size_limit(&minor_status, context_handle, (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL), GSS_C_QOP_DEFAULT, max_msg_size, &wrap->out.max_unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } wrap->out.sig_size = max_msg_size - wrap->out.max_unwrapped; wrap->in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap->wrap_private_data, ads->ldap.ld, &ads_sasl_gssapi_ops, context_handle); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); goto failed; } /* make sure we don't free context_handle */ context_handle = GSS_C_NO_CONTEXT; } failed: if (gss_cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&minor_status, &gss_cred); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); if(scred) ber_bvfree(scred); return status; }
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, void *data, krb5_krbhst_info *hi, time_t timeout, const krb5_data *send_buf, krb5_data *recv_buf) { krb5_error_code ret; NTSTATUS status; struct socket_address *remote_addr; const char *name; struct addrinfo *ai, *a; struct smb_krb5_socket *smb_krb5; struct tevent_context *ev = talloc_get_type(data, struct tevent_context); DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length); ret = krb5_krbhst_get_addrinfo(context, hi, &ai); if (ret) { return ret; } for (a = ai; a; a = ai->ai_next) { smb_krb5 = talloc(NULL, struct smb_krb5_socket); if (!smb_krb5) { return ENOMEM; } smb_krb5->hi = hi; switch (a->ai_family) { case PF_INET: name = "ipv4"; break; #ifdef HAVE_IPV6 case PF_INET6: name = "ipv6"; break; #endif default: talloc_free(smb_krb5); return EINVAL; } status = NT_STATUS_INVALID_PARAMETER; switch (hi->proto) { case KRB5_KRBHST_UDP: status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_TCP: status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_HTTP: talloc_free(smb_krb5); return EINVAL; } if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_steal(smb_krb5, smb_krb5->sock); remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen); if (!remote_addr) { talloc_free(smb_krb5); continue; } status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev); if (!NT_STATUS_IS_OK(status)) { talloc_free(smb_krb5); continue; } talloc_free(remote_addr); /* Setup the FDE, start listening for read events * from the start (otherwise we may miss a socket * drop) and mark as AUTOCLOSE along with the fde */ /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock, socket_get_fd(smb_krb5->sock), TEVENT_FD_READ, smb_krb5_socket_handler, smb_krb5); /* its now the job of the event layer to close the socket */ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn); socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE); tevent_add_timer(ev, smb_krb5, timeval_current_ofs(timeout, 0), smb_krb5_request_timeout, smb_krb5); smb_krb5->status = NT_STATUS_OK; smb_krb5->reply = data_blob(NULL, 0); switch (hi->proto) { case KRB5_KRBHST_UDP: TEVENT_FD_WRITEABLE(smb_krb5->fde); smb_krb5->request = send_blob; break; case KRB5_KRBHST_TCP: smb_krb5->packet = packet_init(smb_krb5); if (smb_krb5->packet == NULL) { talloc_free(smb_krb5); return ENOMEM; } packet_set_private(smb_krb5->packet, smb_krb5); packet_set_socket(smb_krb5->packet, smb_krb5->sock); packet_set_callback(smb_krb5->packet, smb_krb5_full_packet); packet_set_full_request(smb_krb5->packet, packet_full_request_u32); packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler); packet_set_event_context(smb_krb5->packet, ev); packet_set_fde(smb_krb5->packet, smb_krb5->fde); smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4); RSIVAL(smb_krb5->request.data, 0, send_blob.length); memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length); packet_send(smb_krb5->packet, smb_krb5->request); break; case KRB5_KRBHST_HTTP: talloc_free(smb_krb5); return EINVAL; } while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { if (tevent_loop_once(ev) != 0) { talloc_free(smb_krb5); return EINVAL; } } if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) { talloc_free(smb_krb5); continue; } if (!NT_STATUS_IS_OK(smb_krb5->status)) { DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status))); talloc_free(smb_krb5); continue; } ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length); if (ret) { talloc_free(smb_krb5); return ret; } talloc_free(smb_krb5); break; } if (a) { return 0; } return KRB5_KDC_UNREACH; }