mirror of
https://github.com/zeromq/libzmq.git
synced 2024-12-12 10:33:52 +01:00
Merge pull request #782 from hashstat/master
Add support for extending ZAP request address with IPC peer credentials.
This commit is contained in:
commit
b4395d15f9
@ -631,10 +631,11 @@ list(APPEND tests
|
||||
test_monitor
|
||||
test_pair_ipc
|
||||
test_reqrep_ipc
|
||||
test_abstract_ipc
|
||||
test_fork
|
||||
test_abstract_ipc
|
||||
test_proxy
|
||||
test_filter_ipc
|
||||
test_zap_ipc_creds
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -652,6 +653,15 @@ foreach(test ${tests})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set_tests_properties(test_abstract_ipc PROPERTIES WILL_FAIL true)
|
||||
endif()
|
||||
if(NOT ZMQ_HAVE_SO_PEERCRED AND NOT ZMQ_HAVE_LOCAL_PEERCRED)
|
||||
set_tests_properties(test_zap_ipc_creds PROPERTIES WILL_FAIL true)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# installer
|
||||
|
||||
|
@ -354,6 +354,7 @@ AC_LANG_PUSH(C++)
|
||||
|
||||
AC_CHECK_DECLS([SO_PEERCRED], [AC_DEFINE(ZMQ_HAVE_SO_PEERCRED, 1, [Have SO_PEERCRED socket option])], [], [#include <sys/socket.h>])
|
||||
AC_CHECK_DECLS([LOCAL_PEERCRED], [AC_DEFINE(ZMQ_HAVE_LOCAL_PEERCRED, 1, [Have LOCAL_PEERCRED socket option])], [], [#include <sys/socket.h>])
|
||||
AM_CONDITIONAL(HAVE_IPC_PEERCRED, test "x$ac_cv_have_decl_SO_PEERCRED" = "xyes" || test "x$ac_cv_have_decl_LOCAL_PEERCRED" = "xyes")
|
||||
|
||||
AC_HEADER_STDBOOL
|
||||
AC_C_CONST
|
||||
|
@ -497,6 +497,25 @@ Default value:: -1 (leave to OS default)
|
||||
Applicable socket types:: all, when using TCP transports.
|
||||
|
||||
|
||||
ZMQ_ZAP_IPC_CREDS: Retrieve IPC peer credentials state
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The 'ZMQ_ZAP_IPC_CREDS' option shall return True (1) if credentials of IPC
|
||||
peers will be appended to the address sent in ZAP request messages and False
|
||||
(0) otherwise.
|
||||
|
||||
Refer to linkzmq:zmq_setsockopt[3] for more information.
|
||||
|
||||
NOTE: IPC peer credentials are only available on platforms supporting the
|
||||
SO_PEERCRED or LOCAL_PEERCRED socket options.
|
||||
|
||||
[horizontal]
|
||||
Option value type:: int
|
||||
Option value unit:: boolean
|
||||
Default value:: 0 (false)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_MECHANISM: Retrieve current security mechanism
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The 'ZMQ_MECHANISM' option shall retrieve the current security mechanism
|
||||
|
@ -663,6 +663,25 @@ Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_ZAP_IPC_CREDS: Append IPC peer credentials to ZAP address
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If set, the credentials of IPC peers will be appended to the address sent in
|
||||
ZAP request messages. The new address will be formatted as ADDRESS:UID:GID:PID
|
||||
where UID and GID are the effective group and user IDs of the user owning the
|
||||
peer process and PID is the process ID. PID will be empty on systems not
|
||||
supporting SO_PEERCRED.
|
||||
|
||||
NOTE: IPC peer credentials are only available on platforms supporting the
|
||||
SO_PEERCRED or LOCAL_PEERCRED socket options.
|
||||
|
||||
[horizontal]
|
||||
Option value type:: int
|
||||
Option value unit:: boolean
|
||||
Default value:: 0 (false)
|
||||
Applicable socket types:: all listening sockets, when using IPC transports.
|
||||
|
||||
|
||||
ZMQ_PLAIN_SERVER: Set PLAIN server role
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -293,6 +293,7 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval);
|
||||
#define ZMQ_IPC_FILTER_PID 58
|
||||
#define ZMQ_IPC_FILTER_UID 59
|
||||
#define ZMQ_IPC_FILTER_GID 60
|
||||
#define ZMQ_ZAP_IPC_CREDS 61
|
||||
|
||||
/* Message options */
|
||||
#define ZMQ_MORE 1
|
||||
|
10
src/ip.cpp
10
src/ip.cpp
@ -109,7 +109,7 @@ void zmq::enable_ipv4_mapping (fd_t s_)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
|
||||
int zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_storage ss;
|
||||
@ -126,7 +126,7 @@ bool zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
|
||||
WSAGetLastError () != WSAEFAULT &&
|
||||
WSAGetLastError () != WSAEINPROGRESS &&
|
||||
WSAGetLastError () != WSAENOTSOCK);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
if (rc == -1) {
|
||||
@ -135,7 +135,7 @@ bool zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
|
||||
errno != EINVAL &&
|
||||
errno != ENOTCONN &&
|
||||
errno != ENOTSOCK);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -143,10 +143,10 @@ bool zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
|
||||
rc = getnameinfo ((struct sockaddr*) &ss, addrlen, host, sizeof host,
|
||||
NULL, 0, NI_NUMERICHOST);
|
||||
if (rc != 0)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
ip_addr_ = host;
|
||||
return true;
|
||||
return (int) ((struct sockaddr *) &ss)->sa_family;
|
||||
}
|
||||
|
||||
void zmq::set_ip_type_of_service (fd_t s_, int iptos)
|
||||
|
@ -37,7 +37,7 @@ namespace zmq
|
||||
|
||||
// Returns string representation of peer's address.
|
||||
// Socket sockfd_ must be connected. Returns true iff successful.
|
||||
bool get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_);
|
||||
int get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_);
|
||||
|
||||
// Sets the IP Type-Of-Service for the underlying socket
|
||||
void set_ip_type_of_service (fd_t s_, int iptos);
|
||||
|
@ -51,6 +51,9 @@ zmq::options_t::options_t () :
|
||||
tcp_keepalive_cnt (-1),
|
||||
tcp_keepalive_idle (-1),
|
||||
tcp_keepalive_intvl (-1),
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
zap_ipc_creds (false),
|
||||
# endif
|
||||
mechanism (ZMQ_NULL),
|
||||
as_server (0),
|
||||
socket_id (0),
|
||||
@ -258,6 +261,13 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
|
||||
break;
|
||||
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
case ZMQ_ZAP_IPC_CREDS:
|
||||
if (is_int && (value == 0 || value == 1)) {
|
||||
zap_ipc_creds = (value != 0);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZMQ_IPC_FILTER_UID:
|
||||
if (optvallen_ == 0 && optval_ == NULL) {
|
||||
ipc_uid_accept_filters.clear ();
|
||||
@ -591,6 +601,15 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
|
||||
}
|
||||
break;
|
||||
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
case ZMQ_ZAP_IPC_CREDS:
|
||||
if (is_int) {
|
||||
*value = zap_ipc_creds;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
# endif
|
||||
|
||||
case ZMQ_MECHANISM:
|
||||
if (is_int) {
|
||||
*value = mechanism;
|
||||
|
@ -127,6 +127,7 @@ namespace zmq
|
||||
|
||||
// IPC accept() filters
|
||||
# if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
bool zap_ipc_creds;
|
||||
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;
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <sstream>
|
||||
|
||||
#include "stream_engine.hpp"
|
||||
#include "io_thread.hpp"
|
||||
@ -84,8 +85,34 @@ zmq::stream_engine_t::stream_engine_t (fd_t fd_, const options_t &options_,
|
||||
// Put the socket into non-blocking mode.
|
||||
unblock_socket (s);
|
||||
|
||||
if (!get_peer_ip_address (s, peer_address))
|
||||
int family = get_peer_ip_address (s, peer_address);
|
||||
if (family == 0)
|
||||
peer_address = "";
|
||||
#if defined ZMQ_HAVE_SO_PEERCRED
|
||||
else if (family == PF_UNIX && options.zap_ipc_creds) {
|
||||
struct ucred cred;
|
||||
socklen_t size = sizeof (cred);
|
||||
if (!getsockopt (s, SOL_SOCKET, SO_PEERCRED, &cred, &size)) {
|
||||
std::ostringstream buf;
|
||||
buf << ":" << cred.uid << ":" << cred.gid << ":" << cred.pid;
|
||||
peer_address += buf.str ();
|
||||
}
|
||||
}
|
||||
#elif defined ZMQ_HAVE_LOCAL_PEERCRED
|
||||
else if (family == PF_UNIX && options.zap_ipc_creds) {
|
||||
struct xucred cred;
|
||||
socklen_t size = sizeof (cred);
|
||||
if (!getsockopt (s, 0, LOCAL_PEERCRED, &cred, &size)
|
||||
&& cred.cr_version == XUCRED_VERSION) {
|
||||
std::ostringstream buf;
|
||||
buf << ":" << cred.cr_uid << ":";
|
||||
if (cred.cr_ngroups > 0)
|
||||
buf << cred.cr_groups[0];
|
||||
buf << ":";
|
||||
peer_address += buf.str ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SO_NOSIGPIPE
|
||||
// Make sure that SIGPIPE signal is not generated when writing to a
|
||||
|
@ -51,7 +51,8 @@ noinst_PROGRAMS += test_shutdown_stress \
|
||||
test_reqrep_ipc \
|
||||
test_timeo \
|
||||
test_fork \
|
||||
test_filter_ipc
|
||||
test_filter_ipc \
|
||||
test_zap_ipc_creds
|
||||
endif
|
||||
|
||||
if BUILD_TIPC
|
||||
@ -113,6 +114,7 @@ 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
|
||||
test_zap_ipc_creds_SOURCES = test_zap_ipc_creds.cpp
|
||||
endif
|
||||
if BUILD_TIPC
|
||||
test_connect_delay_tipc_SOURCES = test_connect_delay_tipc.cpp
|
||||
@ -127,7 +129,12 @@ endif
|
||||
|
||||
# Run the test cases
|
||||
TESTS = $(noinst_PROGRAMS)
|
||||
XFAIL_TESTS =
|
||||
|
||||
if !ON_LINUX
|
||||
XFAIL_TESTS = test_abstract_ipc
|
||||
XFAIL_TESTS += test_abstract_ipc
|
||||
endif
|
||||
|
||||
if !HAVE_IPC_PEERCRED
|
||||
XFAIL_TESTS += test_zap_ipc_creds
|
||||
endif
|
||||
|
135
tests/test_zap_ipc_creds.cpp
Normal file
135
tests/test_zap_ipc_creds.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
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 <sstream>
|
||||
|
||||
#include "testutil.hpp"
|
||||
|
||||
static void zap_handler (void *handler)
|
||||
{
|
||||
// Process ZAP requests forever
|
||||
while (true) {
|
||||
char *version = s_recv (handler);
|
||||
if (!version)
|
||||
break; // Terminating
|
||||
char *sequence = s_recv (handler);
|
||||
char *domain = s_recv (handler);
|
||||
char *address = s_recv (handler);
|
||||
char *identity = s_recv (handler);
|
||||
char *mechanism = s_recv (handler);
|
||||
|
||||
assert (streq (version, "1.0"));
|
||||
assert (streq (mechanism, "NULL"));
|
||||
|
||||
if (streq (domain, "creds")) {
|
||||
std::ostringstream buf;
|
||||
buf << "localhost:" << getuid () << ":" << getgid () << ":";
|
||||
# ifdef ZMQ_HAVE_SO_PEERCRED
|
||||
buf << getpid ();
|
||||
# endif
|
||||
assert (streq (address, buf.str ().c_str ()));
|
||||
} else
|
||||
assert (streq (address, "localhost"));
|
||||
|
||||
s_sendmore (handler, version);
|
||||
s_sendmore (handler, sequence);
|
||||
s_sendmore (handler, "200");
|
||||
s_sendmore (handler, "OK");
|
||||
s_sendmore (handler, "anonymous");
|
||||
s_send (handler, "");
|
||||
|
||||
free (version);
|
||||
free (sequence);
|
||||
free (domain);
|
||||
free (address);
|
||||
free (identity);
|
||||
free (mechanism);
|
||||
}
|
||||
zmq_close (handler);
|
||||
}
|
||||
|
||||
static void run_test (bool with_creds)
|
||||
{
|
||||
void *ctx = zmq_ctx_new ();
|
||||
assert (ctx);
|
||||
|
||||
// Spawn ZAP handler
|
||||
// We create and bind ZAP socket in main thread to avoid case
|
||||
// where child thread does not start up fast enough.
|
||||
void *handler = zmq_socket (ctx, ZMQ_REP);
|
||||
assert (handler);
|
||||
int rc = zmq_bind (handler, "inproc://zeromq.zap.01");
|
||||
assert (rc == 0);
|
||||
void *zap_thread = zmq_threadstart (&zap_handler, handler);
|
||||
|
||||
void *sb = zmq_socket (ctx, ZMQ_PAIR);
|
||||
assert (sb);
|
||||
|
||||
void *sc = zmq_socket (ctx, ZMQ_PAIR);
|
||||
assert (sc);
|
||||
|
||||
// Now use the right domain, the test must pass
|
||||
if (with_creds) {
|
||||
rc = zmq_setsockopt (sb, ZMQ_ZAP_DOMAIN, "creds", 5);
|
||||
assert (rc == 0);
|
||||
int ipc_creds = 1;
|
||||
rc = zmq_setsockopt (sb, ZMQ_ZAP_IPC_CREDS, &ipc_creds, sizeof (int));
|
||||
assert (rc == 0);
|
||||
} else {
|
||||
rc = zmq_setsockopt (sb, ZMQ_ZAP_DOMAIN, "none", 4);
|
||||
assert (rc == 0);
|
||||
int ipc_creds = 1;
|
||||
size_t size = sizeof (int);
|
||||
rc = zmq_getsockopt (sb, ZMQ_ZAP_IPC_CREDS, &ipc_creds, &size);
|
||||
assert (rc == 0);
|
||||
assert (ipc_creds == 0);
|
||||
}
|
||||
|
||||
rc = zmq_bind (sb, "ipc://@/tmp/test");
|
||||
assert (rc == 0);
|
||||
|
||||
rc = zmq_connect (sc, "ipc://@/tmp/test");
|
||||
assert (rc == 0);
|
||||
|
||||
bounce (sb, sc);
|
||||
|
||||
rc = zmq_close (sc);
|
||||
assert (rc == 0);
|
||||
rc = zmq_close (sb);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = zmq_ctx_term (ctx);
|
||||
assert (rc == 0);
|
||||
|
||||
// Wait until ZAP handler terminates.
|
||||
zmq_threadclose (zap_thread);
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
setup_test_environment();
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user