From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/rpc_server/rpc_sock_helper.c | 399 +++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 source3/rpc_server/rpc_sock_helper.c (limited to 'source3/rpc_server/rpc_sock_helper.c') 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 + * + * 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 . + */ + +#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 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