mirror of
https://github.com/zeromq/libzmq.git
synced 2025-03-04 19:13:35 +01:00
Merge pull request #773 from hashstat/master
Add IPC accept filter support
This commit is contained in:
commit
b7de0424fc
1
.gitignore
vendored
1
.gitignore
vendored
@ -72,6 +72,7 @@ tests/test_security_null
|
||||
tests/test_security_plain
|
||||
tests/test_proxy
|
||||
tests/test_abstract_ipc
|
||||
tests/test_filter_ipc
|
||||
tests/test_connect_delay_tipc
|
||||
tests/test_pair_tipc
|
||||
tests/test_reqrep_device_tipc
|
||||
|
@ -170,6 +170,7 @@ case "${host_os}" in
|
||||
fi
|
||||
AC_DEFINE(ZMQ_HAVE_LINUX, 1, [Have Linux OS])
|
||||
libzmq_on_linux="yes"
|
||||
AC_CHECK_DECLS([SO_PEERCRED], [AC_DEFINE(ZMQ_HAVE_SO_PEERCRED, 1, [Have SO_PEERCRED socket option])], [], [#include <sys/socket.h>])
|
||||
|
||||
if test "x$libzmq_tipc_support" = "xyes"; then
|
||||
AC_DEFINE(ZMQ_HAVE_TIPC, 1, [Have TIPC support])
|
||||
@ -215,6 +216,7 @@ case "${host_os}" in
|
||||
libzmq_pedantic="no"
|
||||
libzmq_werror="no"
|
||||
AC_DEFINE(ZMQ_HAVE_OSX, 1, [Have DarwinOSX OS])
|
||||
AC_CHECK_DECLS([LOCAL_PEERCRED], [AC_DEFINE(ZMQ_HAVE_LOCAL_PEERCRED, 1, [Have LOCAL_PEERCRED socket option])], [], [#include <sys/socket.h>])
|
||||
AC_LANG_PUSH([C++])
|
||||
LIBZMQ_CHECK_LANG_FLAG_PREPEND([-Wno-uninitialized])
|
||||
AC_LANG_POP([C++])
|
||||
|
@ -601,6 +601,68 @@ Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using TCP transports.
|
||||
|
||||
|
||||
ZMQ_IPC_ACCEPT_FILTER_UID: Assign user ID filters to allow new IPC connections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Assign an arbitrary number of filters that will be applied for each new IPC
|
||||
transport connection on a listening socket. If no IPC filters are applied, then
|
||||
the IPC transport allows connections from any process. If at least one UID,
|
||||
GID, or PID filter is applied then new connection credentials should be
|
||||
matched. To clear all UID filters call zmq_setsockopt(socket,
|
||||
ZMQ_IPC_ACCEPT_FILTER_UID, NULL, 0).
|
||||
|
||||
NOTE: UID filters are only available on platforms supporting SO_PEERCRED or
|
||||
LOCAL_PEERCRED socket options (currently only Linux and later versions of
|
||||
OS X).
|
||||
|
||||
[horizontal]
|
||||
Option value type:: uid_t
|
||||
Option value unit:: N/A
|
||||
Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_IPC_ACCEPT_FILTER_GID: Assign group ID filters to allow new IPC connections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Assign an arbitrary number of filters that will be applied for each new IPC
|
||||
transport connection on a listening socket. If no IPC filters are applied, then
|
||||
the IPC transport allows connections from any process. If at least one UID,
|
||||
GID, or PID filter is applied then new connection credentials should be
|
||||
matched. To clear all GID filters call zmq_setsockopt(socket,
|
||||
ZMQ_IPC_ACCEPT_FILTER_GID, NULL, 0).
|
||||
|
||||
NOTE: GID filters are only available on platforms supporting SO_PEERCRED or
|
||||
LOCAL_PEERCRED socket options (currently only Linux and later versions of
|
||||
OS X).
|
||||
|
||||
[horizontal]
|
||||
Option value type:: gid_t
|
||||
Option value unit:: N/A
|
||||
Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_IPC_ACCEPT_FILTER_PID: Assign process ID filters to new IPC connections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Assign an arbitrary number of filters that will be applied for each new IPC
|
||||
transport connection on a listening socket. If no IPC filters are applied, then
|
||||
the IPC transport allows connections from any process. If at least one UID,
|
||||
GID, or PID filter is applied then new connection credentials should be
|
||||
matched. To clear all PID filters call zmq_setsockopt(socket,
|
||||
ZMQ_IPC_ACCEPT_FILTER_PID, NULL, 0).
|
||||
|
||||
NOTE: PID filters are only available on platforms supporting the SO_PEERCRED
|
||||
socket option (currently only Linux).
|
||||
|
||||
[horizontal]
|
||||
Option value type:: pid_t
|
||||
Option value unit:: N/A
|
||||
Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_PLAIN_SERVER: Set PLAIN server role
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -290,6 +290,9 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval);
|
||||
#define ZMQ_ZAP_DOMAIN 55
|
||||
#define ZMQ_ROUTER_HANDOVER 56
|
||||
#define ZMQ_TOS 57
|
||||
#define ZMQ_IPC_ACCEPT_FILTER_PID 58
|
||||
#define ZMQ_IPC_ACCEPT_FILTER_UID 59
|
||||
#define ZMQ_IPC_ACCEPT_FILTER_GID 60
|
||||
|
||||
/* Message options */
|
||||
#define ZMQ_MORE 1
|
||||
|
@ -39,6 +39,14 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef ZMQ_HAVE_SO_PEERCRED
|
||||
# include <pwd.h>
|
||||
# include <grp.h>
|
||||
#endif
|
||||
|
||||
zmq::ipc_listener_t::ipc_listener_t (io_thread_t *io_thread_,
|
||||
socket_base_t *socket_, const options_t &options_) :
|
||||
own_t (io_thread_, options_),
|
||||
@ -192,6 +200,67 @@ int zmq::ipc_listener_t::close ()
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED
|
||||
|
||||
bool zmq::ipc_listener_t::filter (fd_t sock)
|
||||
{
|
||||
if (options.ipc_uid_accept_filters.empty () &&
|
||||
options.ipc_pid_accept_filters.empty () &&
|
||||
options.ipc_gid_accept_filters.empty ())
|
||||
return true;
|
||||
|
||||
struct ucred cred;
|
||||
socklen_t size = sizeof (cred);
|
||||
|
||||
if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cred, &size))
|
||||
return false;
|
||||
if (options.ipc_uid_accept_filters.find (cred.uid) != options.ipc_uid_accept_filters.end () ||
|
||||
options.ipc_pid_accept_filters.find (cred.pid) != options.ipc_pid_accept_filters.end ())
|
||||
return true;
|
||||
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
|
||||
if (!(pw = getpwuid (cred.uid)))
|
||||
return false;
|
||||
for (options_t::ipc_gid_accept_filters_t::const_iterator it = options.ipc_gid_accept_filters.begin ();
|
||||
it != options.ipc_gid_accept_filters.end (); it++) {
|
||||
if (!(gr = getgrgid (*it)))
|
||||
continue;
|
||||
for (char **mem = gr->gr_mem; *mem; mem++)
|
||||
if (!strcmp (*mem, pw->pw_name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
|
||||
bool zmq::ipc_listener_t::filter (fd_t sock)
|
||||
{
|
||||
if (options.ipc_uid_accept_filters.empty () &&
|
||||
options.ipc_gid_accept_filters.empty ())
|
||||
return true;
|
||||
|
||||
struct xucred cred;
|
||||
socklen_t size = sizeof (cred);
|
||||
|
||||
if (getsockopt (sock, 0, LOCAL_PEERCRED, &cred, &size))
|
||||
return false;
|
||||
if (cred.cr_version != XUCRED_VERSION)
|
||||
return false;
|
||||
if (options.ipc_uid_accept_filters.find (cred.cr_uid) != options.ipc_uid_accept_filters.end ())
|
||||
return true;
|
||||
for (int i = 0; i < cred.cr_ngroups; i++) {
|
||||
if (options.ipc_gid_accept_filters.find (cred.cr_groups[i]) != options.ipc_gid_accept_filters.end ())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
zmq::fd_t zmq::ipc_listener_t::accept ()
|
||||
{
|
||||
// Accept one connection and deal with different failure modes.
|
||||
@ -205,6 +274,16 @@ zmq::fd_t zmq::ipc_listener_t::accept ()
|
||||
errno == ENFILE);
|
||||
return retired_fd;
|
||||
}
|
||||
|
||||
// IPC accept() filters
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
if (!filter (sock)) {
|
||||
int rc = ::close (sock);
|
||||
errno_assert (rc == 0);
|
||||
return retired_fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,12 @@ namespace zmq
|
||||
// Close the listening socket.
|
||||
int close ();
|
||||
|
||||
// Filter new connections if the OS provides a mechanism to get
|
||||
// the credentials of the peer process. Called from accept().
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
bool filter (fd_t sock);
|
||||
# endif
|
||||
|
||||
// Accept the new connection. Returns the file descriptor of the
|
||||
// newly created connection. The function may return retired_fd
|
||||
// if the connection was dropped while waiting in the listen backlog.
|
||||
|
@ -257,6 +257,46 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
|
||||
}
|
||||
break;
|
||||
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
case ZMQ_IPC_ACCEPT_FILTER_UID:
|
||||
if (optvallen_ == 0 && optval_ == NULL) {
|
||||
ipc_uid_accept_filters.clear ();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
if (optvallen_ == sizeof (uid_t) && optval_ != NULL) {
|
||||
ipc_uid_accept_filters.insert (*((uid_t *) optval_));
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZMQ_IPC_ACCEPT_FILTER_GID:
|
||||
if (optvallen_ == 0 && optval_ == NULL) {
|
||||
ipc_gid_accept_filters.clear ();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
if (optvallen_ == sizeof (gid_t) && optval_ != NULL) {
|
||||
ipc_gid_accept_filters.insert (*((gid_t *) optval_));
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
# endif
|
||||
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED
|
||||
case ZMQ_IPC_ACCEPT_FILTER_PID:
|
||||
if (optvallen_ == 0 && optval_ == NULL) {
|
||||
ipc_pid_accept_filters.clear ();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
if (optvallen_ == sizeof (pid_t) && optval_ != NULL) {
|
||||
ipc_pid_accept_filters.insert (*((pid_t *) optval_));
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
# endif
|
||||
|
||||
case ZMQ_PLAIN_SERVER:
|
||||
if (is_int && (value == 0 || value == 1)) {
|
||||
as_server = value;
|
||||
|
@ -22,12 +22,17 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.hpp"
|
||||
#include "tcp_address.hpp"
|
||||
#include "../include/zmq.h"
|
||||
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
// Normal base 256 key is 32 bytes
|
||||
#define CURVE_KEYSIZE 32
|
||||
// Key encoded using Z85 is 40 bytes
|
||||
@ -120,6 +125,18 @@ namespace zmq
|
||||
typedef std::vector <tcp_address_mask_t> tcp_accept_filters_t;
|
||||
tcp_accept_filters_t tcp_accept_filters;
|
||||
|
||||
// IPC accept() filters
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
typedef std::set <uid_t> ipc_uid_accept_filters_t;
|
||||
ipc_uid_accept_filters_t ipc_uid_accept_filters;
|
||||
typedef std::set <gid_t> ipc_gid_accept_filters_t;
|
||||
ipc_gid_accept_filters_t ipc_gid_accept_filters;
|
||||
# endif
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED
|
||||
typedef std::set <pid_t> ipc_pid_accept_filters_t;
|
||||
ipc_pid_accept_filters_t ipc_pid_accept_filters;
|
||||
# endif
|
||||
|
||||
// Security mechanism for all connections on this socket
|
||||
int mechanism;
|
||||
|
||||
|
@ -50,7 +50,8 @@ noinst_PROGRAMS += test_shutdown_stress \
|
||||
test_pair_ipc \
|
||||
test_reqrep_ipc \
|
||||
test_timeo \
|
||||
test_fork
|
||||
test_fork \
|
||||
test_filter_ipc
|
||||
endif
|
||||
|
||||
if BUILD_TIPC
|
||||
@ -111,6 +112,7 @@ test_pair_ipc_SOURCES = test_pair_ipc.cpp testutil.hpp
|
||||
test_reqrep_ipc_SOURCES = test_reqrep_ipc.cpp testutil.hpp
|
||||
test_timeo_SOURCES = test_timeo.cpp
|
||||
test_fork_SOURCES = test_fork.cpp
|
||||
test_filter_ipc_SOURCES = test_filter_ipc.cpp
|
||||
endif
|
||||
if BUILD_TIPC
|
||||
test_connect_delay_tipc_SOURCES = test_connect_delay_tipc.cpp
|
||||
|
141
tests/test_filter_ipc.cpp
Normal file
141
tests/test_filter_ipc.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
|
||||
|
||||
This file is part of 0MQ.
|
||||
|
||||
0MQ is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
0MQ 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "testutil.hpp"
|
||||
|
||||
static void bounce_fail (void *server, void *client)
|
||||
{
|
||||
const char *content = "12345678ABCDEFGH12345678abcdefgh";
|
||||
char buffer [32];
|
||||
|
||||
// Send message from client to server
|
||||
int rc = zmq_send (client, content, 32, ZMQ_SNDMORE);
|
||||
assert (rc == 32);
|
||||
rc = zmq_send (client, content, 32, 0);
|
||||
assert (rc == 32);
|
||||
|
||||
// Receive message at server side (should not succeed)
|
||||
int timeout = 150;
|
||||
rc = zmq_setsockopt (server, ZMQ_RCVTIMEO, &timeout, sizeof (int));
|
||||
assert (rc == 0);
|
||||
rc = zmq_recv (server, buffer, 32, 0);
|
||||
assert (rc == -1);
|
||||
assert (zmq_errno () == EAGAIN);
|
||||
|
||||
// Send message from server to client to test other direction
|
||||
rc = zmq_setsockopt (server, ZMQ_SNDTIMEO, &timeout, sizeof (int));
|
||||
assert (rc == 0);
|
||||
rc = zmq_send (server, content, 32, ZMQ_SNDMORE);
|
||||
assert (rc == -1);
|
||||
assert (zmq_errno () == EAGAIN);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void run_test (int opt, T optval, int expected_error, int bounce_test)
|
||||
{
|
||||
void *ctx = zmq_ctx_new ();
|
||||
assert (ctx);
|
||||
|
||||
void *sb = zmq_socket (ctx, ZMQ_PAIR);
|
||||
assert (sb);
|
||||
|
||||
if (opt) {
|
||||
int rc = zmq_setsockopt(sb, opt, &optval, sizeof (optval));
|
||||
if (expected_error) {
|
||||
assert (rc == -1);
|
||||
assert (zmq_errno () == expected_error);
|
||||
} else {
|
||||
assert (rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void *sc = zmq_socket (ctx, ZMQ_PAIR);
|
||||
assert (sc);
|
||||
|
||||
if (bounce_test) {
|
||||
int rc = zmq_bind (sb, "ipc://@/tmp/test");
|
||||
assert (rc == 0);
|
||||
|
||||
rc = zmq_connect (sc, "ipc://@/tmp/test");
|
||||
assert (rc == 0);
|
||||
|
||||
if (bounce_test > 0)
|
||||
bounce (sb, sc);
|
||||
else
|
||||
bounce_fail (sb, sc);
|
||||
}
|
||||
|
||||
close_zero_linger (sc);
|
||||
close_zero_linger (sb);
|
||||
|
||||
int rc = zmq_ctx_term (ctx);
|
||||
assert (rc == 0);
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
setup_test_environment();
|
||||
|
||||
// No filters
|
||||
run_test<int> (0, 0, 0, 1);
|
||||
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
// Get the group and supplimental groups of the process owner
|
||||
gid_t groups[100];
|
||||
int ngroups = getgroups(100, groups);
|
||||
assert (ngroups != -1);
|
||||
gid_t group = getgid(), supgroup = groups[0], notgroup = groups[ngroups - 1] + 1;
|
||||
for (int i = 0; i < ngroups; i++) {
|
||||
if (supgroup == group && group != groups[i])
|
||||
supgroup = groups[i];
|
||||
if (notgroup <= groups[i])
|
||||
notgroup = groups[i] + 1;
|
||||
}
|
||||
|
||||
// Test filter with UID of process owner
|
||||
run_test<uid_t> (ZMQ_IPC_ACCEPT_FILTER_UID, getuid(), 0, 1);
|
||||
// Test filter with UID of another (possibly non-existent) user
|
||||
run_test<uid_t> (ZMQ_IPC_ACCEPT_FILTER_UID, getuid() + 1, 0, -1);
|
||||
// Test filter with GID of process owner
|
||||
run_test<gid_t> (ZMQ_IPC_ACCEPT_FILTER_GID, group, 0, 1);
|
||||
// Test filter with supplimental group of process owner
|
||||
run_test<gid_t> (ZMQ_IPC_ACCEPT_FILTER_GID, supgroup, 0, 1);
|
||||
// Test filter with GID of another (possibly non-existent) group
|
||||
run_test<gid_t> (ZMQ_IPC_ACCEPT_FILTER_GID, notgroup, 0, -1);
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED
|
||||
// Test filter with PID of current process
|
||||
run_test<pid_t> (ZMQ_IPC_ACCEPT_FILTER_PID, getpid(), 0, 1);
|
||||
// Test filter with PID of another (possibly non-existent) process
|
||||
run_test<pid_t> (ZMQ_IPC_ACCEPT_FILTER_PID, getpid() + 1, 0, -1);
|
||||
# else
|
||||
// Setup of PID filter should fail with operation not supported error
|
||||
run_test<pid_t> (ZMQ_IPC_ACCEPT_FILTER_PID, getpid(), EINVAL, 0);
|
||||
# endif
|
||||
#else
|
||||
run_test<uid_t> (ZMQ_IPC_ACCEPT_FILTER_UID, 0, EINVAL, 0);
|
||||
run_test<gid_t> (ZMQ_IPC_ACCEPT_FILTER_GID, 0, EINVAL, 0);
|
||||
run_test<pid_t> (ZMQ_IPC_ACCEPT_FILTER_PID, 0, EINVAL, 0);
|
||||
#endif // defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user