1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/rpc_server/rpc_sock_helper.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

400 lines
9.3 KiB
C
Raw Normal View History

/*
* Unix SMB/CIFS implementation.
*
* RPC Socket Helper
*
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
*
* 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 "includes.h"
#include "ntdomain.h"
#include "../lib/tsocket/tsocket.h"
#include "librpc/rpc/dcesrv_core.h"
#include "rpc_server/rpc_sock_helper.h"
#include "librpc/ndr/ndr_table.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
static NTSTATUS dcesrv_create_ncacn_np_socket(
struct dcerpc_binding *b, int *out_fd)
{
char *np_dir = NULL;
int fd = -1;
NTSTATUS status;
const char *endpoint;
char *endpoint_normalized = NULL;
char *p = NULL;
endpoint = dcerpc_binding_get_string_option(b, "endpoint");
if (endpoint == NULL) {
DBG_ERR("Endpoint mandatory for named pipes\n");
return NT_STATUS_INVALID_PARAMETER;
}
/* The endpoint string from IDL can be mixed uppercase and case is
* normalized by smbd on connection */
endpoint_normalized = strlower_talloc(talloc_tos(), endpoint);
if (endpoint_normalized == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* The endpoint string from IDL can be prefixed by \pipe\ */
p = endpoint_normalized;
if (strncmp(p, "\\pipe\\", 6) == 0) {
p += 6;
}
/*
* As lp_ncalrpc_dir() should have 0755, but
* lp_ncalrpc_dir()/np should have 0700, we need to
* create lp_ncalrpc_dir() first.
*/
if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
status = map_nt_error_from_unix_common(errno);
DBG_ERR("Failed to create pipe directory %s - %s\n",
lp_ncalrpc_dir(), strerror(errno));
goto out;
}
np_dir = talloc_asprintf(talloc_tos(), "%s/np", lp_ncalrpc_dir());
if (!np_dir) {
status = NT_STATUS_NO_MEMORY;
DBG_ERR("Out of memory\n");
goto out;
}
if (!directory_create_or_exist_strict(np_dir, geteuid(), 0700)) {
status = map_nt_error_from_unix_common(errno);
DBG_ERR("Failed to create pipe directory %s - %s\n",
np_dir, strerror(errno));
goto out;
}
fd = create_pipe_sock(np_dir, p, 0700);
if (fd == -1) {
status = map_nt_error_from_unix_common(errno);
DBG_ERR("Failed to create ncacn_np socket! '%s/%s': %s\n",
np_dir, p, strerror(errno));
goto out;
}
DBG_DEBUG("Opened pipe socket fd %d for %s\n", fd, p);
*out_fd = fd;
status = NT_STATUS_OK;
out:
TALLOC_FREE(endpoint_normalized);
TALLOC_FREE(np_dir);
return status;
}
/********************************************************************
* Start listening on the tcp/ip socket
********************************************************************/
static NTSTATUS dcesrv_create_ncacn_ip_tcp_socket(
const struct sockaddr_storage *ifss, uint16_t *port, int *out_fd)
{
int fd = -1;
if (*port == 0) {
static uint16_t low = 0;
uint16_t i;
if (low == 0) {
low = lp_rpc_low_port();
}
for (i = low; i <= lp_rpc_high_port(); i++) {
fd = open_socket_in(SOCK_STREAM, ifss, i, false);
if (fd >= 0) {
*port = i;
low = i+1;
break;
}
}
} else {
fd = open_socket_in(SOCK_STREAM, ifss, *port, true);
}
if (fd < 0) {
DBG_ERR("Failed to create socket on port %u!\n", *port);
return map_nt_error_from_unix(-fd);
}
/* ready to listen */
set_socket_options(fd, "SO_KEEPALIVE");
set_socket_options(fd, lp_socket_options());
DBG_DEBUG("Opened ncacn_ip_tcp socket fd %d for port %u\n", fd, *port);
*out_fd = fd;
return NT_STATUS_OK;
}
static NTSTATUS dcesrv_create_ncacn_ip_tcp_sockets(
struct dcerpc_binding *b,
TALLOC_CTX *mem_ctx,
size_t *pnum_fds,
int **pfds)
{
uint16_t port = 0;
char port_str[11];
const char *endpoint = NULL;
size_t i = 0, num_fds;
int *fds = NULL;
struct samba_sockaddr *addrs = NULL;
NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
bool ok;
endpoint = dcerpc_binding_get_string_option(b, "endpoint");
if (endpoint != NULL) {
port = atoi(endpoint);
}
if (lp_interfaces() && lp_bind_interfaces_only()) {
num_fds = iface_count();
} else {
num_fds = 1;
#ifdef HAVE_IPV6
num_fds += 1;
#endif
}
addrs = talloc_array(mem_ctx, struct samba_sockaddr, num_fds);
if (addrs == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
fds = talloc_array(mem_ctx, int, num_fds);
if (fds == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
/*
* Fill "addrs"
*/
if (lp_interfaces() && lp_bind_interfaces_only()) {
for (i=0; i<num_fds; i++) {
const struct sockaddr_storage *ifss =
iface_n_sockaddr_storage(i);
ok = sockaddr_storage_to_samba_sockaddr(
&addrs[i], ifss);
if (!ok) {
i = 0; /* nothing to close */
goto fail;
}
}
} else {
struct sockaddr_storage ss = { .ss_family = 0 };
#ifdef HAVE_IPV6
ok = interpret_string_addr(
&ss, "::", AI_NUMERICHOST|AI_PASSIVE);
if (!ok) {
goto fail;
}
ok = sockaddr_storage_to_samba_sockaddr(&addrs[0], &ss);
if (!ok) {
goto fail;
}
#endif
ok = interpret_string_addr(
&ss, "0.0.0.0", AI_NUMERICHOST|AI_PASSIVE);
if (!ok) {
goto fail;
}
/* num_fds set above depending on HAVE_IPV6 */
ok = sockaddr_storage_to_samba_sockaddr(
&addrs[num_fds-1], &ss);
if (!ok) {
goto fail;
}
}
for (i=0; i<num_fds; i++) {
status = dcesrv_create_ncacn_ip_tcp_socket(
&addrs[i].u.ss, &port, &fds[i]);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
samba_sockaddr_set_port(&addrs[i], port);
}
/* Set the port in the endpoint */
snprintf(port_str, sizeof(port_str), "%"PRIu16, port);
status = dcerpc_binding_set_string_option(b, "endpoint", port_str);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("Failed to set binding endpoint '%s': %s\n",
port_str, nt_errstr(status));
goto fail;
}
TALLOC_FREE(addrs);
*pfds = fds;
*pnum_fds = num_fds;
return NT_STATUS_OK;
fail:
while (i > 0) {
close(fds[i-1]);
i -= 1;
}
TALLOC_FREE(fds);
TALLOC_FREE(addrs);
return status;
}
/********************************************************************
* Start listening on the ncalrpc socket
********************************************************************/
static NTSTATUS dcesrv_create_ncalrpc_socket(
struct dcerpc_binding *b, int *out_fd)
{
int fd = -1;
const char *endpoint = NULL;
NTSTATUS status;
endpoint = dcerpc_binding_get_string_option(b, "endpoint");
if (endpoint == NULL) {
/*
* No identifier specified: use DEFAULT or SMBD.
*
* When role is AD DC we run two rpc server instances, the one
* started by 'samba' and the one embedded in 'smbd'.
* Avoid listening in DEFAULT socket for NCALRPC as both
* servers will race to accept connections. In this case smbd
* will listen in SMBD socket and rpcint binding handle
* implementation will pick the right socket to use.
*
* TODO: DO NOT hardcode this value anywhere else. Rather,
* specify no endpoint and let the epmapper worry about it.
*/
if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
endpoint = "SMBD";
} else {
endpoint = "DEFAULT";
}
status = dcerpc_binding_set_string_option(
b, "endpoint", endpoint);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("Failed to set ncalrpc 'endpoint' binding "
"string option to '%s': %s\n",
endpoint, nt_errstr(status));
return status;
}
}
if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
status = map_nt_error_from_unix_common(errno);
DBG_ERR("Failed to create ncalrpc directory '%s': %s\n",
lp_ncalrpc_dir(), strerror(errno));
goto out;
}
fd = create_pipe_sock(lp_ncalrpc_dir(), endpoint, 0755);
if (fd == -1) {
status = map_nt_error_from_unix_common(errno);
DBG_ERR("Failed to create ncalrpc socket '%s/%s': %s\n",
lp_ncalrpc_dir(), endpoint, strerror(errno));
goto out;
}
DBG_DEBUG("Opened ncalrpc socket fd '%d' for '%s/%s'\n",
fd, lp_ncalrpc_dir(), endpoint);
*out_fd = fd;
return NT_STATUS_OK;
out:
return status;
}
NTSTATUS dcesrv_create_binding_sockets(
struct dcerpc_binding *b,
TALLOC_CTX *mem_ctx,
size_t *pnum_fds,
int **pfds)
{
enum dcerpc_transport_t transport = dcerpc_binding_get_transport(b);
size_t i, num_fds = 1;
int *fds = NULL;
NTSTATUS status;
if ((transport == NCALRPC) || (transport == NCACN_NP)) {
fds = talloc(mem_ctx, int);
if (fds == NULL) {
return NT_STATUS_NO_MEMORY;
}
}
switch(transport) {
case NCALRPC:
status = dcesrv_create_ncalrpc_socket(b, fds);
break;
case NCACN_NP:
status = dcesrv_create_ncacn_np_socket(b, fds);
break;
case NCACN_IP_TCP:
status = dcesrv_create_ncacn_ip_tcp_sockets(
b, talloc_tos(), &num_fds, &fds);
break;
default:
status = NT_STATUS_NOT_SUPPORTED;
break;
}
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(fds);
return status;
}
for (i=0; i<num_fds; i++) {
bool ok = smb_set_close_on_exec(fds[i]);
if (!ok) {
status = map_nt_error_from_unix(errno);
break;
}
}
if (i < num_fds) {
for (i=0; i<num_fds; i++) {
close(fds[i]);
}
TALLOC_FREE(fds);
return status;
}
*pfds = fds;
*pnum_fds = num_fds;
return NT_STATUS_OK;
}
/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */