Exemple #1
0
/* 
   push a ncacn_packet into a blob, potentially with auth info
*/
NTSTATUS ncacn_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
			 struct ncacn_packet *pkt,
			 struct dcerpc_auth *auth_info)
{
	struct ndr_push *ndr;
	enum ndr_err_code ndr_err;

	ndr = ndr_push_init_ctx(mem_ctx);
	if (!ndr) {
		return NT_STATUS_NO_MEMORY;
	}

	if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
		ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
	}

	if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
		ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
	}

	if (auth_info) {
		pkt->auth_length = auth_info->credentials.length;
	} else {
		pkt->auth_length = 0;
	}

	ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return ndr_map_error2ntstatus(ndr_err);
	}

	if (auth_info) {
#if 0
		/* the s3 rpc server doesn't handle auth padding in
		   bind requests. Use zero auth padding to keep us
		   working with old servers */
		uint32_t offset = ndr->offset;
		ndr_err = ndr_push_align(ndr, 16);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			return ndr_map_error2ntstatus(ndr_err);
		}
		auth_info->auth_pad_length = ndr->offset - offset;
#else
		auth_info->auth_pad_length = 0;
#endif
		ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			return ndr_map_error2ntstatus(ndr_err);
		}
	}

	*blob = ndr_push_blob(ndr);

	/* fill in the frag length */
	dcerpc_set_frag_length(blob, blob->length);

	return NT_STATUS_OK;
}
Exemple #2
0
/*
  return a dcerpc fault
*/
NTSTATUS dcesrv_fault_with_flags(struct dcesrv_call_state *call,
                                 uint32_t fault_code,
                                 uint8_t extra_flags)
{
    struct ncacn_packet pkt;
    struct data_blob_list_item *rep;
    static const uint8_t zeros[4] = { 0, };
    NTSTATUS status;

    /* setup a fault */
    dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
    pkt.auth_length = 0;
    pkt.call_id = call->pkt.call_id;
    pkt.ptype = DCERPC_PKT_FAULT;
    pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
    pkt.u.fault.alloc_hint = 24;
    switch (call->pkt.ptype) {
    case DCERPC_PKT_REQUEST:
        pkt.u.fault.context_id = call->pkt.u.request.context_id;
        break;
    default:
        pkt.u.fault.context_id = 0;
        break;
    }
    if (fault_code == DCERPC_NCA_S_PROTO_ERROR) {
        /*
         * context_id = 0 is forced on protocol errors.
         */
        pkt.u.fault.context_id = 0;
    }
    pkt.u.fault.cancel_count = 0;
    pkt.u.fault.status = fault_code;
    pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));

    rep = talloc_zero(call, struct data_blob_list_item);
    if (!rep) {
        return NT_STATUS_NO_MEMORY;
    }

    status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    dcerpc_set_frag_length(&rep->blob, rep->blob.length);

    DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
    dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);

    if (call->conn->call_list && call->conn->call_list->replies) {
        if (call->conn->transport.report_output_data) {
            call->conn->transport.report_output_data(call->conn);
        }
    }

    return NT_STATUS_OK;
}
Exemple #3
0
/*
  return a dcerpc fault
*/
NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
{
	struct ncacn_packet pkt;
	struct data_blob_list_item *rep;
	uint8_t zeros[4];
	NTSTATUS status;

	/* setup a bind_ack */
	dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
	pkt.auth_length = 0;
	pkt.call_id = call->pkt.call_id;
	pkt.ptype = DCERPC_PKT_FAULT;
	pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
	pkt.u.fault.alloc_hint = 0;
	pkt.u.fault.context_id = 0;
	pkt.u.fault.cancel_count = 0;
	pkt.u.fault.status = fault_code;

	ZERO_STRUCT(zeros);
	pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));

	rep = talloc(call, struct data_blob_list_item);
	if (!rep) {
		return NT_STATUS_NO_MEMORY;
	}

	status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	dcerpc_set_frag_length(&rep->blob, rep->blob.length);

	DLIST_ADD_END(call->replies, rep);
	dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);

	if (call->conn->call_list && call->conn->call_list->replies) {
		if (call->conn->transport.report_output_data) {
			call->conn->transport.report_output_data(call->conn);
		}
	}

	return NT_STATUS_OK;
}
Exemple #4
0
/* 
   push a ncacn_packet into a blob, potentially with auth info
*/
NTSTATUS ncacn_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
			  struct ncacn_packet *pkt,
			  struct dcerpc_auth *auth_info)
{
	NTSTATUS status;
	struct ndr_push *ndr;

