diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source3/rpc_server/rpc_sock_helper.c | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/rpc_server/rpc_sock_helper.c')
-rw-r--r-- | source3/rpc_server/rpc_sock_helper.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/source3/rpc_server/rpc_sock_helper.c b/source3/rpc_server/rpc_sock_helper.c new file mode 100644 index 0000000..364b889 --- /dev/null +++ b/source3/rpc_server/rpc_sock_helper.c @@ -0,0 +1,399 @@ +/* + * 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: */ |