Intial IPC accept filter support.

Adds sets of process (Linux only), user, and group IDs for filtering
connections from peer processes over IPC transport.  If all of the
filter sets are empty, every connection is accepted.  Otherwise,
credentials for a connecting process are checked against the filter sets
and the connection is only accepted if a match is found.

This commit is part of LIBZMQ-568 and only adds the filter sets and
implements the filter in the IPC accept method.  The interface for
adding IDs to filter sets are included in a separate commit.

IPC accept filtering is supported only on Linux and OS X.
This commit is contained in:
Brandon Carpenter
2013-12-04 13:56:12 -08:00
parent a9492a0852
commit 8662f44efa
4 changed files with 104 additions and 0 deletions

View File

@@ -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;
}