	ndr = ndr_push_init_ctx(mem_ctx);
	if (!ndr) {
		return NT_STATUS_NO_MEMORY;
	}

	if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
		ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
	}

	if (pkt->pfc_flags & DCERPC_PFC_FLAG_ORPC) {
		ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
	}

	if (auth_info) {
		pkt->auth_length = auth_info->credentials.length;
	} else {
		pkt->auth_length = 0;
	}

	status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (auth_info) {
		status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
	}

	*blob = ndr_push_blob(ndr);

	/* fill in the frag length */
	dcerpc_set_frag_length(blob, blob->length);

	return NT_STATUS_OK;
}
Exemple #5
0
/**
* @brief NDR Encodes a ncacn_packet
*
* @param mem_ctx	The memory context the blob will be allocated on
* @param ptype		The DCERPC packet type
* @param pfc_flags	The DCERPC PFC Falgs
* @param auth_length	The length of the trailing auth blob
* @param call_id	The call ID
* @param u		The payload of the packet
* @param blob [out]	The encoded blob if successful
*
* @return an NTSTATUS error code
*/
NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
				  enum dcerpc_pkt_type ptype,
				  uint8_t pfc_flags,
				  uint16_t auth_length,
				  uint32_t call_id,
				  union dcerpc_payload *u,
				  DATA_BLOB *blob)
{
	struct ncacn_packet r;
	enum ndr_err_code ndr_err;

	r.rpc_vers		= 5;
	r.rpc_vers_minor	= 0;
	r.ptype			= ptype;
	r.pfc_flags		= pfc_flags;
	r.drep[0]		= DCERPC_DREP_LE;
	r.drep[1]		= 0;
	r.drep[2]		= 0;
	r.drep[3]		= 0;
	r.auth_length		= auth_length;
	r.call_id		= call_id;
	r.u			= *u;

	ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
		(ndr_push_flags_fn_t)ndr_push_ncacn_packet);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return ndr_map_error2ntstatus(ndr_err);
	}

	dcerpc_set_frag_length(blob, blob->length);


	if (DEBUGLEVEL >= 10) {
		/* set frag len for print function */
		r.frag_length = blob->length;
		NDR_PRINT_DEBUG(ncacn_packet, &r);
	}

	return NT_STATUS_OK;
}
Exemple #6
0
/* 
   push a dcerpc request packet into a blob, possibly signing it.
*/
static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, 
					 DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
					 size_t sig_size,
					 struct ncacn_packet *pkt)
{
	NTSTATUS status;
	struct ndr_push *ndr;
	DATA_BLOB creds2;
	size_t payload_length;
	enum ndr_err_code ndr_err;
	size_t hdr_size = DCERPC_REQUEST_LENGTH;

	/* non-signed packets are simpler */
	if (sig_size == 0) {
		return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);
	}

	switch (c->security_state.auth_info->auth_level) {
	case DCERPC_AUTH_LEVEL_PRIVACY:
	case DCERPC_AUTH_LEVEL_INTEGRITY:
		break;

	case DCERPC_AUTH_LEVEL_CONNECT:
		/* TODO: let the gensec mech decide if it wants to generate a signature */
		return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);

	case DCERPC_AUTH_LEVEL_NONE:
		return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);

	default:
		return NT_STATUS_INVALID_LEVEL;
	}

	ndr = ndr_push_init_ctx(mem_ctx, c->iconv_convenience);
	if (!ndr) {
		return NT_STATUS_NO_MEMORY;
	}

	if (c->flags & DCERPC_PUSH_BIGENDIAN) {
		ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
	}

	if (c->flags & DCERPC_NDR64) {
		ndr->flags |= LIBNDR_FLAG_NDR64;
	}

	if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
		ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
		hdr_size += 16;
	}

	ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return ndr_map_error2ntstatus(ndr_err);
	}
	status = NT_STATUS_OK;

	/* pad to 16 byte multiple in the payload portion of the
	   packet. This matches what w2k3 does */
	c->security_state.auth_info->auth_pad_length = 
		(16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
	ndr_err = ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return ndr_map_error2ntstatus(ndr_err);
	}
	status = NT_STATUS_OK;

	payload_length = pkt->u.request.stub_and_verifier.length + 
		c->security_state.auth_info->auth_pad_length;

	/* we start without signature, it will appended later */
	c->security_state.auth_info->credentials = data_blob(NULL,0);

	/* add the auth verifier */
	ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return ndr_map_error2ntstatus(ndr_err);
	}
	status = NT_STATUS_OK;

	/* extract the whole packet as a blob */
	*blob = ndr_push_blob(ndr);

	/*
	 * Setup the frag and auth length in the packet buffer.
	 * This is needed if the GENSEC mech does AEAD signing
	 * of the packet headers. The signature itself will be
	 * appended later.
	 */
	dcerpc_set_frag_length(blob, blob->length + sig_size);
	dcerpc_set_auth_length(blob, sig_size);

	/* sign or seal the packet */
	switch (c->security_state.auth_info->auth_level) {
	case DCERPC_AUTH_LEVEL_PRIVACY:
		status = gensec_seal_packet(c->security_state.generic_state, 
					    mem_ctx, 
					    blob->data + hdr_size,
					    payload_length,
					    blob->data,
					    blob->length,
					    &creds2);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}
		break;

	case DCERPC_AUTH_LEVEL_INTEGRITY:
		status = gensec_sign_packet(c->security_state.generic_state, 
					    mem_ctx, 
					    blob->data + hdr_size,
					    payload_length, 
					    blob->data,
					    blob->length,
					    &creds2);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}
		break;

	default:
		status = NT_STATUS_INVALID_LEVEL;
		break;
	}

	if (creds2.length != sig_size) {
		DEBUG(0,("ncacn_push_request_sign: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
			(unsigned) creds2.length,
			(unsigned) sig_size,
			(unsigned) c->security_state.auth_info->auth_pad_length,
			(unsigned) pkt->u.request.stub_and_verifier.length));
		return NT_STATUS_INTERNAL_ERROR;
	}

	if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) {
		return NT_STATUS_NO_MEMORY;
	}

	return NT_STATUS_OK;
}
Exemple #7
0
/* 
   push a signed or sealed dcerpc request packet into a blob
*/
bool dcesrv_auth_response(struct dcesrv_call_state *call,
			  DATA_BLOB *blob, size_t sig_size,
			  struct ncacn_packet *pkt)
{
	struct dcesrv_connection *dce_conn = call->conn;
	NTSTATUS status;
	enum ndr_err_code ndr_err;
	struct ndr_push *ndr;
	uint32_t payload_length;
	DATA_BLOB creds2;

	/* non-signed packets are simple */
	if (sig_size == 0) {
		status = ncacn_push_auth(blob, call, pkt, NULL);
		return NT_STATUS_IS_OK(status);
	}

	switch (dce_conn->auth_state.auth_info->auth_level) {
	case DCERPC_AUTH_LEVEL_PRIVACY:
	case DCERPC_AUTH_LEVEL_INTEGRITY:
		break;

	case DCERPC_AUTH_LEVEL_CONNECT:
		/*
		 * TODO: let the gensec mech decide if it wants to generate a
		 *       signature that might be needed for schannel...
		 */
		status = ncacn_push_auth(blob, call, pkt, NULL);
		return NT_STATUS_IS_OK(status);

	case DCERPC_AUTH_LEVEL_NONE:
		status = ncacn_push_auth(blob, call, pkt, NULL);
		return NT_STATUS_IS_OK(status);

	default:
		return false;
	}

	ndr = ndr_push_init_ctx(call);
	if (!ndr) {
		return false;
	}

