The branch, master has been updated via f69ed59 swrap: add basic support for fd-passing via SCM_RIGHTS via 66a5bef tests: Add test for socket_wrapper fd-passing support via 650115f swrap: always check new fd's against socket_fds_max and use libc_close() for cleanup via a79dec9 swrap: add const to swrap_add_socket_info() via c740bbe swrap: let swrap_sendmsg_before_unix() create a copy of msg_tmp.msg_control via de81995 swrap: add stubs for swrap_{sendmsg,recvmsg}_{before,after}_unix() via e030a18 swrap: filter out SCM_{RIGHTS,CREDENTIALS} on inet socket via 00f61ed swrap: add error checking/cleanup to swrap_sendmsg_filter_cmsghdr() via 00650ff swrap: Fix MSGHDR check in sendmsg() via 5b2a982 swrap: print out SOCKET_WRAPPER_PACKAGE and SOCKET_WRAPPER_VERSION on first use from ef90a76 src/socket_wrapper.c: Improve checks and debug output of socket_wrapper_dir()
https://git.samba.org/?p=socket_wrapper.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit f69ed59e64acac52c1f8b3f8c7a76c5db0bc5877 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 29 14:10:54 2020 +0200 swrap: add basic support for fd-passing via SCM_RIGHTS We only allow passing up to 6 fds in a single sendmsg call, in order to keep the logic simple. That's more than enough for Samba's use of fd-passing, there we only pass a single fd and the fd will be closed in the sender on success. It means it's ok to keep the socket_info.io.pck_{snd,rcv} fields per process and the PCAP generation will still work as expected. If these constraints turn out to be a problem for other applications, we need to change to a more complex design and move the socket_info array into a shared memory file and use shared robust mutexes. But for now we just want to support multi-channel testing in Samba. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11899 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 66a5bef7f9aac4d70124bc8efec72dcd434c660a Author: Anoop C S <anoo...@samba.org> Date: Thu Jul 23 15:59:29 2020 +0530 tests: Add test for socket_wrapper fd-passing support Signed-off-by: Anoop C S <anoo...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 650115f1b4ed2fd4aa7d65ce7578bc919e0601f4 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Jun 30 17:53:20 2020 +0200 swrap: always check new fd's against socket_fds_max and use libc_close() for cleanup Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit a79dec9866a0c42ff60c72ba04483818d4575127 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Jun 30 17:51:29 2020 +0200 swrap: add const to swrap_add_socket_info() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit c740bbe30affb7b94c6cca5f65a4e9047a1119ec Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 29 09:39:22 2020 +0200 swrap: let swrap_sendmsg_before_unix() create a copy of msg_tmp.msg_control With fd-passing we'll have to modify the content of it. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit de819958b00b48f285a1f31bfee1f8bee8e16827 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 29 09:27:24 2020 +0200 swrap: add stubs for swrap_{sendmsg,recvmsg}_{before,after}_unix() In order to implement fd-passing of socket_wrapper simulated sockets we need to modify the msghdr structures from the callers. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit e030a1897873f3cb24e7d5a35adab421a9d11b9f Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 29 13:07:26 2020 +0200 swrap: filter out SCM_{RIGHTS,CREDENTIALS} on inet socket These are only valid on unix domain sockets and ignored otherwise (at least on Linux). Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 00f61edc7363cd17435b559beb04b567200fb38f Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 29 10:12:07 2020 +0200 swrap: add error checking/cleanup to swrap_sendmsg_filter_cmsghdr() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 00650ffb0b913010aab7a004fa2d3eadf2f2f007 Author: Anoop C S <anoo...@redhat.com> Date: Wed May 22 10:45:37 2019 +0530 swrap: Fix MSGHDR check in sendmsg() Check for msg_controllen and msg_control data members from msghdr structure needs to be validated on the received omsg pointer rather than on newly created msghdr struture inside the wrapper. Signed-off-by: Anoop C S <anoo...@redhat.com> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 5b2a98203bbb0230785a65429338b52bd377971b Author: Stefan Metzmacher <me...@samba.org> Date: Thu Jan 28 12:40:14 2021 +0100 swrap: print out SOCKET_WRAPPER_PACKAGE and SOCKET_WRAPPER_VERSION on first use Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Anoop C S <anoo...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> ----------------------------------------------------------------------- Summary of changes: ConfigureChecks.cmake | 4 +- config.h.cmake | 4 +- doc/socket_wrapper.1 | 15 +- doc/socket_wrapper.1.txt | 5 +- src/socket_wrapper.c | 1042 ++++++++++++++++++++++++++++-- tests/CMakeLists.txt | 2 +- tests/test_echo_tcp_sendmsg_recvmsg_fd.c | 188 ++++++ 7 files changed, 1196 insertions(+), 64 deletions(-) create mode 100644 tests/test_echo_tcp_sendmsg_recvmsg_fd.c Changeset truncated at 500 lines: diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 3c8e615..bfb8c17 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -7,8 +7,8 @@ include(CheckStructHasMember) include(CheckPrototypeDefinition) include(TestBigEndian) -set(PACKAGE ${PROJECT_NAME}) -set(VERSION ${PROJECT_VERSION}) +set(SOCKET_WRAPPER_PACKAGE ${PROJECT_NAME}) +set(SOCKET_WRAPPER_VERSION ${PROJECT_VERSION}) set(BINARYDIR ${CMAKE_BINARY_DIR}) set(SOURCEDIR ${CMAKE_SOURCE_DIR}) diff --git a/config.h.cmake b/config.h.cmake index 38166d2..1148f74 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -1,8 +1,8 @@ /* Name of package */ -#cmakedefine PACKAGE "${PROJECT_NAME}" +#cmakedefine SOCKET_WRAPPER_PACKAGE "${SOCKET_WRAPPER_PACKAGE}" /* Version number of package */ -#cmakedefine VERSION "${PROJECT_VERSION}" +#cmakedefine SOCKET_WRAPPER_VERSION "${SOCKET_WRAPPER_VERSION}" #cmakedefine BINARYDIR "${BINARYDIR}" #cmakedefine SOURCEDIR "${SOURCEDIR}" diff --git a/doc/socket_wrapper.1 b/doc/socket_wrapper.1 index ec7f4b8..3bd171e 100644 --- a/doc/socket_wrapper.1 +++ b/doc/socket_wrapper.1 @@ -2,12 +2,12 @@ .\" Title: socket_wrapper .\" Author: Samba Team .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> -.\" Date: 2018-11-28 +.\" Date: 2021-02-01 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "SOCKET_WRAPPER" "1" "2018\-11\-28" "\ \&" "\ \&" +.TH "SOCKET_WRAPPER" "1" "2021\-02\-01" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -68,6 +68,17 @@ Support for IPv4 and IPv6 socket and addressing emulation\&. .\} Ability to capture network traffic in pcap format\&. .RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Passing IP sockets (up to 6) via SCM_RIGHTS is supported, but pcap support only works reliable if the socket is used by a single process at a time\&. +.RE .SH "ENVIRONMENT VARIABLES" .PP \fBSOCKET_WRAPPER_DIR\fR diff --git a/doc/socket_wrapper.1.txt b/doc/socket_wrapper.1.txt index d070fbf..425b08f 100644 --- a/doc/socket_wrapper.1.txt +++ b/doc/socket_wrapper.1.txt @@ -1,6 +1,6 @@ socket_wrapper(1) ================= -:revdate: 2018-11-28 +:revdate: 2021-02-01 :author: Samba Team NAME @@ -24,6 +24,9 @@ testing of complex network configurations. - Redirects all network communication to happen over Unix sockets. - Support for IPv4 and IPv6 socket and addressing emulation. - Ability to capture network traffic in pcap format. +- Passing IP sockets (up to 6) via SCM_RIGHTS is supported, + but pcap support only works reliable if the socket is used + by a single process at a time. ENVIRONMENT VARIABLES --------------------- diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c index 89454ca..14d0dda 100644 --- a/src/socket_wrapper.c +++ b/src/socket_wrapper.c @@ -302,6 +302,11 @@ static int first_free; struct socket_info { + /* + * Remember to update swrap_unix_scm_right_magic + * on any change. + */ + int family; int type; int protocol; @@ -313,6 +318,7 @@ struct socket_info int pktinfo; int tcp_nodelay; int listening; + int fd_passed; /* The unix path so we can unlink it on close() */ struct sockaddr_un un_addr; @@ -1672,6 +1678,10 @@ static void socket_wrapper_init_sockets(void) return; } + SWRAP_LOG(SWRAP_LOG_DEBUG, + "SOCKET_WRAPPER_PACKAGE[%s] SOCKET_WRAPPER_VERSION[%s]", + SOCKET_WRAPPER_PACKAGE, SOCKET_WRAPPER_VERSION); + /* * Intialize the static cache early before * any thread is able to start. @@ -1807,7 +1817,7 @@ static int find_socket_info_index(int fd) return socket_fds_idx[fd]; } -static int swrap_add_socket_info(struct socket_info *si_input) +static int swrap_add_socket_info(const struct socket_info *si_input) { struct socket_info *si = NULL; int si_index = -1; @@ -1850,6 +1860,7 @@ static int swrap_create_socket(struct socket_info *si, int fd) "trying to add %d", socket_fds_max, fd); + errno = EMFILE; return -1; } @@ -2960,7 +2971,7 @@ static int swrap_pcap_get_fd(const char *fname) file_hdr.link_type = 0x0065; /* 101 RAW IP */ if (write(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { - close(fd); + libc_close(fd); fd = -1; } return fd; @@ -3465,6 +3476,9 @@ static int swrap_socket(int family, int type, int protocol) ret = swrap_create_socket(si, fd); if (ret == -1) { + int saved_errno = errno; + libc_close(fd); + errno = saved_errno; return -1; } @@ -3635,7 +3649,7 @@ static int swrap_accept(int s, &in_addr.sa_socklen); if (ret == -1) { SWRAP_UNLOCK_SI(parent_si); - close(fd); + libc_close(fd); return ret; } @@ -3667,7 +3681,7 @@ static int swrap_accept(int s, &un_my_addr.sa.s, &un_my_addr.sa_socklen); if (ret == -1) { - close(fd); + libc_close(fd); return ret; } @@ -3678,7 +3692,7 @@ static int swrap_accept(int s, &in_my_addr.sa.s, &in_my_addr.sa_socklen); if (ret == -1) { - close(fd); + libc_close(fd); return ret; } @@ -3693,7 +3707,9 @@ static int swrap_accept(int s, idx = swrap_create_socket(&new_si, fd); if (idx == -1) { - close (fd); + int saved_errno = errno; + libc_close(fd); + errno = saved_errno; return -1; } @@ -4987,16 +5003,21 @@ static int swrap_msghdr_add_socket_info(struct socket_info *si, return rc; } -static int swrap_sendmsg_copy_cmsg(struct cmsghdr *cmsg, +static int swrap_sendmsg_copy_cmsg(const struct cmsghdr *cmsg, uint8_t **cm_data, size_t *cm_data_space); -static int swrap_sendmsg_filter_cmsg_socket(struct cmsghdr *cmsg, - uint8_t **cm_data, - size_t *cm_data_space); - -static int swrap_sendmsg_filter_cmsghdr(struct msghdr *msg, +static int swrap_sendmsg_filter_cmsg_ipproto_ip(const struct cmsghdr *cmsg, + uint8_t **cm_data, + size_t *cm_data_space); +static int swrap_sendmsg_filter_cmsg_sol_socket(const struct cmsghdr *cmsg, + uint8_t **cm_data, + size_t *cm_data_space); + +static int swrap_sendmsg_filter_cmsghdr(const struct msghdr *_msg, uint8_t **cm_data, - size_t *cm_data_space) { + size_t *cm_data_space) +{ + struct msghdr *msg = discard_const_p(struct msghdr, _msg); struct cmsghdr *cmsg; int rc = -1; @@ -5010,9 +5031,14 @@ static int swrap_sendmsg_filter_cmsghdr(struct msghdr *msg, cmsg = CMSG_NXTHDR(msg, cmsg)) { switch (cmsg->cmsg_level) { case IPPROTO_IP: - rc = swrap_sendmsg_filter_cmsg_socket(cmsg, - cm_data, - cm_data_space); + rc = swrap_sendmsg_filter_cmsg_ipproto_ip(cmsg, + cm_data, + cm_data_space); + break; + case SOL_SOCKET: + rc = swrap_sendmsg_filter_cmsg_sol_socket(cmsg, + cm_data, + cm_data_space); break; default: rc = swrap_sendmsg_copy_cmsg(cmsg, @@ -5020,12 +5046,19 @@ static int swrap_sendmsg_filter_cmsghdr(struct msghdr *msg, cm_data_space); break; } + if (rc < 0) { + int saved_errno = errno; + SAFE_FREE(*cm_data); + *cm_data_space = 0; + errno = saved_errno; + return rc; + } } return rc; } -static int swrap_sendmsg_copy_cmsg(struct cmsghdr *cmsg, +static int swrap_sendmsg_copy_cmsg(const struct cmsghdr *cmsg, uint8_t **cm_data, size_t *cm_data_space) { @@ -5048,14 +5081,14 @@ static int swrap_sendmsg_copy_cmsg(struct cmsghdr *cmsg, return 0; } -static int swrap_sendmsg_filter_cmsg_pktinfo(struct cmsghdr *cmsg, +static int swrap_sendmsg_filter_cmsg_pktinfo(const struct cmsghdr *cmsg, uint8_t **cm_data, size_t *cm_data_space); -static int swrap_sendmsg_filter_cmsg_socket(struct cmsghdr *cmsg, - uint8_t **cm_data, - size_t *cm_data_space) +static int swrap_sendmsg_filter_cmsg_ipproto_ip(const struct cmsghdr *cmsg, + uint8_t **cm_data, + size_t *cm_data_space) { int rc = -1; @@ -5081,7 +5114,7 @@ static int swrap_sendmsg_filter_cmsg_socket(struct cmsghdr *cmsg, return rc; } -static int swrap_sendmsg_filter_cmsg_pktinfo(struct cmsghdr *cmsg, +static int swrap_sendmsg_filter_cmsg_pktinfo(const struct cmsghdr *cmsg, uint8_t **cm_data, size_t *cm_data_space) { @@ -5095,7 +5128,832 @@ static int swrap_sendmsg_filter_cmsg_pktinfo(struct cmsghdr *cmsg, */ return 0; } + +static int swrap_sendmsg_filter_cmsg_sol_socket(const struct cmsghdr *cmsg, + uint8_t **cm_data, + size_t *cm_data_space) +{ + int rc = -1; + + switch (cmsg->cmsg_type) { + case SCM_RIGHTS: + SWRAP_LOG(SWRAP_LOG_TRACE, + "Ignoring SCM_RIGHTS on inet socket!"); + rc = 0; + break; +#ifdef SCM_CREDENTIALS + case SCM_CREDENTIALS: + SWRAP_LOG(SWRAP_LOG_TRACE, + "Ignoring SCM_CREDENTIALS on inet socket!"); + rc = 0; + break; +#endif /* SCM_CREDENTIALS */ + default: + rc = swrap_sendmsg_copy_cmsg(cmsg, + cm_data, + cm_data_space); + break; + } + + return rc; +} + +static const uint64_t swrap_unix_scm_right_magic = 0x8e0e13f27c42fc36; + +/* + * We only allow up to 6 fds at a time + * as that's more than enough for Samba + * and it means we can keep the logic simple + * and work with fixed size arrays. + * + * We also keep sizeof(struct swrap_unix_scm_rights) + * under PIPE_BUF (4096) in order to allow a non-blocking + * write into the pipe. + */ +#ifndef PIPE_BUF +#define PIPE_BUF 4096 +#endif +#define SWRAP_MAX_PASSED_FDS ((size_t)6) +#define SWRAP_MAX_PASSED_SOCKET_INFO SWRAP_MAX_PASSED_FDS +struct swrap_unix_scm_rights_payload { + uint8_t num_idxs; + int8_t idxs[SWRAP_MAX_PASSED_FDS]; + struct socket_info infos[SWRAP_MAX_PASSED_SOCKET_INFO]; +}; +struct swrap_unix_scm_rights { + uint64_t magic; + char package_name[sizeof(SOCKET_WRAPPER_PACKAGE)]; + char package_version[sizeof(SOCKET_WRAPPER_VERSION)]; + uint32_t full_size; + uint32_t payload_size; + struct swrap_unix_scm_rights_payload payload; +}; + +static void swrap_dec_fd_passed_array(size_t num, struct socket_info **array) +{ + int saved_errno = errno; + size_t i; + + for (i = 0; i < num; i++) { + struct socket_info *si = array[i]; + if (si == NULL) { + continue; + } + + SWRAP_LOCK_SI(si); + swrap_dec_refcount(si); + if (si->fd_passed > 0) { + si->fd_passed -= 1; + } + SWRAP_UNLOCK_SI(si); + array[i] = NULL; + } + + errno = saved_errno; +} + +static void swrap_undo_si_idx_array(size_t num, int *array) +{ + int saved_errno = errno; + size_t i; + + swrap_mutex_lock(&first_free_mutex); + + for (i = 0; i < num; i++) { + struct socket_info *si = NULL; + + if (array[i] == -1) { + continue; + } + + si = swrap_get_socket_info(array[i]); + if (si == NULL) { + continue; + } + + SWRAP_LOCK_SI(si); + swrap_dec_refcount(si); + SWRAP_UNLOCK_SI(si); + + swrap_set_next_free(si, first_free); + first_free = array[i]; + array[i] = -1; + } + + swrap_mutex_unlock(&first_free_mutex); + errno = saved_errno; +} + +static void swrap_close_fd_array(size_t num, const int *array) +{ + int saved_errno = errno; + size_t i; + + for (i = 0; i < num; i++) { + if (array[i] == -1) { + continue; + } + libc_close(array[i]); + } + + errno = saved_errno; +} + +union __swrap_fds { + const uint8_t *p; + int *fds; +}; + +union __swrap_cmsghdr { + const uint8_t *p; + struct cmsghdr *cmsg; +}; + +static int swrap_sendmsg_unix_scm_rights(const struct cmsghdr *cmsg, + uint8_t **cm_data, + size_t *cm_data_space, + int *scm_rights_pipe_fd) +{ + struct swrap_unix_scm_rights info; + struct swrap_unix_scm_rights_payload *payload = NULL; + int si_idx_array[SWRAP_MAX_PASSED_FDS]; + struct socket_info *si_array[SWRAP_MAX_PASSED_FDS] = { NULL, }; + size_t info_idx = 0; + size_t size_fds_in; + size_t num_fds_in; + union __swrap_fds __fds_in = { .p = NULL, }; + const int *fds_in = NULL; + size_t num_fds_out; + size_t size_fds_out; + union __swrap_fds __fds_out = { .p = NULL, }; + int *fds_out = NULL; + size_t cmsg_len; + size_t cmsg_space; + size_t new_cm_data_space; + union __swrap_cmsghdr __new_cmsg = { .p = NULL, }; + struct cmsghdr *new_cmsg = NULL; + uint8_t *p = NULL; + size_t i; + int pipefd[2] = { -1, -1 }; + int rc; + ssize_t sret; + + /* + * We pass this a buffer to the kernel make sure any padding + * is also cleared. + */ + ZERO_STRUCT(info); + info.magic = swrap_unix_scm_right_magic; + memcpy(info.package_name, + SOCKET_WRAPPER_PACKAGE, + sizeof(info.package_name)); + memcpy(info.package_version, + SOCKET_WRAPPER_VERSION, + sizeof(info.package_version)); + info.full_size = sizeof(info); + info.payload_size = sizeof(info.payload); + payload = &info.payload; + + if (*scm_rights_pipe_fd != -1) { + SWRAP_LOG(SWRAP_LOG_ERROR, + "Two SCM_RIGHTS headers are not supported by socket_wrapper"); + errno = EINVAL; + return -1; + } + + if (cmsg->cmsg_len < CMSG_LEN(0)) { + SWRAP_LOG(SWRAP_LOG_ERROR, + "cmsg->cmsg_len=%zu < CMSG_LEN(0)=%zu", + (size_t)cmsg->cmsg_len, + CMSG_LEN(0)); + errno = EINVAL; + return -1; + } + size_fds_in = cmsg->cmsg_len - CMSG_LEN(0); -- Socket Wrapper Repository