diff --git a/CMakeLists.txt b/CMakeLists.txt index 13be7fb58b0a755d094017323b48677cd121e2f8..8927ebb56e8d371a03c09f83b0439990246d0b0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,16 @@ install( pkgconfig ) +configure_file(socket_wrapper_noop.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/socket_wrapper_noop.pc @ONLY) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/socket_wrapper_noop.pc + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/pkgconfig + COMPONENT + pkgconfig +) + # cmake config files configure_file(socket_wrapper-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/socket_wrapper-config-version.cmake @ONLY) configure_file(socket_wrapper-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/socket_wrapper-config.cmake @ONLY) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index bfb8c177fd66f84340c28821f96ceb0f897c30bc..2c78b83f168dabde65f1c1b20dc7c03bc0ff0b31 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -74,6 +74,7 @@ check_function_exists(getexecname HAVE_GETEXECNAME) check_function_exists(pledge HAVE_PLEDGE) check_function_exists(_socket HAVE__SOCKET) check_function_exists(_close HAVE__CLOSE) +check_function_exists(__close_nocancel HAVE___CLOSE_NOCANCEL) if (UNIX) find_library(DLFCN_LIBRARY dl) diff --git a/config.h.cmake b/config.h.cmake index 1148f749c639fb189931b9b1c5208983e7d6916e..0f2fb09e58b224c13f9e6b47661dc9faf59b0525 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -47,6 +47,7 @@ #cmakedefine HAVE_PLEDGE 1 #cmakedefine HAVE__SOCKET 1 #cmakedefine HAVE__CLOSE 1 +#cmakedefine HAVE___CLOSE_NOCANCEL 1 #cmakedefine HAVE_ACCEPT_PSOCKLEN_T 1 #cmakedefine HAVE_IOCTL_INT 1 diff --git a/doc/socket_wrapper.1 b/doc/socket_wrapper.1 index 0272c63d30d5053eced8cbf6d4cbb32f2675aab2..c98822790aa3d9ae242f1b7bf585966f4476e455 100644 --- a/doc/socket_wrapper.1 +++ b/doc/socket_wrapper.1 @@ -1,13 +1,13 @@ '\" t .\" Title: socket_wrapper .\" Author: Samba Team -.\" Generator: Asciidoctor 2.0.12 -.\" Date: 2021-02-02 +.\" Generator: Asciidoctor 2.0.10 +.\" Date: 2021-02-24 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "SOCKET_WRAPPER" "1" "2021-02-02" "\ \&" "\ \&" +.TH "SOCKET_WRAPPER" "1" "2021-02-24" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -214,54 +214,142 @@ to be usable. .sp .if n .RS 4 .nf -.fam C # Open a console and create a directory for the unix sockets. $ mktemp \-d /tmp/tmp.bQRELqDrhM -.fam .fi .if n .RE .sp .if n .RS 4 .nf -.fam C # Then start nc to listen for network traffic using the temporary directory. $ LD_PRELOAD=libsocket_wrapper.so \(rs SOCKET_WRAPPER_DIR=/tmp/tmp.bQRELqDrhM \(rs SOCKET_WRAPPER_DEFAULT_IFACE=10 nc \-v \-l 127.0.0.10 7 -.fam .fi .if n .RE .sp .if n .RS 4 .nf -.fam C # (If nc, listens on 0.0.0.0 then listener will be open on 127.0.0.10 because # it is the default interface) -.fam .fi .if n .RE .sp .if n .RS 4 .nf -.fam C # Now open another console and start \(aqnc\(aq as a client to connect to the server: $ LD_PRELOAD=libsocket_wrapper.so \(rs SOCKET_WRAPPER_DIR=/tmp/tmp.bQRELqDrhM \(rs SOCKET_WRAPPER_DEFAULT_IFACE=100 nc \-v 127.0.0.10 7 -.fam .fi .if n .RE .sp .if n .RS 4 .nf -.fam C # (The client will use the address 127.0.0.100 when connecting to the server) # Now you can type \(aqHello!\(aq which will be sent to the server and should appear # in the console output of the server. -.fam .fi .if n .RE +.SH "PUBLIC FUNCTIONS" +.sp +Socket wrapper advanced helpers. +.sp +Applications with the need to alter their behaviour when +socket wrapper is active, can link use these functions. +.sp +By default it\(cqs required for applications to use any of these +functions as libsocket_wrapper.so is injected at runtime via +LD_PRELOAD. +.sp +Applications using these functions should link against +libsocket_wrapper_noop.so by using \-lsocket_wrapper_noop, +or implement their own noop stubs. +.sp +#include +.sp +bool socket_wrapper_enabled(void); +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +This returns true when socket wrapper is actively in use. +.RE +.sp +void socket_wrapper_indicate_no_inet_fd(int fd); +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +This allows socket_wrapper aware applications to +indicate that the given fd does not belong to +an inet socket. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +socket_wrapper may not be able to intercept the __close_nocancel() +syscall made from within libc.so. As result it\(cqs possible +that the in memory meta date of socket_wrapper references +stale file descriptors, which are already reused for unrelated +kernel objects, e.g. files, directories, ... +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Socket wrapper already intercepts a lot of unrelated +functions like eventfd(), timerfd_create(), ... in order +to remove stale meta data for the returned fd, but +it will never be able to handle all possible syscalls. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +socket_wrapper_indicate_no_inet_fd() gives applications a way +to do the same, explicitly without waiting for new syscalls to +be added to libsocket_wrapper.so. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +This is a no\-op if socket_wrapper is not in use or +if the there is no in memory meta data for the given fd. +.RE .SH "RESOURCES" .sp \fBProject web site:\fP \c diff --git a/doc/socket_wrapper.1.adoc b/doc/socket_wrapper.1.adoc index 9519fd4113e873c9bbe73926698a9a9a6527519d..39c46eee8aaf5b482356dfeee12c8b723aaa972a 100644 --- a/doc/socket_wrapper.1.adoc +++ b/doc/socket_wrapper.1.adoc @@ -133,6 +133,54 @@ EXAMPLE # Now you can type 'Hello!' which will be sent to the server and should appear # in the console output of the server. +PUBLIC FUNCTIONS +---------------- + +Socket wrapper advanced helpers. + +Applications with the need to alter their behaviour when +socket wrapper is active, can link use these functions. + +By default it's required for applications to use any of these +functions as libsocket_wrapper.so is injected at runtime via +LD_PRELOAD. + +Applications using these functions should link against +libsocket_wrapper_noop.so by using -lsocket_wrapper_noop, +or implement their own noop stubs. + +#include + +bool socket_wrapper_enabled(void); + +- This returns true when socket wrapper is actively in use. + + +void socket_wrapper_indicate_no_inet_fd(int fd); + +- This allows socket_wrapper aware applications to + indicate that the given fd does not belong to + an inet socket. + +- socket_wrapper may not be able to intercept the __close_nocancel() + syscall made from within libc.so. As result it's possible + that the in memory meta date of socket_wrapper references + stale file descriptors, which are already reused for unrelated + kernel objects, e.g. files, directories, ... + +- Socket wrapper already intercepts a lot of unrelated + functions like eventfd(), timerfd_create(), ... in order + to remove stale meta data for the returned fd, but + it will never be able to handle all possible syscalls. + +- socket_wrapper_indicate_no_inet_fd() gives applications a way + to do the same, explicitly without waiting for new syscalls to + be added to libsocket_wrapper.so. + +- This is a no-op if socket_wrapper is not in use or + if the there is no in memory meta data for the given fd. + + RESOURCES --------- diff --git a/socket_wrapper_noop.pc.cmake b/socket_wrapper_noop.pc.cmake new file mode 100644 index 0000000000000000000000000000000000000000..5c8ac499c9695fb9e1ef15f4279caabc297bb035 --- /dev/null +++ b/socket_wrapper_noop.pc.cmake @@ -0,0 +1,8 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ +Description: The socket_wrapper_noop library +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -lsocket_wrapper_noop +Cflags: -I${includedir} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 73f8cd0836f4bd6f2b8c243cbbd1828fa7de2b45..ac56c8618f6cca1e9da1abd909ede4bcc36f35d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,3 +27,35 @@ install(TARGETS socket_wrapper ) set(SOCKET_WRAPPER_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}socket_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" PARENT_SCOPE) + +add_library(socket_wrapper_noop SHARED socket_wrapper_noop.c) +target_include_directories(socket_wrapper_noop + PRIVATE + ${CMAKE_BINARY_DIR}) +target_compile_options(socket_wrapper_noop + PRIVATE + ${DEFAULT_C_COMPILE_FLAGS} + -D_GNU_SOURCE) +target_link_libraries(socket_wrapper_noop ${SWRAP_REQUIRED_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +set_target_properties(socket_wrapper_noop + PROPERTIES + VERSION ${LIBRARY_VERSION} + SOVERSION ${LIBRARY_SOVERSION}) +if (DEFINED DEFAULT_LINK_FLAGS) + set_target_properties(socket_wrapper_noop + PROPERTIES + LINK_FLAGS ${DEFAULT_LINK_FLAGS}) +endif() + +install(TARGETS socket_wrapper_noop + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/socket_wrapper.h + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR} +) diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c index a950a0a0dbc882e8ca8b95278bcd1f013b0036ab..44cfad8c6cfd4790ba82a3a76cc91799a28c7466 100644 --- a/src/socket_wrapper.c +++ b/src/socket_wrapper.c @@ -2,8 +2,8 @@ * BSD 3-Clause License * * Copyright (c) 2005-2008, Jelmer Vernooij - * Copyright (c) 2006-2018, Stefan Metzmacher - * Copyright (c) 2013-2018, Andreas Schneider + * Copyright (c) 2006-2021, Stefan Metzmacher + * Copyright (c) 2013-2021, Andreas Schneider * Copyright (c) 2014-2017, Michael Adam * Copyright (c) 2016-2018, Anoop C S * All rights reserved. @@ -86,6 +86,8 @@ #endif #include +#include "socket_wrapper.h" + enum swrap_dbglvl_e { SWRAP_LOG_ERROR = 0, SWRAP_LOG_WARN, @@ -370,7 +372,7 @@ static pthread_mutex_t autobind_start_mutex = PTHREAD_MUTEX_INITIALIZER; /* Mutex to guard the initialization of array of socket_info structures */ static pthread_mutex_t sockets_mutex = PTHREAD_MUTEX_INITIALIZER; -/* Mutex to guard the socket reset in swrap_close() and swrap_remove_stale() */ +/* Mutex to guard the socket reset in swrap_remove_wrapper() */ static pthread_mutex_t socket_reset_mutex = PTHREAD_MUTEX_INITIALIZER; /* Mutex to synchronize access to first free index in socket_info array */ @@ -392,8 +394,6 @@ static pthread_mutex_t mtu_update_mutex = PTHREAD_MUTEX_INITIALIZER; /* Function prototypes */ -bool socket_wrapper_enabled(void); - #if ! defined(HAVE_CONSTRUCTOR_ATTRIBUTE) && defined(HAVE_PRAGMA_INIT) /* xlC and other oldschool compilers support (only) this */ #pragma init (swrap_constructor) @@ -492,6 +492,9 @@ typedef int (*__libc_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); typedef int (*__libc_close)(int fd); +#ifdef HAVE___CLOSE_NOCANCEL +typedef int (*__libc___close_nocancel)(int fd); +#endif typedef int (*__libc_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); @@ -572,6 +575,9 @@ struct swrap_libc_symbols { #endif SWRAP_SYMBOL_ENTRY(bind); SWRAP_SYMBOL_ENTRY(close); +#ifdef HAVE___CLOSE_NOCANCEL + SWRAP_SYMBOL_ENTRY(__close_nocancel); +#endif SWRAP_SYMBOL_ENTRY(connect); SWRAP_SYMBOL_ENTRY(dup); SWRAP_SYMBOL_ENTRY(dup2); @@ -851,6 +857,15 @@ static int libc_close(int fd) return swrap.libc.symbols._libc_close.f(fd); } +#ifdef HAVE___CLOSE_NOCANCEL +static int libc___close_nocancel(int fd) +{ + swrap_bind_symbol_all(); + + return swrap.libc.symbols._libc___close_nocancel.f(fd); +} +#endif /* HAVE___CLOSE_NOCANCEL */ + static int libc_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) @@ -1199,6 +1214,9 @@ static void __swrap_bind_symbol_all_once(void) #endif swrap_bind_symbol_libsocket(bind); swrap_bind_symbol_libc(close); +#ifdef HAVE___CLOSE_NOCANCEL + swrap_bind_symbol_libc(__close_nocancel); +#endif swrap_bind_symbol_libsocket(connect); swrap_bind_symbol_libc(dup); swrap_bind_symbol_libc(dup2); @@ -2027,6 +2045,13 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i type = u_type; iface = (addr & 0x000000FF); } else { + char str[256] = {0,}; + inet_ntop(inaddr->sa_family, + &in->sin_addr, + str, sizeof(str)); + SWRAP_LOG(SWRAP_LOG_WARN, + "str[%s] prt[%u]", + str, (unsigned)prt); errno = ENETUNREACH; return -1; } @@ -2062,6 +2087,13 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) { iface = in->sin6_addr.s6_addr[15]; } else { + char str[256] = {0,}; + inet_ntop(inaddr->sa_family, + &in->sin6_addr, + str, sizeof(str)); + SWRAP_LOG(SWRAP_LOG_WARN, + "str[%s] prt[%u]", + str, (unsigned)prt); errno = ENETUNREACH; return -1; } @@ -2390,46 +2422,7 @@ static bool check_addr_port_in_use(const struct sockaddr *sa, socklen_t len) } #endif -static void swrap_remove_stale(int fd) -{ - struct socket_info *si; - int si_index; - - SWRAP_LOG(SWRAP_LOG_TRACE, "remove stale wrapper for %d", fd); - - swrap_mutex_lock(&socket_reset_mutex); - - si_index = find_socket_info_index(fd); - if (si_index == -1) { - swrap_mutex_unlock(&socket_reset_mutex); - return; - } - - reset_socket_info_index(fd); - - si = swrap_get_socket_info(si_index); - - swrap_mutex_lock(&first_free_mutex); - SWRAP_LOCK_SI(si); - - swrap_dec_refcount(si); - - if (swrap_get_refcount(si) > 0) { - goto out; - } - - if (si->un_addr.sun_path[0] != '\0') { - unlink(si->un_addr.sun_path); - } - - swrap_set_next_free(si, first_free); - first_free = si_index; - -out: - SWRAP_UNLOCK_SI(si); - swrap_mutex_unlock(&first_free_mutex); - swrap_mutex_unlock(&socket_reset_mutex); -} +static void swrap_remove_stale(int fd); static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, @@ -2990,7 +2983,7 @@ static int swrap_pcap_get_fd(const char *fname) file_hdr.frame_max_len = SWRAP_FRAME_LENGTH_MAX; file_hdr.link_type = 0x0065; /* 101 RAW IP */ - if (write(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { + if (libc_write(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { libc_close(fd); fd = -1; } @@ -3325,7 +3318,7 @@ static void swrap_pcap_dump_packet(struct socket_info *si, fd = swrap_pcap_get_fd(file_name); if (fd != -1) { - if (write(fd, packet, packet_len) != (ssize_t)packet_len) { + if (libc_write(fd, packet, packet_len) != (ssize_t)packet_len) { free(packet); goto done; } @@ -5532,7 +5525,7 @@ static int swrap_sendmsg_unix_scm_rights(const struct cmsghdr *cmsg, return -1; } - sret = write(pipefd[1], &info, sizeof(info)); + sret = libc_write(pipefd[1], &info, sizeof(info)); if (sret != sizeof(info)) { int saved_errno = errno; if (sret != -1) { @@ -7403,10 +7396,13 @@ ssize_t writev(int s, const struct iovec *vector, int count) * CLOSE ***************************/ -static int swrap_close(int fd) +static int swrap_remove_wrapper(const char *__func_name, + int (*__close_fd_fn)(int fd), + int fd) { struct socket_info *si = NULL; int si_index; + int ret_errno = errno; int ret; swrap_mutex_lock(&socket_reset_mutex); @@ -7414,10 +7410,10 @@ static int swrap_close(int fd) si_index = find_socket_info_index(fd); if (si_index == -1) { swrap_mutex_unlock(&socket_reset_mutex); - return libc_close(fd); + return __close_fd_fn(fd); } - SWRAP_LOG(SWRAP_LOG_TRACE, "Close wrapper for fd=%d", fd); + swrap_log(SWRAP_LOG_TRACE, __func_name, "Remove wrapper for fd=%d", fd); reset_socket_info_index(fd); si = swrap_get_socket_info(si_index); @@ -7425,7 +7421,10 @@ static int swrap_close(int fd) swrap_mutex_lock(&first_free_mutex); SWRAP_LOCK_SI(si); - ret = libc_close(fd); + ret = __close_fd_fn(fd); + if (ret == -1) { + ret_errno = errno; + } swrap_dec_refcount(si); @@ -7460,14 +7459,68 @@ out: swrap_mutex_unlock(&first_free_mutex); swrap_mutex_unlock(&socket_reset_mutex); + errno = ret_errno; return ret; } +static int swrap_noop_close(int fd) +{ + (void)fd; /* unused */ + return 0; +} + +static void swrap_remove_stale(int fd) +{ + swrap_remove_wrapper(__func__, swrap_noop_close, fd); +} + +/* + * This allows socket_wrapper aware applications to + * indicate that the given fd does not belong to + * an inet socket. + * + * We already overload a lot of unrelated functions + * like eventfd(), timerfd_create(), ... in order to + * call swrap_remove_stale() on the returned fd, but + * we'll never be able to handle all possible syscalls. + * + * socket_wrapper_indicate_no_inet_fd() gives them a way + * to do the same. + * + * We don't export swrap_remove_stale() in order to + * make it easier to analyze SOCKET_WRAPPER_DEBUGLEVEL=3 + * log files. + */ +void socket_wrapper_indicate_no_inet_fd(int fd) +{ + swrap_remove_wrapper(__func__, swrap_noop_close, fd); +} + +static int swrap_close(int fd) +{ + return swrap_remove_wrapper(__func__, libc_close, fd); +} + int close(int fd) { return swrap_close(fd); } +#ifdef HAVE___CLOSE_NOCANCEL + +static int swrap___close_nocancel(int fd) +{ + return swrap_remove_wrapper(__func__, libc___close_nocancel, fd); +} + +int __close_nocancel(int fd); +int __close_nocancel(int fd) +{ + return swrap___close_nocancel(fd); +} + +#endif /* HAVE___CLOSE_NOCANCEL */ + /**************************** * DUP ***************************/ diff --git a/src/socket_wrapper.h b/src/socket_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..3ec5031f7d16d66b7f7fff23476ff55cec90d656 --- /dev/null +++ b/src/socket_wrapper.h @@ -0,0 +1,89 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2005-2008, Jelmer Vernooij + * Copyright (c) 2006-2021, Stefan Metzmacher + * Copyright (c) 2013-2021, Andreas Schneider + * Copyright (c) 2014-2017, Michael Adam + * Copyright (c) 2016-2018, Anoop C S + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __SOCKET_WRAPPER_H__ +#define __SOCKET_WRAPPER_H__ 1 + +#include + +/* + Socket wrapper advanced helpers. + + Applications with the need to alter their behaviour when + socket wrapper is active, can link use these functions. + + By default it's required for applications to use any of these + functions as libsocket_wrapper.so is injected at runtime via + LD_PRELOAD. + + Applications using these functions should link against + libsocket_wrapper_noop.so by using -lsocket_wrapper_noop, + or implement their own noop stubs. +*/ + +/* + * This returns true when socket wrapper is actively in use. + */ +bool socket_wrapper_enabled(void); + +/* + * This allows socket_wrapper aware applications to + * indicate that the given fd does not belong to + * an inet socket. + * + * socket_wrapper may not be able to intercept the __close_nocancel() + * syscall made from within libc.so. As result it's possible + * that the in memory meta date of socket_wrapper references + * stale file descriptors, which are already reused for unrelated + * kernel objects, e.g. files, directories, ... + * + * Socket wrapper already intercepts a lot of unrelated + * functions like eventfd(), timerfd_create(), ... in order + * to remove stale meta data for the returned fd, but + * it will never be able to handle all possible syscalls. + * + * socket_wrapper_indicate_no_inet_fd() gives applications a way + * to do the same, explicitly without waiting for new syscalls to + * be added to libsocket_wrapper.so. + * + * This is a no-op if socket_wrapper is not in use or + * if the there is no in memory meta data for the given fd. + */ +void socket_wrapper_indicate_no_inet_fd(int fd); + +#endif /* __SOCKET_WRAPPER_H__ */ diff --git a/src/socket_wrapper_noop.c b/src/socket_wrapper_noop.c new file mode 100644 index 0000000000000000000000000000000000000000..aadf35098210701f08262d8d353f83021810620c --- /dev/null +++ b/src/socket_wrapper_noop.c @@ -0,0 +1,63 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2005-2008, Jelmer Vernooij + * Copyright (c) 2006-2021, Stefan Metzmacher + * Copyright (c) 2013-2021, Andreas Schneider + * Copyright (c) 2014-2017, Michael Adam + * Copyright (c) 2016-2018, Anoop C S + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + Socket wrapper noop library. + + Applications with the need to alter their behaviour when + socket wrapper is active, can link to this with -lsocket_wrapper_noop + in order to call get the required public functions at link time. + + During runtime these are overloaded with LD_PRELOAD by the real + libsocket_wrapper.so. +*/ + +#include "config.h" +#include "stdbool.h" +#include "socket_wrapper.h" + +bool socket_wrapper_enabled(void) +{ + return false; +} + +void socket_wrapper_indicate_no_inet_fd(int fd) +{ + (void) fd; /* unused */ + return; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db36bf35321b96e7b375d3a80890956ed96454f2..2f98af1460163dad8b6da891160b05da7b41c5b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,6 +56,7 @@ set(SWRAP_TESTS test_echo_udp_sendmsg_recvmsg test_swrap_unit test_max_sockets + test_public_functions test_close_failure test_tcp_socket_overwrite ${SWRAP_THREADED_TESTS}) @@ -111,7 +112,7 @@ foreach(_SWRAP_TEST ${SWRAP_TESTS}) add_cmocka_test(${_SWRAP_TEST} SOURCES ${_SWRAP_TEST}.c COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS} -D_GNU_SOURCE - LINK_LIBRARIES ${TORTURE_LIBRARY} + LINK_LIBRARIES ${TORTURE_LIBRARY} socket_wrapper_noop LINK_OPTIONS ${DEFAULT_LINK_FLAGS}) add_cmocka_test_environment(${_SWRAP_TEST}) endforeach() diff --git a/tests/test_public_functions.c b/tests/test_public_functions.c new file mode 100644 index 0000000000000000000000000000000000000000..816cd7d5bc09f255f92bab216fde3fb56620c51d --- /dev/null +++ b/tests/test_public_functions.c @@ -0,0 +1,109 @@ +#include "torture.h" + +#include +#include +#include +#include +#include + +#include + +static int setup_enabled(void **state) +{ + torture_setup_socket_dir(state); + + return 0; +} + +static int teardown_enabled(void **state) +{ + torture_teardown_socket_dir(state); + + return 0; +} + +static int setup_disabled(void **state) +{ + (void) state; /* unused */ + + unsetenv("SOCKET_WRAPPER_DIR"); + unsetenv("SOCKET_WRAPPER_DEFAULT_IFACE"); + unsetenv("SOCKET_WRAPPER_PCAP_FILE"); + + return 0; +} + +static int teardown_disabled(void **state) +{ + (void) state; /* unused */ + + return 0; +} + +static void test_call_enabled_true(void **state) +{ + char *s = getenv("SOCKET_WRAPPER_DIR"); + + (void) state; /* unused */ + + assert_true(socket_wrapper_enabled()); + assert_true(s != NULL); +} + +static void test_call_enabled_false(void **state) +{ + char *s = getenv("SOCKET_WRAPPER_DIR"); + + (void) state; /* unused */ + + assert_false(socket_wrapper_enabled()); + assert_false(s != NULL); +} + +static void test_call_indicate_no_inet_fd(void **state) +{ + int rc; + int s = -1; + + (void) state; /* unused */ + + socket_wrapper_indicate_no_inet_fd(987654321); + socket_wrapper_indicate_no_inet_fd(-1); + + rc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (rc >= 0) { + s = rc; + rc = 0; + } + assert_return_code(rc, errno); + + socket_wrapper_indicate_no_inet_fd(987654321); + socket_wrapper_indicate_no_inet_fd(-1); + socket_wrapper_indicate_no_inet_fd(s); + socket_wrapper_indicate_no_inet_fd(0); + socket_wrapper_indicate_no_inet_fd(1); + socket_wrapper_indicate_no_inet_fd(2); +} + +int main(void) { + int rc; + + const struct CMUnitTest max_sockets_tests[] = { + cmocka_unit_test_setup_teardown(test_call_enabled_true, + setup_enabled, + teardown_enabled), + cmocka_unit_test_setup_teardown(test_call_enabled_false, + setup_disabled, + teardown_disabled), + cmocka_unit_test_setup_teardown(test_call_indicate_no_inet_fd, + setup_enabled, + teardown_enabled), + cmocka_unit_test_setup_teardown(test_call_indicate_no_inet_fd, + setup_disabled, + teardown_disabled), + }; + + rc = cmocka_run_group_tests(max_sockets_tests, NULL, NULL); + + return rc; +} diff --git a/tests/test_tcp_dup2.c b/tests/test_tcp_dup2.c index fd6adc21de6a8d2b241c30bcef869c673f145816..238b9a8609291a50137d60cc1c0235fc81ec3a2a 100644 --- a/tests/test_tcp_dup2.c +++ b/tests/test_tcp_dup2.c @@ -2,6 +2,11 @@ #include #include +#include + +#ifdef HAVE___CLOSE_NOCANCEL +extern int __close_nocancel(int fd); +#endif static int setup(void **state) { @@ -20,6 +25,7 @@ static int teardown(void **state) static void test_dup2_existing_open_fd(void **state) { int s, dup_s; + int rc; (void) state; /* unused */ @@ -34,7 +40,19 @@ static void test_dup2_existing_open_fd(void **state) dup_s = dup2(s, s); assert_int_equal(dup_s, s); - close(s); +#ifdef HAVE___CLOSE_NOCANCEL + rc = __close_nocancel(s); + assert_return_code(rc, errno); + rc = close(s); + assert_int_equal(rc, -1); + assert_int_equal(errno, EBADF); + rc = __close_nocancel(s); + assert_int_equal(rc, -1); + assert_int_equal(errno, EBADF); +#else + rc = close(s); + assert_return_code(rc, errno); +#endif } int main(void) {