	if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
		ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
	}

	ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return false;
	}

	/* pad to 16 byte multiple in the payload portion of the
	   packet. This matches what w2k3 does. Note that we can't use
	   ndr_push_align() as that is relative to the start of the
	   whole packet, whereas w2k8 wants it relative to the start
	   of the stub */
	dce_conn->auth_state.auth_info->auth_pad_length =
		(16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
	ndr_err = ndr_push_zero(ndr,
				dce_conn->auth_state.auth_info->auth_pad_length);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return false;
	}

	payload_length = pkt->u.response.stub_and_verifier.length +
		dce_conn->auth_state.auth_info->auth_pad_length;

	/* we start without signature, it will appended later */
	dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);

	/* add the auth verifier */
	ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
				       dce_conn->auth_state.auth_info);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return false;
	}

	/* extract the whole packet as a blob */
	*blob = ndr_push_blob(ndr);

	/*
	 * Setup the frag and auth length in the packet buffer.
	 * This is needed if the GENSEC mech does AEAD signing
	 * of the packet headers. The signature itself will be
	 * appended later.
	 */
	dcerpc_set_frag_length(blob, blob->length + sig_size);
	dcerpc_set_auth_length(blob, sig_size);

	/* sign or seal the packet */
	switch (dce_conn->auth_state.auth_info->auth_level) {
	case DCERPC_AUTH_LEVEL_PRIVACY:
		status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
					    call,
					    ndr->data + DCERPC_REQUEST_LENGTH, 
					    payload_length,
					    blob->data,
					    blob->length,
					    &creds2);
		break;

	case DCERPC_AUTH_LEVEL_INTEGRITY:
		status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
					    call,
					    ndr->data + DCERPC_REQUEST_LENGTH, 
					    payload_length,
					    blob->data,
					    blob->length,
					    &creds2);
		break;

	default:
		status = NT_STATUS_INVALID_LEVEL;
		break;
	}

	if (!NT_STATUS_IS_OK(status)) {
		return false;
	}	

	if (creds2.length != sig_size) {
		DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
			 (unsigned)creds2.length, (uint32_t)sig_size,
			 (unsigned)dce_conn->auth_state.auth_info->auth_pad_length,
			 (unsigned)pkt->u.response.stub_and_verifier.length));
		dcerpc_set_frag_length(blob, blob->length + creds2.length);
		dcerpc_set_auth_length(blob, creds2.length);
	}

	if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
		status = NT_STATUS_NO_MEMORY;
		return false;
	}
	data_blob_free(&creds2);

	return true;
}
Exemple #8
0
static bool api_pipe_alter_context(struct pipes_struct *p,
					struct ncacn_packet *pkt)
{
	struct dcerpc_auth auth_info = {0};
	uint16_t assoc_gid;
	NTSTATUS status;
	union dcerpc_payload u;
	struct dcerpc_ack_ctx bind_ack_ctx;
	DATA_BLOB auth_resp = data_blob_null;
	DATA_BLOB auth_blob = data_blob_null;
	struct gensec_security *gensec_security;

	DEBUG(5,("api_pipe_alter_context: make response. %d\n", __LINE__));

	if (pkt->u.bind.assoc_group_id != 0) {
		assoc_gid = pkt->u.bind.assoc_group_id;
	} else {
		assoc_gid = 0x53f0;
	}

	/*
	 * Create the bind response struct.
	 */

	/* If the requested abstract synt uuid doesn't match our client pipe,
		reject the bind_ack & set the transfer interface synt to all 0's,
		ver 0 (observed when NT5 attempts to bind to abstract interfaces
		unknown to NT4)
		Needed when adding entries to a DACL from NT5 - SK */

	if (check_bind_req(p,
			&pkt->u.bind.ctx_list[0].abstract_syntax,
			&pkt->u.bind.ctx_list[0].transfer_syntaxes[0],
			pkt->u.bind.ctx_list[0].context_id)) {

		bind_ack_ctx.result = 0;
		bind_ack_ctx.reason.value = 0;
		bind_ack_ctx.syntax = pkt->u.bind.ctx_list[0].transfer_syntaxes[0];
	} else {
		p->pipe_bound = False;
		/* Rejection reason: abstract syntax not supported */
		bind_ack_ctx.result = DCERPC_BIND_PROVIDER_REJECT;
		bind_ack_ctx.reason.value = DCERPC_BIND_REASON_ASYNTAX;
		bind_ack_ctx.syntax = ndr_syntax_id_null;
	}

