/** * @brief Append an auth footer according to what is the current mechanism * * @param auth The pipe_auth_data associated with the connection * @param pad_len The padding used in the packet * @param rpc_out Packet blob up to and including the auth header * * @return A NTSTATUS error code. */ NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth, size_t pad_len, DATA_BLOB *rpc_out) { struct gensec_security *gensec_security; char pad[CLIENT_NDR_PADDING_SIZE] = { 0, }; DATA_BLOB auth_info; DATA_BLOB auth_blob; NTSTATUS status; if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) { return NT_STATUS_OK; } if (pad_len) { /* Copy the sign/seal padding data. */ if (!data_blob_append(NULL, rpc_out, pad, pad_len)) { return NT_STATUS_NO_MEMORY; } } /* marshall the dcerpc_auth with an actually empty auth_blob. * This is needed because the ntmlssp signature includes the * auth header. We will append the actual blob later. */ auth_blob = data_blob_null; status = dcerpc_push_dcerpc_auth(rpc_out->data, auth->auth_type, auth->auth_level, pad_len, 1 /* context id. */, &auth_blob, &auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } /* append the header */ if (!data_blob_append(NULL, rpc_out, auth_info.data, auth_info.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_info.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_info); /* Generate any auth sign/seal and add the auth footer. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: status = NT_STATUS_OK; break; default: gensec_security = auth->auth_ctx; status = add_generic_auth_footer(gensec_security, auth->auth_level, rpc_out); break; } return status; }
/** * @brief Append an auth footer according to what is the current mechanism * * @param auth The pipe_auth_data associated with the connection * @param pad_len The padding used in the packet * @param rpc_out Packet blob up to and including the auth header * * @return A NTSTATUS error code. */ NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth, size_t pad_len, DATA_BLOB *rpc_out) { struct schannel_state *schannel_auth; struct auth_ntlmssp_state *ntlmssp_ctx; struct spnego_context *spnego_ctx; struct gse_context *gse_ctx; char pad[CLIENT_NDR_PADDING_SIZE] = { 0, }; DATA_BLOB auth_info; DATA_BLOB auth_blob; NTSTATUS status; if (auth->auth_type == DCERPC_AUTH_TYPE_NONE || auth->auth_type == DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM) { return NT_STATUS_OK; } if (pad_len) { /* Copy the sign/seal padding data. */ if (!data_blob_append(NULL, rpc_out, pad, pad_len)) { return NT_STATUS_NO_MEMORY; } } /* marshall the dcerpc_auth with an actually empty auth_blob. * This is needed because the ntmlssp signature includes the * auth header. We will append the actual blob later. */ auth_blob = data_blob_null; status = dcerpc_push_dcerpc_auth(rpc_out->data, auth->auth_type, auth->auth_level, pad_len, 1 /* context id. */, &auth_blob, &auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } /* append the header */ if (!data_blob_append(NULL, rpc_out, auth_info.data, auth_info.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_info.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_info); /* Generate any auth sign/seal and add the auth footer. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: status = NT_STATUS_OK; break; case DCERPC_AUTH_TYPE_SPNEGO: spnego_ctx = talloc_get_type_abort(auth->auth_ctx, struct spnego_context); status = add_spnego_auth_footer(spnego_ctx, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_NTLMSSP: ntlmssp_ctx = talloc_get_type_abort(auth->auth_ctx, struct auth_ntlmssp_state); status = add_ntlmssp_auth_footer(ntlmssp_ctx, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_SCHANNEL: schannel_auth = talloc_get_type_abort(auth->auth_ctx, struct schannel_state); status = add_schannel_auth_footer(schannel_auth, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_KRB5: gse_ctx = talloc_get_type_abort(auth->auth_ctx, struct gse_context); status = add_gssapi_auth_footer(gse_ctx, auth->auth_level, rpc_out); break; default: status = NT_STATUS_INVALID_PARAMETER; break; } return status; }
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); }
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); }