mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
31180e0e6d
More code, but will be more flexible in the future. Bug: https://bugzilla.samba.org/show_bug.cgi?id=15361 Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
833 lines
22 KiB
C
833 lines
22 KiB
C
/*
|
|
* Unix SMB/CIFS implementation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "source3/include/includes.h"
|
|
#include <spawn.h>
|
|
#include "local_np.h"
|
|
#include "lib/async_req/async_sock.h"
|
|
#include "librpc/gen_ndr/ndr_named_pipe_auth.h"
|
|
#include "libcli/named_pipe_auth/npa_tstream.h"
|
|
#include "libcli/named_pipe_auth/tstream_u32_read.h"
|
|
#include "lib/util/tevent_unix.h"
|
|
#include "auth/auth_util.h"
|
|
#include "libcli/security/dom_sid.h"
|
|
#include "libcli/security/security_token.h"
|
|
|
|
/**
|
|
* @file local_np.c
|
|
*
|
|
* Connect to a local named pipe by connecting to
|
|
* samba-dcerpcd. Start samba-dcerpcd if it isn't
|
|
* already running.
|
|
*/
|
|
|
|
extern bool override_logfile;
|
|
|
|
struct np_sock_connect_state {
|
|
struct tevent_context *ev;
|
|
struct samba_sockaddr addr;
|
|
const struct named_pipe_auth_req *npa_req;
|
|
struct named_pipe_auth_rep *npa_rep;
|
|
|
|
DATA_BLOB npa_blob;
|
|
struct iovec iov;
|
|
|
|
int sock;
|
|
struct tevent_req *subreq;
|
|
struct tstream_context *transport;
|
|
struct tstream_context *npa_stream;
|
|
};
|
|
|
|
static void np_sock_connect_cleanup(
|
|
struct tevent_req *req, enum tevent_req_state req_state);
|
|
static void np_sock_connect_before(void *private_data);
|
|
static void np_sock_connect_after(void *private_data);
|
|
static void np_sock_connect_connected(struct tevent_req *subreq);
|
|
static void np_sock_connect_written(struct tevent_req *subreq);
|
|
static void np_sock_connect_read_done(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *np_sock_connect_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const char *sockpath,
|
|
const struct named_pipe_auth_req *npa_req)
|
|
{
|
|
struct tevent_req *req = NULL;
|
|
struct np_sock_connect_state *state = NULL;
|
|
size_t len;
|
|
int ret;
|
|
bool ok;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct np_sock_connect_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->npa_req = npa_req;
|
|
state->sock = -1;
|
|
state->addr.u.un.sun_family = AF_UNIX;
|
|
|
|
state->npa_rep = talloc_zero(state, struct named_pipe_auth_rep);
|
|
if (tevent_req_nomem(state->npa_rep, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
tevent_req_set_cleanup_fn(req, np_sock_connect_cleanup);
|
|
|
|
state->addr.sa_socklen = sizeof(struct sockaddr_un);
|
|
len = strlcpy(state->addr.u.un.sun_path,
|
|
sockpath,
|
|
sizeof(state->addr.u.un.sun_path));
|
|
if (len >= sizeof(state->addr.u.un.sun_path)) {
|
|
tevent_req_error(req, ENAMETOOLONG);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (state->sock == -1) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
ret = set_blocking(state->sock, true);
|
|
if (ret == -1) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
ok = set_close_on_exec(state->sock);
|
|
if (!ok) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->subreq = async_connect_send(
|
|
state,
|
|
ev,
|
|
state->sock,
|
|
&state->addr.u.sa,
|
|
state->addr.sa_socklen,
|
|
np_sock_connect_before,
|
|
np_sock_connect_after,
|
|
NULL);
|
|
if (tevent_req_nomem(state->subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(state->subreq, np_sock_connect_connected, req);
|
|
|
|
return req;
|
|
}
|
|
|
|
static void np_sock_connect_cleanup(
|
|
struct tevent_req *req, enum tevent_req_state req_state)
|
|
{
|
|
struct np_sock_connect_state *state = tevent_req_data(
|
|
req, struct np_sock_connect_state);
|
|
|
|
TALLOC_FREE(state->subreq);
|
|
TALLOC_FREE(state->transport);
|
|
|
|
if (state->sock != -1) {
|
|
close(state->sock);
|
|
state->sock = -1;
|
|
}
|
|
}
|
|
|
|
static void np_sock_connect_before(void *private_data)
|
|
{
|
|
become_root();
|
|
}
|
|
|
|
static void np_sock_connect_after(void *private_data)
|
|
{
|
|
unbecome_root();
|
|
}
|
|
|
|
static void np_sock_connect_connected(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct np_sock_connect_state *state = tevent_req_data(
|
|
req, struct np_sock_connect_state);
|
|
enum ndr_err_code ndr_err;
|
|
int ret, err;
|
|
|
|
SMB_ASSERT(subreq == state->subreq);
|
|
|
|
ret = async_connect_recv(subreq, &err);
|
|
TALLOC_FREE(subreq);
|
|
state->subreq = NULL;
|
|
if (ret == -1) {
|
|
DBG_DEBUG("async_connect_recv returned %s\n", strerror(err));
|
|
tevent_req_error(req, err);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* As a quick workaround for bug 15310 we have done the
|
|
* connect in blocking mode (see np_sock_connect_send()). The
|
|
* rest of our code expects a nonblocking socket, activate
|
|
* this after the connect succeeded.
|
|
*/
|
|
ret = set_blocking(state->sock, false);
|
|
if (ret == -1) {
|
|
tevent_req_error(req, errno);
|
|
return;
|
|
}
|
|
|
|
ret = tstream_bsd_existing_socket(
|
|
state, state->sock, &state->transport);
|
|
if (ret == -1) {
|
|
err = errno;
|
|
DBG_DEBUG("tstream_bsd_existing_socket failed: %s\n",
|
|
strerror(err));
|
|
tevent_req_error(req, err);
|
|
return;
|
|
}
|
|
state->sock = -1;
|
|
|
|
ndr_err = ndr_push_struct_blob(
|
|
&state->npa_blob,
|
|
state,
|
|
state->npa_req,
|
|
(ndr_push_flags_fn_t)ndr_push_named_pipe_auth_req);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
DBG_DEBUG("ndr_push_struct_blob failed: %s\n",
|
|
ndr_errstr(ndr_err));
|
|
tevent_req_error(req, ndr_map_error2errno(ndr_err));
|
|
return;
|
|
}
|
|
state->iov = (struct iovec) {
|
|
.iov_base = state->npa_blob.data,
|
|
.iov_len = state->npa_blob.length,
|
|
};
|
|
|
|
subreq = tstream_writev_send(
|
|
state, state->ev, state->transport, &state->iov, 1);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, np_sock_connect_written, req);
|
|
}
|
|
|
|
static void np_sock_connect_written(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct np_sock_connect_state *state = tevent_req_data(
|
|
req, struct np_sock_connect_state);
|
|
int ret, err;
|
|
|
|
ret = tstream_writev_recv(subreq, &err);
|
|
TALLOC_FREE(subreq);
|
|
if (ret == -1) {
|
|
DBG_DEBUG("tstream_writev_recv returned %s\n", strerror(err));
|
|
tevent_req_error(req, err);
|
|
return;
|
|
}
|
|
|
|
subreq = tstream_u32_read_send(
|
|
state, state->ev, 0x00FFFFFF, state->transport);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, np_sock_connect_read_done, req);
|
|
}
|
|
|
|
static void np_sock_connect_read_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct np_sock_connect_state *state = tevent_req_data(
|
|
req, struct np_sock_connect_state);
|
|
DATA_BLOB in;
|
|
int ret;
|
|
enum ndr_err_code ndr_err;
|
|
|
|
ret = tstream_u32_read_recv(subreq, state, &in.data, &in.length);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_error(req, ret)) {
|
|
return;
|
|
}
|
|
|
|
ndr_err = ndr_pull_struct_blob_all(
|
|
&in,
|
|
state->npa_rep,
|
|
state->npa_rep,
|
|
(ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_rep);
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
DBG_DEBUG("ndr_pull_named_pipe_auth_rep failed: %s\n",
|
|
ndr_errstr(ndr_err));
|
|
tevent_req_error(req, ndr_map_error2errno(ndr_err));
|
|
return;
|
|
}
|
|
if (state->npa_rep->level != 6) {
|
|
DBG_DEBUG("npa level = %"PRIu32", expected 6\n",
|
|
state->npa_rep->level);
|
|
tevent_req_error(req, EIO);
|
|
return;
|
|
}
|
|
|
|
ret = tstream_npa_existing_stream(
|
|
state,
|
|
&state->transport,
|
|
state->npa_rep->info.info6.file_type,
|
|
&state->npa_stream);
|
|
if (ret == -1) {
|
|
ret = errno;
|
|
DBG_DEBUG("tstream_npa_existing_stream failed: %s\n",
|
|
strerror(ret));
|
|
tevent_req_error(req, ret);
|
|
return;
|
|
}
|
|
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static int np_sock_connect_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tstream_context **stream)
|
|
{
|
|
struct np_sock_connect_state *state = tevent_req_data(
|
|
req, struct np_sock_connect_state);
|
|
int err;
|
|
|
|
if (tevent_req_is_unix_error(req, &err)) {
|
|
tevent_req_received(req);
|
|
return err;
|
|
}
|
|
*stream = talloc_move(mem_ctx, &state->npa_stream);
|
|
tevent_req_received(req);
|
|
return 0;
|
|
}
|
|
|
|
struct start_rpc_host_state {
|
|
int ready_fd;
|
|
struct tevent_req *read_ready_req;
|
|
};
|
|
|
|
static void start_rpc_host_cleanup(
|
|
struct tevent_req *req, enum tevent_req_state req_state);
|
|
static void start_rpc_host_ready(struct tevent_req *subreq);
|
|
|
|
/*
|
|
* Start samba-dcerpcd and wait for it to report ready.
|
|
*/
|
|
static struct tevent_req *start_rpc_host_send(
|
|
TALLOC_CTX *mem_ctx, struct tevent_context *ev)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct start_rpc_host_state *state = NULL;
|
|
int ret;
|
|
int ready_fds[2] = { -1, -1 };
|
|
char **argv = NULL;
|
|
pid_t pid;
|
|
bool ok;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct start_rpc_host_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ret = pipe(ready_fds);
|
|
if (ret == -1) {
|
|
ret = errno;
|
|
DBG_DEBUG("pipe() failed: %s\n", strerror(ret));
|
|
goto fail;
|
|
}
|
|
|
|
ok = smb_set_close_on_exec(ready_fds[0]);
|
|
if (!ok) {
|
|
ret = errno;
|
|
DBG_DEBUG("smb_set_close_on_exec failed: %s\n",
|
|
strerror(ret));
|
|
goto fail;
|
|
}
|
|
|
|
argv = str_list_make_empty(mem_ctx);
|
|
str_list_add_printf(
|
|
&argv, "%s/samba-dcerpcd", get_dyn_SAMBA_LIBEXECDIR());
|
|
if (!is_default_dyn_CONFIGFILE()) {
|
|
str_list_add_printf(
|
|
&argv, "--configfile=%s", get_dyn_CONFIGFILE());
|
|
}
|
|
str_list_add_printf(&argv, "--libexec-rpcds");
|
|
str_list_add_printf(&argv, "--ready-signal-fd=%d", ready_fds[1]);
|
|
str_list_add_printf(&argv, "--np-helper");
|
|
str_list_add_printf(
|
|
&argv, "--debuglevel=%d", debuglevel_get_class(DBGC_RPC_SRV));
|
|
if (!is_default_dyn_LOGFILEBASE()) {
|
|
str_list_add_printf(
|
|
&argv, "--log-basename=%s", get_dyn_LOGFILEBASE());
|
|
}
|
|
if (argv == NULL) {
|
|
errno = ENOMEM;
|
|
goto fail;
|
|
}
|
|
|
|
become_root();
|
|
ret = posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
|
|
unbecome_root();
|
|
if (ret != 0) {
|
|
DBG_DEBUG("posix_spawn() failed: %s\n", strerror(ret));
|
|
goto fail;
|
|
}
|
|
|
|
state->ready_fd = ready_fds[0];
|
|
ready_fds[0] = -1;
|
|
tevent_req_set_cleanup_fn(req, start_rpc_host_cleanup);
|
|
|
|
close(ready_fds[1]);
|
|
ready_fds[1] = -1;
|
|
|
|
subreq = read_packet_send(state, ev, state->ready_fd, 1, NULL, NULL);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, start_rpc_host_ready, req);
|
|
return req;
|
|
|
|
fail:
|
|
if (ready_fds[0] == -1) {
|
|
close(ready_fds[0]);
|
|
ready_fds[0] = -1;
|
|
}
|
|
if (ready_fds[1] == -1) {
|
|
close(ready_fds[1]);
|
|
ready_fds[1] = -1;
|
|
}
|
|
tevent_req_error(req, ret);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
static void start_rpc_host_cleanup(
|
|
struct tevent_req *req, enum tevent_req_state req_state)
|
|
{
|
|
struct start_rpc_host_state *state = tevent_req_data(
|
|
req, struct start_rpc_host_state);
|
|
|
|
if (state->ready_fd != -1) {
|
|
close(state->ready_fd);
|
|
state->ready_fd = -1;
|
|
}
|
|
}
|
|
|
|
static void start_rpc_host_ready(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct start_rpc_host_state *state = tevent_req_data(
|
|
req, struct start_rpc_host_state);
|
|
uint8_t *buf;
|
|
int err;
|
|
ssize_t nread;
|
|
|
|
nread = read_packet_recv(subreq, state, &buf, &err);
|
|
TALLOC_FREE(subreq);
|
|
if (nread == -1) {
|
|
tevent_req_error(req, err);
|
|
return;
|
|
}
|
|
|
|
close(state->ready_fd);
|
|
state->ready_fd = -1;
|
|
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static int start_rpc_host_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_unix(req);
|
|
}
|
|
|
|
struct local_np_connect_state {
|
|
struct tevent_context *ev;
|
|
const char *socketpath;
|
|
struct named_pipe_auth_req *npa_req;
|
|
struct tstream_context *npa_stream;
|
|
};
|
|
|
|
static void local_np_connect_connected(struct tevent_req *subreq);
|
|
static void local_np_connect_started(struct tevent_req *subreq);
|
|
static void local_np_connect_retried(struct tevent_req *subreq);
|
|
|
|
/**
|
|
* @brief Async connect to a local named pipe RPC interface
|
|
*
|
|
* Start "samba-dcerpcd" on demand if it does not exist
|
|
*
|
|
* @param[in] mem_ctx The memory context to use.
|
|
* @param[in] ev The tevent context to use.
|
|
*
|
|
* @param[in] pipename The raw pipename to connect to without path
|
|
* @param[in] remote_client_name The client name to transmit
|
|
* @param[in] remote_client_addr The client addr to transmit
|
|
* @param[in] local_server_name The server name to transmit
|
|
* @param[in] local_server_addr The server addr to transmit
|
|
* @param[in] session_info The authorization info to use
|
|
* @param[in] need_idle_server Does this need to be an exclusive server?
|
|
* @return The tevent_req that was started
|
|
*/
|
|
|
|
struct tevent_req *local_np_connect_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const char *pipename,
|
|
enum dcerpc_transport_t transport,
|
|
const char *remote_client_name,
|
|
const struct tsocket_address *remote_client_addr,
|
|
const char *local_server_name,
|
|
const struct tsocket_address *local_server_addr,
|
|
const struct auth_session_info *session_info,
|
|
bool need_idle_server)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct local_np_connect_state *state = NULL;
|
|
struct named_pipe_auth_req_info6 *i6 = NULL;
|
|
const char *socket_dir = NULL;
|
|
char *lower_case_pipename = NULL;
|
|
struct dom_sid npa_sid = global_sid_Samba_NPA_Flags;
|
|
uint32_t npa_flags = 0;
|
|
struct security_token *token = NULL;
|
|
NTSTATUS status;
|
|
size_t num_npa_sids;
|
|
bool ok;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct local_np_connect_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
|
|
num_npa_sids =
|
|
security_token_count_flag_sids(session_info->security_token,
|
|
&npa_sid,
|
|
1,
|
|
NULL);
|
|
if (num_npa_sids != 0) {
|
|
DBG_ERR("ERROR: %zu NPA Flags SIDs have already been "
|
|
"detected in the security token!\n",
|
|
num_npa_sids);
|
|
tevent_req_error(req, EACCES);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
socket_dir = lp_parm_const_string(
|
|
GLOBAL_SECTION_SNUM, "external_rpc_pipe", "socket_dir",
|
|
lp_ncalrpc_dir());
|
|
if (socket_dir == NULL) {
|
|
DBG_DEBUG("external_rpc_pipe:socket_dir not set\n");
|
|
tevent_req_error(req, EINVAL);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
lower_case_pipename = strlower_talloc(state, pipename);
|
|
if (tevent_req_nomem(lower_case_pipename, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->socketpath = talloc_asprintf(
|
|
state, "%s/np/%s", socket_dir, lower_case_pipename);
|
|
if (tevent_req_nomem(state->socketpath, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
TALLOC_FREE(lower_case_pipename);
|
|
|
|
state->npa_req = talloc_zero(state, struct named_pipe_auth_req);
|
|
if (tevent_req_nomem(state->npa_req, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
state->npa_req->level = 6;
|
|
|
|
i6 = &state->npa_req->info.info6;
|
|
|
|
i6->transport = transport;
|
|
|
|
/* we don't have "int" in IDL, make sure we don't overflow */
|
|
SMB_ASSERT(i6->transport == transport);
|
|
|
|
if (remote_client_name == NULL) {
|
|
remote_client_name = get_myname(state->npa_req);
|
|
if (remote_client_name == NULL) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
}
|
|
i6->remote_client_name = remote_client_name;
|
|
|
|
if (remote_client_addr == NULL) {
|
|
struct tsocket_address *addr = NULL;
|
|
int ret = tsocket_address_inet_from_strings(
|
|
state->npa_req, "ip", NULL, 0, &addr);
|
|
if (ret != 0) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
remote_client_addr = addr;
|
|
}
|
|
i6->remote_client_addr = tsocket_address_inet_addr_string(
|
|
remote_client_addr, state->npa_req);
|
|
if (i6->remote_client_addr == NULL) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
i6->remote_client_port = tsocket_address_inet_port(remote_client_addr);
|
|
|
|
if (local_server_name == NULL) {
|
|
local_server_name = remote_client_name;
|
|
}
|
|
i6->local_server_name = local_server_name;
|
|
|
|
if (local_server_addr == NULL) {
|
|
struct tsocket_address *addr = NULL;
|
|
int ret = tsocket_address_inet_from_strings(
|
|
state->npa_req, "ip", NULL, 0, &addr);
|
|
if (ret != 0) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
local_server_addr = addr;
|
|
}
|
|
i6->local_server_addr = tsocket_address_inet_addr_string(
|
|
local_server_addr, state->npa_req);
|
|
if (i6->local_server_addr == NULL) {
|
|
tevent_req_error(req, errno);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
i6->local_server_port = tsocket_address_inet_port(local_server_addr);
|
|
|
|
i6->session_info = talloc_zero(
|
|
state->npa_req, struct auth_session_info_transport);
|
|
if (tevent_req_nomem(i6->session_info, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
i6->session_info->session_info = copy_session_info(
|
|
i6->session_info, session_info);
|
|
if (tevent_req_nomem(i6->session_info->session_info, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
if (need_idle_server) {
|
|
npa_flags |= SAMBA_NPA_FLAGS_NEED_IDLE;
|
|
}
|
|
|
|
ok = sid_append_rid(&npa_sid, npa_flags);
|
|
if (!ok) {
|
|
tevent_req_error(req, EINVAL);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
token = i6->session_info->session_info->security_token;
|
|
|
|
status = add_sid_to_array_unique(token,
|
|
&npa_sid,
|
|
&token->sids,
|
|
&token->num_sids);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
tevent_req_oom(req);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = np_sock_connect_send(
|
|
state, state->ev, state->socketpath, state->npa_req);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, local_np_connect_connected, req);
|
|
|
|
return req;
|
|
}
|
|
|
|
static void local_np_connect_connected(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct local_np_connect_state *state = tevent_req_data(
|
|
req, struct local_np_connect_state);
|
|
int ret;
|
|
|
|
ret = np_sock_connect_recv(subreq, state, &state->npa_stream);
|
|
TALLOC_FREE(subreq);
|
|
|
|
if (ret == 0) {
|
|
tevent_req_done(req);
|
|
return;
|
|
}
|
|
|
|
DBG_DEBUG("np_sock_connect failed: %s\n", strerror(ret));
|
|
|
|
if (!lp_rpc_start_on_demand_helpers()) {
|
|
/*
|
|
* samba-dcerpcd should already be started in
|
|
* daemon/standalone mode when "rpc start on demand
|
|
* helpers = false". We are prohibited from starting
|
|
* on demand as a named-pipe helper.
|
|
*/
|
|
DBG_ERR("Can't connect to a running samba-dcerpcd. smb.conf "
|
|
"config prohibits starting as named pipe helper as "
|
|
"the [global] section contains "
|
|
"\"rpc start on demand helpers = false\".\n");
|
|
tevent_req_error(req, ret);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* samba-dcerpcd isn't running. We need to start it.
|
|
* Note if it doesn't start we treat this as a fatal
|
|
* error for connecting to the named pipe and don't
|
|
* keep trying to restart for this connection.
|
|
*/
|
|
subreq = start_rpc_host_send(state, state->ev);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, local_np_connect_started, req);
|
|
}
|
|
|
|
static void local_np_connect_started(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct local_np_connect_state *state = tevent_req_data(
|
|
req, struct local_np_connect_state);
|
|
int ret;
|
|
|
|
ret = start_rpc_host_recv(subreq);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_error(req, ret)) {
|
|
DBG_DEBUG("start_rpc_host_recv failed: %s\n",
|
|
strerror(ret));
|
|
return;
|
|
}
|
|
|
|
subreq = np_sock_connect_send(
|
|
state, state->ev, state->socketpath, state->npa_req);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, local_np_connect_retried, req);
|
|
}
|
|
|
|
static void local_np_connect_retried(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct local_np_connect_state *state = tevent_req_data(
|
|
req, struct local_np_connect_state);
|
|
int ret;
|
|
|
|
ret = np_sock_connect_recv(subreq, state, &state->npa_stream);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_error(req, ret)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
/**
|
|
* @brief Receive handle to a local named pipe RPC interface
|
|
*
|
|
* @param[in] req The tevent_req that started the operation
|
|
* @param[in] ev The tevent context to use.
|
|
* @param[in] mem_ctx The memory context to put pstream on
|
|
* @param[out] pstream The established connection to the RPC server
|
|
*
|
|
* @return 0/errno
|
|
*/
|
|
|
|
int local_np_connect_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tstream_context **pstream)
|
|
{
|
|
struct local_np_connect_state *state = tevent_req_data(
|
|
req, struct local_np_connect_state);
|
|
int err;
|
|
|
|
if (tevent_req_is_unix_error(req, &err)) {
|
|
tevent_req_received(req);
|
|
return err;
|
|
}
|
|
|
|
*pstream = talloc_move(mem_ctx, &state->npa_stream);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Sync connect to a local named pipe RPC interface
|
|
*
|
|
* Start "samba-dcerpcd" on demand if it does not exist
|
|
*
|
|
* @param[in] pipename The raw pipename to connect to without path
|
|
* @param[in] remote_client_name The client name to transmit
|
|
* @param[in] remote_client_addr The client addr to transmit
|
|
* @param[in] local_server_name The server name to transmit
|
|
* @param[in] local_server_addr The server addr to transmit
|
|
* @param[in] session_info The authorization info to use
|
|
* @param[in] need_idle_server Does this need to be an exclusive server?
|
|
* @param[in] mem_ctx The memory context to use.
|
|
* @param[out] pstream The established connection to the RPC server
|
|
* @return 0/errno
|
|
*/
|
|
|
|
int local_np_connect(
|
|
const char *pipename,
|
|
enum dcerpc_transport_t transport,
|
|
const char *remote_client_name,
|
|
const struct tsocket_address *remote_client_addr,
|
|
const char *local_server_name,
|
|
const struct tsocket_address *local_server_addr,
|
|
const struct auth_session_info *session_info,
|
|
bool need_idle_server,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tstream_context **pstream)
|
|
{
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
int ret = ENOMEM;
|
|
|
|
ev = samba_tevent_context_init(mem_ctx);
|
|
if (ev == NULL) {
|
|
goto fail;
|
|
}
|
|
req = local_np_connect_send(
|
|
ev,
|
|
ev,
|
|
pipename,
|
|
transport,
|
|
remote_client_name,
|
|
remote_client_addr,
|
|
local_server_name,
|
|
local_server_addr,
|
|
session_info,
|
|
need_idle_server);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
if (!tevent_req_poll_unix(req, ev, &ret)) {
|
|
goto fail;
|
|
}
|
|
ret = local_np_connect_recv(req, mem_ctx, pstream);
|
|
fail:
|
|
TALLOC_FREE(req);
|
|
TALLOC_FREE(ev);
|
|
return ret;
|
|
}
|