	/*
	 * Check if this is an authenticated alter context request.
	 */
	if (pkt->auth_length) {
		/* Quick length check. Won't catch a bad auth footer,
		 * prevents overrun. */

		if (pkt->frag_length < RPC_HEADER_LEN +
					DCERPC_AUTH_TRAILER_LENGTH +
					pkt->auth_length) {
			DEBUG(0,("api_pipe_alter_context: auth_len (%u) "
				"too long for fragment %u.\n",
				(unsigned int)pkt->auth_length,
				(unsigned int)pkt->frag_length ));
			goto err_exit;
		}

		status = dcerpc_pull_dcerpc_auth(pkt,
						 &pkt->u.bind.auth_info,
						 &auth_info, p->endian);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("Unable to unmarshall dcerpc_auth.\n"));
			goto err_exit;
		}

		/* We can only finish if the pipe is unbound for now */
		if (p->pipe_bound) {
			DEBUG(0, (__location__ ": Pipe already bound, "
				  "Altering Context not yet supported!\n"));
			goto err_exit;
		}

		if (auth_info.auth_type != p->auth.auth_type) {
			DEBUG(0, ("Auth type mismatch! Client sent %d, "
				  "but auth was started as type %d!\n",
				  auth_info.auth_type, p->auth.auth_type));
			goto err_exit;
		}

		gensec_security = p->auth.auth_ctx;
		status = auth_generic_server_step(gensec_security,
						  pkt,
						  &auth_info.credentials,
						  &auth_resp);
		if (NT_STATUS_IS_OK(status)) {
			/* third leg of auth, verify auth info */
			status = pipe_auth_verify_final(p);
			if (!NT_STATUS_IS_OK(status)) {
				DEBUG(0, ("Auth Verify failed (%s)\n",
					  nt_errstr(status)));
				goto err_exit;
			}
		} else if (NT_STATUS_EQUAL(status,
					NT_STATUS_MORE_PROCESSING_REQUIRED)) {
			DEBUG(10, ("More auth legs required.\n"));
		} else {
			DEBUG(0, ("Auth step returned an error (%s)\n",
				  nt_errstr(status)));
			goto err_exit;
		}
	}

	ZERO_STRUCT(u.alter_resp);
	u.alter_resp.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
	u.alter_resp.max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
	u.alter_resp.assoc_group_id = assoc_gid;

	/* secondary address CAN be NULL
	 * as the specs say it's ignored.
	 * It MUST be NULL to have the spoolss working.
	 */
	u.alter_resp.secondary_address = "";
	u.alter_resp.secondary_address_size = 1;

	u.alter_resp.num_results = 1;
	u.alter_resp.ctx_list = &bind_ack_ctx;

	/* NOTE: We leave the auth_info empty so we can calculate the padding
	 * later and then append the auth_info --simo */

	/*
	 * Marshall directly into the outgoing PDU space. We
	 * must do this as we need to set to the bind response
	 * header and are never sending more than one PDU here.
	 */

	status = dcerpc_push_ncacn_packet(p->mem_ctx,
					  DCERPC_PKT_ALTER_RESP,
					  DCERPC_PFC_FLAG_FIRST |
						DCERPC_PFC_FLAG_LAST,
					  auth_resp.length,
					  pkt->call_id,
					  &u,
					  &p->out_data.frag);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("Failed to marshall bind_ack packet. (%s)\n",
			  nt_errstr(status)));
	}

	if (auth_resp.length) {
		status = dcerpc_push_dcerpc_auth(pkt,
						 auth_info.auth_type,
						 auth_info.auth_level,
						 0, /* pad_len */
						 1, /* auth_context_id */
						 &auth_resp,
						 &auth_blob);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("Marshalling of dcerpc_auth failed.\n"));
			goto err_exit;
		}
	}

	/* Now that we have the auth len store it into the right place in
	 * the dcerpc header */
	dcerpc_set_frag_length(&p->out_data.frag,
				p->out_data.frag.length +
				auth_blob.length);

	if (auth_resp.length) {
		if (!data_blob_append(p->mem_ctx, &p->out_data.frag,
					auth_blob.data, auth_blob.length)) {
			DEBUG(0, ("Append of auth info failed.\n"));
			goto err_exit;
		}
	}

	/*
	 * Setup the lengths for the initial reply.
	 */

	p->out_data.data_sent_length = 0;
	p->out_data.current_pdu_sent = 0;

	TALLOC_FREE(auth_blob.data);
	return True;

  err_exit:

	data_blob_free(&p->out_data.frag);
	TALLOC_FREE(auth_blob.data);
	return setup_bind_nak(p, pkt);
}
Exemple #9
0
static bool api_pipe_bind_req(struct pipes_struct *p,
				struct ncacn_packet *pkt)
{
	struct dcerpc_auth auth_info = {0};
	uint16_t assoc_gid;
	unsigned int auth_type = DCERPC_AUTH_TYPE_NONE;
	NTSTATUS status;
	struct ndr_syntax_id id;
	uint8_t pfc_flags = 0;
	union dcerpc_payload u;
	struct dcerpc_ack_ctx bind_ack_ctx;
	DATA_BLOB auth_resp = data_blob_null;
	DATA_BLOB auth_blob = data_blob_null;
	const struct ndr_interface_table *table;

	/* No rebinds on a bound pipe - use alter context. */
	if (p->pipe_bound) {
		DEBUG(2,("Rejecting bind request on bound rpc connection\n"));
		return setup_bind_nak(p, pkt);
	}

	if (pkt->u.bind.num_contexts == 0) {
		DEBUG(0, ("api_pipe_bind_req: no rpc contexts around\n"));
		goto err_exit;
	}

	/*
	 * Try and find the correct pipe name to ensure
	 * that this is a pipe name we support.
	 */
	id = pkt->u.bind.ctx_list[0].abstract_syntax;

	table = ndr_table_by_uuid(&id.uuid);
	if (table == NULL) {
		DEBUG(0,("unknown interface\n"));
		return false;
	}

