int buffer_next_uint16(xrtp_buffer_t * buf, uint16 * ret){ if(buf->pos + sizeof(uint16) > buf->len){ return 0; } if(HOST_BYTEORDER == BIGEND_ORDER) { *ret = RSVAL(buf->data, buf->pos); buffer_log(("buffer_next_uint16: BE(%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); } else { *ret = SVAL(buf->data, buf->pos); buffer_log(("buffer_next_uint16: LE(%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); } if(HOST_BYTEORDER != buf->byte_order) { _buffer_swap16(*ret); buffer_log(("buffer_next_uint16: HE(%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); } buf->pos += sizeof(uint16); return sizeof(uint16); }
static void dbg_rw_punival(BOOL charmode, const char *name, int depth, prs_struct *ps, char *in_buf, char *out_buf, int len) { int i; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) { for (i = 0; i < len; i++) SSVAL(out_buf,2*i,RSVAL(in_buf, 2*i)); } else { for (i = 0; i < len; i++) SSVAL(out_buf, 2*i, SVAL(in_buf, 2*i)); } } else { if (ps->bigendian_data) { for (i = 0; i < len; i++) RSSVAL(in_buf, 2*i, SVAL(out_buf,2*i)); } else { for (i = 0; i < len; i++) SSVAL(in_buf, 2*i, SVAL(out_buf,2*i)); } } DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name)); if (charmode) print_asc(5, (unsigned char*)out_buf, 2*len); else { for (i = 0; i < len; i++) DEBUG(5,("%04x ", out_buf[i])); } DEBUG(5,("\n")); }
uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob) { if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) { return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET); } else { return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET); } }
_PUBLIC_ NTSTATUS packet_full_request_u16(void *private_data, DATA_BLOB blob, size_t *size) { if (blob.length < 2) { return STATUS_MORE_ENTRIES; } *size = 2 + RSVAL(blob.data, 0); if (*size > blob.length) { return STATUS_MORE_ENTRIES; } return NT_STATUS_OK; }
BOOL prs_uint16s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len) { int i; char *q = prs_mem_get(ps, len * sizeof(uint16)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) { for (i = 0; i < len; i++) data16s[i] = RSVAL(q, 2*i); } else { for (i = 0; i < len; i++) data16s[i] = SVAL(q, 2*i); } } else { if (ps->bigendian_data) { for (i = 0; i < len; i++) RSSVAL(q, 2*i, data16s[i]); } else { for (i = 0; i < len; i++) SSVAL(q, 2*i, data16s[i]); } } DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name)); if (charmode) print_asc(5, (unsigned char*)data16s, 2*len); else { for (i = 0; i < len; i++) DEBUG(5,("%04x ", data16s[i])); } DEBUG(5,("\n")); ps->data_offset += (len * sizeof(uint16)); return True; }
BOOL prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16) { char *q = prs_mem_get(ps, sizeof(uint16)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *data16 = RSVAL(q,0); else *data16 = SVAL(q,0); } else { if (ps->bigendian_data) RSSVAL(q,0,*data16); else SSVAL(q,0,*data16); } DEBUG(5,("%s%04x %s: %04x\n", tab_depth(depth), ps->data_offset, name, *data16)); ps->data_offset += sizeof(uint16); return True; }
static krb5_error_code parse_setpw_reply(krb5_context context, bool use_tcp, krb5_auth_context auth_context, krb5_data *packet) { krb5_data ap_rep; char *p; int vnum, ret, res_code; krb5_data cipherresult; krb5_data clearresult; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; unsigned int msg_length = packet->length; if (packet->length < (use_tcp ? 8 : 4)) { return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)packet->data; /* ** see if it is an error */ if (krb5_is_krb_error(packet)) { ret = handle_krberror_packet(context, packet); if (ret) { return ret; } } /* tcp... */ if (use_tcp) { msg_length -= 4; if (RIVAL(p, 0) != msg_length) { DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n", RIVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 4; } if (RSVAL(p, 0) != msg_length) { DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n", RSVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 2; vnum = RSVAL(p, 0); p += 2; /* FIXME: According to standard there is only one type of reply */ if (vnum != KRB5_KPASSWD_VERS_SETPW && vnum != KRB5_KPASSWD_VERS_SETPW_ALT && vnum != KRB5_KPASSWD_VERS_CHANGEPW) { DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum)); return KRB5KDC_ERR_BAD_PVNO; } ap_rep.length = RSVAL(p, 0); p += 2; if (p + ap_rep.length >= (char *)packet->data + packet->length) { DEBUG(1,("ptr beyond end of packet from kpasswd server\n")); return KRB5KRB_AP_ERR_MODIFIED; } if (ap_rep.length == 0) { DEBUG(1,("got unencrypted setpw result?!\n")); return KRB5KRB_AP_ERR_MODIFIED; } /* verify ap_rep */ ap_rep.data = p; p += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } krb5_free_ap_rep_enc_part(context, ap_rep_enc); cipherresult.data = p; cipherresult.length = ((char *)packet->data + packet->length) - p; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) { DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } if (clearresult.length < 2) { free(clearresult.data); ret = KRB5KRB_AP_ERR_MODIFIED; return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)clearresult.data; res_code = RSVAL(p, 0); free(clearresult.data); if ((res_code < KRB5_KPASSWD_SUCCESS) || (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { return KRB5KRB_AP_ERR_MODIFIED; } if (res_code == KRB5_KPASSWD_SUCCESS) { return 0; } else { const char *errstr; setpw_result_code_string(context, res_code, &errstr); DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code)); return kpasswd_err_to_krb5_err(res_code); } }
bool kpasswdd_process(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, DATA_BLOB *input, DATA_BLOB *reply, struct tsocket_address *peer_addr, struct tsocket_address *my_addr, int datagram_reply) { bool ret; const uint16_t header_len = 6; uint16_t len; uint16_t ap_req_len; uint16_t krb_priv_len; uint16_t version; NTSTATUS nt_status; DATA_BLOB ap_req, krb_priv_req; DATA_BLOB krb_priv_rep = data_blob(NULL, 0); DATA_BLOB ap_rep = data_blob(NULL, 0); DATA_BLOB kpasswd_req, kpasswd_rep; struct cli_credentials *server_credentials; struct gensec_security *gensec_security; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); char *keytab_name; if (!tmp_ctx) { return false; } /* Be parinoid. We need to ensure we don't just let the * caller lead us into a buffer overflow */ if (input->length <= header_len) { talloc_free(tmp_ctx); return false; } len = RSVAL(input->data, 0); if (input->length != len) { talloc_free(tmp_ctx); return false; } /* There are two different versions of this protocol so far, * plus others in the standards pipe. Fortunetly they all * take a very similar framing */ version = RSVAL(input->data, 2); ap_req_len = RSVAL(input->data, 4); if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) { talloc_free(tmp_ctx); return false; } krb_priv_len = len - ap_req_len; ap_req = data_blob_const(&input->data[header_len], ap_req_len); krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len); server_credentials = cli_credentials_init(tmp_ctx); if (!server_credentials) { DEBUG(1, ("Failed to init server credentials\n")); return false; } /* We want the credentials subsystem to use the krb5 context * we already have, rather than a new context */ cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context); cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx); keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->base_ctx); cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED); ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED); if (ret != 0) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "Failed to obtain server credentials for kadmin/changepw: %s\n", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* We don't strictly need to call this wrapper, and could call * gensec_server_start directly, as we have no need for NTLM * and we have a PAC, but this ensures that the wrapper can be * safely extended for other helpful things in future */ nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx, kdc->task->msg_ctx, kdc->task->lp_ctx, server_credentials, "kpasswd", &gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* The kerberos PRIV packets include these addresses. MIT * clients check that they are present */ #if 0 /* Skip this part for now, it breaks with a NetAPP filer and * in any case where the client address is behind NAT. If * older MIT clients need this, we might have to insert more * complex code */ nt_status = gensec_set_local_address(gensec_security, peer_addr); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } #endif nt_status = gensec_set_local_address(gensec_security, my_addr); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* We want the GENSEC wrap calls to generate PRIV tokens */ gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); nt_status = gensec_start_mech_by_name(gensec_security, "krb5"); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* Accept the AP-REQ and generate teh AP-REP we need for the reply */ nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep); if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_update failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* Extract the data from the KRB-PRIV half of the message */ nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req); if (!NT_STATUS_IS_OK(nt_status)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_unwrap failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* Figure out something to do with it (probably changing a password...) */ ret = kpasswd_process_request(kdc, tmp_ctx, gensec_security, version, &kpasswd_req, &kpasswd_rep); if (!ret) { /* Argh! */ return false; } /* And wrap up the reply: This ensures that the error message * or success can be verified by the client */ nt_status = gensec_wrap(gensec_security, tmp_ctx, &kpasswd_rep, &krb_priv_rep); if (!NT_STATUS_IS_OK(nt_status)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_wrap failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } reply: *reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len); if (!reply->data) { return false; } RSSVAL(reply->data, 0, reply->length); RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */ RSSVAL(reply->data, 4, ap_rep.length); memcpy(reply->data + header_len, ap_rep.data, ap_rep.length); memcpy(reply->data + header_len + ap_rep.length, krb_priv_rep.data, krb_priv_rep.length); talloc_free(tmp_ctx); return ret; }
WERROR dns_verify_tsig(struct dns_server *dns, TALLOC_CTX *mem_ctx, struct dns_request_state *state, struct dns_name_packet *packet, DATA_BLOB *in) { WERROR werror; NTSTATUS status; enum ndr_err_code ndr_err; bool found_tsig = false; uint16_t i, arcount = 0; DATA_BLOB tsig_blob, fake_tsig_blob, sig; uint8_t *buffer = NULL; size_t buffer_len = 0, packet_len = 0; struct dns_server_tkey *tkey = NULL; struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, struct dns_fake_tsig_rec); /* Find the first TSIG record in the additional records */ for (i=0; i < packet->arcount; i++) { if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) { found_tsig = true; break; } } if (!found_tsig) { return WERR_OK; } /* The TSIG record needs to be the last additional record */ if (found_tsig && i + 1 != packet->arcount) { DEBUG(1, ("TSIG record not the last additional record!\n")); return DNS_ERR(FORMAT_ERROR); } /* We got a TSIG, so we need to sign our reply */ state->sign = true; state->tsig = talloc_zero(state->mem_ctx, struct dns_res_rec); if (state->tsig == NULL) { return WERR_NOT_ENOUGH_MEMORY; } werror = dns_copy_tsig(state->tsig, &packet->additional[i], state->tsig); if (!W_ERROR_IS_OK(werror)) { return werror; } packet->arcount--; tkey = dns_find_tkey(dns->tkeys, state->tsig->name); if (tkey == NULL) { /* * We must save the name for use in the TSIG error * response and have no choice here but to save the * keyname from the TSIG request. */ state->key_name = talloc_strdup(state->mem_ctx, state->tsig->name); if (state->key_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } state->tsig_error = DNS_RCODE_BADKEY; return DNS_ERR(NOTAUTH); } /* * Remember the keyname that found an existing tkey, used * later to fetch the key with dns_find_tkey() when signing * and adding a TSIG record with MAC. */ state->key_name = talloc_strdup(state->mem_ctx, tkey->name); if (state->key_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } /* FIXME: check TSIG here */ if (check_rec == NULL) { return WERR_NOT_ENOUGH_MEMORY; } /* first build and verify check packet */ check_rec->name = talloc_strdup(check_rec, tkey->name); if (check_rec->name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } check_rec->rr_class = DNS_QCLASS_ANY; check_rec->ttl = 0; check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm); if (check_rec->algorithm_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } check_rec->time_prefix = 0; check_rec->time = state->tsig->rdata.tsig_record.time; check_rec->fudge = state->tsig->rdata.tsig_record.fudge; check_rec->error = 0; check_rec->other_size = 0; check_rec->other_data = NULL; ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, state->tsig, (ndr_push_flags_fn_t)ndr_push_dns_res_rec); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1, ("Failed to push packet: %s!\n", ndr_errstr(ndr_err))); return DNS_ERR(SERVER_FAILURE); } ndr_err = ndr_push_struct_blob(&fake_tsig_blob, mem_ctx, check_rec, (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1, ("Failed to push packet: %s!\n", ndr_errstr(ndr_err))); return DNS_ERR(SERVER_FAILURE); } /* we need to work some magic here. we need to keep the input packet * exactly like we got it, but we need to cut off the tsig record */ packet_len = in->length - tsig_blob.length; buffer_len = packet_len + fake_tsig_blob.length; buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len); if (buffer == NULL) { return WERR_NOT_ENOUGH_MEMORY; } memcpy(buffer, in->data, packet_len); memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length); sig.length = state->tsig->rdata.tsig_record.mac_size; sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length); if (sig.data == NULL) { return WERR_NOT_ENOUGH_MEMORY; } /* Now we also need to count down the additional record counter */ arcount = RSVAL(buffer, 10); RSSVAL(buffer, 10, arcount-1); status = gensec_check_packet(tkey->gensec, buffer, buffer_len, buffer, buffer_len, &sig); if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { state->tsig_error = DNS_RCODE_BADSIG; return DNS_ERR(NOTAUTH); } if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Verifying tsig failed: %s\n", nt_errstr(status))); return ntstatus_to_werror(status); } state->authenticated = true; return WERR_OK; }