	if (rpc_srv_pipe_exists_by_id(&id)) {
		DEBUG(3, ("api_pipe_bind_req: %s -> %s rpc service\n",
			  rpc_srv_get_pipe_cli_name(&id),
			  rpc_srv_get_pipe_srv_name(&id)));
	} else {
		status = smb_probe_module(
			"rpc", dcerpc_default_transport_endpoint(pkt,
				NCACN_NP, table));

		if (NT_STATUS_IS_ERR(status)) {
			DEBUG(3,("api_pipe_bind_req: Unknown rpc service name "
                                 "%s in bind request.\n",
				 ndr_interface_name(&id.uuid,
						    id.if_version)));

			return setup_bind_nak(p, pkt);
		}

		if (rpc_srv_get_pipe_interface_by_cli_name(
				dcerpc_default_transport_endpoint(pkt,
					NCACN_NP, table),
				&id)) {
			DEBUG(3, ("api_pipe_bind_req: %s -> %s rpc service\n",
				  rpc_srv_get_pipe_cli_name(&id),
				  rpc_srv_get_pipe_srv_name(&id)));
		} else {
			DEBUG(0, ("module %s doesn't provide functions for "
				  "pipe %s!\n",
				  ndr_interface_name(&id.uuid,
						     id.if_version),
				  ndr_interface_name(&id.uuid,
						     id.if_version)));
			return setup_bind_nak(p, pkt);
		}
	}

	DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__));

	if (pkt->u.bind.assoc_group_id != 0) {
		assoc_gid = pkt->u.bind.assoc_group_id;
	} else {
		assoc_gid = 0x53f0;
	}

	/*
	 * Create the bind response struct.
	 */

	/* If the requested abstract synt uuid doesn't match our client pipe,
		reject the bind_ack & set the transfer interface synt to all 0's,
		ver 0 (observed when NT5 attempts to bind to abstract interfaces
		unknown to NT4)
		Needed when adding entries to a DACL from NT5 - SK */

	if (check_bind_req(p,
			&pkt->u.bind.ctx_list[0].abstract_syntax,
			&pkt->u.bind.ctx_list[0].transfer_syntaxes[0],
			pkt->u.bind.ctx_list[0].context_id)) {

		bind_ack_ctx.result = 0;
		bind_ack_ctx.reason.value = 0;
		bind_ack_ctx.syntax = pkt->u.bind.ctx_list[0].transfer_syntaxes[0];
	} else {
		p->pipe_bound = False;
		/* Rejection reason: abstract syntax not supported */
		bind_ack_ctx.result = DCERPC_BIND_PROVIDER_REJECT;
		bind_ack_ctx.reason.value = DCERPC_BIND_REASON_ASYNTAX;
		bind_ack_ctx.syntax = ndr_syntax_id_null;
	}

	/*
	 * Check if this is an authenticated bind request.
	 */
	if (pkt->auth_length) {
		/* Quick length check. Won't catch a bad auth footer,
		 * prevents overrun. */

		if (pkt->frag_length < RPC_HEADER_LEN +
					DCERPC_AUTH_TRAILER_LENGTH +
					pkt->auth_length) {
			DEBUG(0,("api_pipe_bind_req: auth_len (%u) "
				"too long for fragment %u.\n",
				(unsigned int)pkt->auth_length,
				(unsigned int)pkt->frag_length));
			goto err_exit;
		}

		/*
		 * Decode the authentication verifier.
		 */
		status = dcerpc_pull_dcerpc_auth(pkt,
						 &pkt->u.bind.auth_info,
						 &auth_info, p->endian);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("Unable to unmarshall dcerpc_auth.\n"));
			goto err_exit;
		}

		auth_type = auth_info.auth_type;

		/* Work out if we have to sign or seal etc. */
		switch (auth_info.auth_level) {
		case DCERPC_AUTH_LEVEL_INTEGRITY:
			p->auth.auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
			break;
		case DCERPC_AUTH_LEVEL_PRIVACY:
			p->auth.auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
			break;
		case DCERPC_AUTH_LEVEL_CONNECT:
			p->auth.auth_level = DCERPC_AUTH_LEVEL_CONNECT;
			break;
		default:
			DEBUG(0, ("Unexpected auth level (%u).\n",
				(unsigned int)auth_info.auth_level ));
			goto err_exit;
		}

		switch (auth_type) {
		case DCERPC_AUTH_TYPE_NONE:
			break;

		default:
			if (!pipe_auth_generic_bind(p, pkt,
						    &auth_info, &auth_resp)) {
				goto err_exit;
			}
			break;
		}
	}

	if (auth_type == DCERPC_AUTH_TYPE_NONE) {
		/* Unauthenticated bind request. */
		/* We're finished - no more packets. */
		p->auth.auth_type = DCERPC_AUTH_TYPE_NONE;
		/* We must set the pipe auth_level here also. */
		p->auth.auth_level = DCERPC_AUTH_LEVEL_NONE;
		p->pipe_bound = True;
		/* The session key was initialized from the SMB
		 * session in make_internal_rpc_pipe_p */
	}

	ZERO_STRUCT(u.bind_ack);
	u.bind_ack.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
	u.bind_ack.max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
	u.bind_ack.assoc_group_id = assoc_gid;

	/* name has to be \PIPE\xxxxx */
	u.bind_ack.secondary_address =
			talloc_asprintf(pkt, "\\PIPE\\%s",
					rpc_srv_get_pipe_srv_name(&id));
	if (!u.bind_ack.secondary_address) {
		DEBUG(0, ("Out of memory!\n"));
		goto err_exit;
	}
	u.bind_ack.secondary_address_size =
				strlen(u.bind_ack.secondary_address) + 1;

	u.bind_ack.num_results = 1;
	u.bind_ack.ctx_list = &bind_ack_ctx;

	/* NOTE: We leave the auth_info empty so we can calculate the padding
	 * later and then append the auth_info --simo */

	/*
	 * Marshall directly into the outgoing PDU space. We
	 * must do this as we need to set to the bind response
	 * header and are never sending more than one PDU here.
	 */

	pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;

	if (p->auth.hdr_signing) {
		pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
	}

	status = dcerpc_push_ncacn_packet(p->mem_ctx,
					  DCERPC_PKT_BIND_ACK,
					  pfc_flags,
					  auth_resp.length,
					  pkt->call_id,
					  &u,
					  &p->out_data.frag);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("Failed to marshall bind_ack packet. (%s)\n",
			  nt_errstr(status)));
	}

	if (auth_resp.length) {

		status = dcerpc_push_dcerpc_auth(pkt,
						 auth_type,
						 auth_info.auth_level,
						 0,
						 1, /* auth_context_id */
						 &auth_resp,
						 &auth_blob);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("Marshalling of dcerpc_auth failed.\n"));
			goto err_exit;
		}
	}

	/* Now that we have the auth len store it into the right place in
	 * the dcerpc header */
	dcerpc_set_frag_length(&p->out_data.frag,
				p->out_data.frag.length + auth_blob.length);

	if (auth_blob.length) {

		if (!data_blob_append(p->mem_ctx, &p->out_data.frag,
					auth_blob.data, auth_blob.length)) {
			DEBUG(0, ("Append of auth info failed.\n"));
			goto err_exit;
		}
	}

	/*
	 * Setup the lengths for the initial reply.
	 */

	p->out_data.data_sent_length = 0;
	p->out_data.current_pdu_sent = 0;

	TALLOC_FREE(auth_blob.data);
	return True;

  err_exit:

	data_blob_free(&p->out_data.frag);
	TALLOC_FREE(auth_blob.data);
	return setup_bind_nak(p, pkt);
}
Exemple #10
0
static NTSTATUS create_next_packet(TALLOC_CTX *mem_ctx,
				   struct pipe_auth_data *auth,
				   uint32_t call_id,
				   DATA_BLOB *rdata,
				   size_t data_sent_length,
				   DATA_BLOB *frag,
				   size_t *pdu_size)
{
	union dcerpc_payload u;
	uint8_t pfc_flags;
	size_t data_left;
	size_t data_to_send;
	size_t frag_len;
	size_t pad_len = 0;
	size_t auth_len = 0;
	NTSTATUS status;

	ZERO_STRUCT(u.response);

	/* Set up rpc packet pfc flags. */
	if (data_sent_length == 0) {
		pfc_flags = DCERPC_PFC_FLAG_FIRST;
	} else {
		pfc_flags = 0;
	}

	/* Work out how much we can fit in a single PDU. */
	data_left = rdata->length - data_sent_length;

	/* Ensure there really is data left to send. */
	if (!data_left) {
		DEBUG(0, ("No data left to send !\n"));
		return NT_STATUS_BUFFER_TOO_SMALL;
	}

	status = dcerpc_guess_sizes(auth,
				    DCERPC_RESPONSE_LENGTH,
				    data_left,
				    RPC_MAX_PDU_FRAG_LEN,
				    &data_to_send, &frag_len,
				    &auth_len, &pad_len);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/* Set up the alloc hint. This should be the data left to send. */
	u.response.alloc_hint = data_left;

	/* Work out if this PDU will be the last. */
	if (data_sent_length + data_to_send >= rdata->length) {
		pfc_flags |= DCERPC_PFC_FLAG_LAST;
	}

	/* Prepare data to be NDR encoded. */
	u.response.stub_and_verifier =
		data_blob_const(rdata->data + data_sent_length, data_to_send);

	/* Store the packet in the data stream. */
	status = dcerpc_push_ncacn_packet(mem_ctx, DCERPC_PKT_RESPONSE,
					  pfc_flags, auth_len, call_id,
					  &u, frag);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("Failed to marshall RPC Packet.\n"));
		return status;
	}

	if (auth_len) {
		/* Set the proper length on the pdu, including padding.
		 * Only needed if an auth trailer will be appended. */
		dcerpc_set_frag_length(frag, frag->length
						+ pad_len
						+ DCERPC_AUTH_TRAILER_LENGTH
						+ auth_len);
	}

	if (auth_len) {
		status = dcerpc_add_auth_footer(auth, pad_len, frag);
		if (!NT_STATUS_IS_OK(status)) {
			data_blob_free(frag);
			return status;
		}
	}

	*pdu_size = data_to_send;
	return NT_STATUS_OK;
}
Exemple #11
0
_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
{
	struct ndr_push *push;
	NTSTATUS status;
	DATA_BLOB stub;
	uint32_t total_length, chunk_size;
	struct dcesrv_connection_context *context = call->context;
	size_t sig_size = 0;

	/* call the reply function */
	status = context->iface->reply(call, call, call->r);
	if (!NT_STATUS_IS_OK(status)) {
		return dcesrv_fault(call, call->fault_code);
	}

	/* form the reply NDR */
	push = ndr_push_init_ctx(call);
	NT_STATUS_HAVE_NO_MEMORY(push);

	/* carry over the pointer count to the reply in case we are
	   using full pointer. See NDR specification for full
	   pointers */
	push->ptr_count = call->ndr_pull->ptr_count;

	if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
		push->flags |= LIBNDR_FLAG_BIGENDIAN;
	}

	status = context->iface->ndr_push(call, call, push, call->r);
	if (!NT_STATUS_IS_OK(status)) {
		return dcesrv_fault(call, call->fault_code);
	}

	stub = ndr_push_blob(push);

	total_length = stub.length;

	/* we can write a full max_recv_frag size, minus the dcerpc
	   request header size */
	chunk_size = call->conn->cli_max_recv_frag;
	chunk_size -= DCERPC_REQUEST_LENGTH;
	if (call->conn->auth_state.auth_info &&
	    call->conn->auth_state.gensec_security) {
		size_t max_payload = chunk_size;

		max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
		max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);

		sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
					   max_payload);
		if (sig_size) {
			chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
			chunk_size -= sig_size;
		}
	}
	chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);

	do {
		uint32_t length;
		struct data_blob_list_item *rep;
		struct ncacn_packet pkt;

		rep = talloc(call, struct data_blob_list_item);
		NT_STATUS_HAVE_NO_MEMORY(rep);

		length = MIN(chunk_size, stub.length);

		/* form the dcerpc response packet */
		dcesrv_init_hdr(&pkt,
				lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
		pkt.auth_length = 0;
		pkt.call_id = call->pkt.call_id;
		pkt.ptype = DCERPC_PKT_RESPONSE;
		pkt.pfc_flags = 0;
		if (stub.length == total_length) {
			pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
		}
		if (length == stub.length) {
			pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
		}
		pkt.u.response.alloc_hint = stub.length;
		pkt.u.response.context_id = call->pkt.u.request.context_id;
		pkt.u.response.cancel_count = 0;
		pkt.u.response._pad.data = call->pkt.u.request._pad.data;
		pkt.u.response._pad.length = call->pkt.u.request._pad.length;
		pkt.u.response.stub_and_verifier.data = stub.data;
		pkt.u.response.stub_and_verifier.length = length;

		if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
			return dcesrv_fault(call, DCERPC_FAULT_OTHER);
		}

		dcerpc_set_frag_length(&rep->blob, rep->blob.length);

		DLIST_ADD_END(call->replies, rep);

		stub.data += length;
		stub.length -= length;
	} while (stub.length != 0);

	/* move the call from the pending to the finished calls list */
	dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);

	if (call->conn->call_list && call->conn->call_list->replies) {
		if (call->conn->transport.report_output_data) {
			call->conn->transport.report_output_data(call->conn);
		}
	}

	return NT_STATUS_OK;
}