1 /*
2     Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3 
4     This file is part of libzmq, the ZeroMQ core engine in C++.
5 
6     libzmq is free software; you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10 
11     As a special exception, the Contributors give you permission to link
12     this library with independent modules to produce an executable,
13     regardless of the license terms of these independent modules, and to
14     copy and distribute the resulting executable under terms of your choice,
15     provided that you also meet, for each linked independent module, the
16     terms and conditions of the license of that module. An independent
17     module is a module which is not derived from or based on this library.
18     If you modify this library, you must extend this exception to your
19     version of the library.
20 
21     libzmq is distributed in the hope that it will be useful, but WITHOUT
22     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23     FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24     License for more details.
25 
26     You should have received a copy of the GNU Lesser General Public License
27     along with this program.  If not, see <http://www.gnu.org/licenses/>.
28 */
29 
30 #include "precompiled.hpp"
31 #include "ipc_listener.hpp"
32 
33 #if defined ZMQ_HAVE_IPC
34 
35 #include <new>
36 
37 #include <string.h>
38 
39 #include "ipc_address.hpp"
40 #include "io_thread.hpp"
41 #include "config.hpp"
42 #include "err.hpp"
43 #include "ip.hpp"
44 #include "socket_base.hpp"
45 #include "address.hpp"
46 
47 #ifdef _MSC_VER
48 #ifdef ZMQ_IOTHREAD_POLLER_USE_SELECT
49 #error On Windows, IPC does not work with POLLER=select, use POLLER=epoll instead, or disable IPC transport
50 #endif
51 
52 #include <afunix.h>
53 #include <direct.h>
54 
55 #define rmdir _rmdir
56 #define unlink _unlink
57 
58 #else
59 #include <unistd.h>
60 #include <sys/socket.h>
61 #include <fcntl.h>
62 #include <sys/un.h>
63 #endif
64 
65 #ifdef ZMQ_HAVE_LOCAL_PEERCRED
66 #include <sys/types.h>
67 #include <sys/ucred.h>
68 #endif
69 #ifdef ZMQ_HAVE_SO_PEERCRED
70 #include <sys/types.h>
71 #include <pwd.h>
72 #include <grp.h>
73 #if defined ZMQ_HAVE_OPENBSD
74 #define ucred sockpeercred
75 #endif
76 #endif
77 
ipc_listener_t(io_thread_t * io_thread_,socket_base_t * socket_,const options_t & options_)78 zmq::ipc_listener_t::ipc_listener_t (io_thread_t *io_thread_,
79                                      socket_base_t *socket_,
80                                      const options_t &options_) :
81     stream_listener_base_t (io_thread_, socket_, options_),
82     _has_file (false)
83 {
84 }
85 
in_event()86 void zmq::ipc_listener_t::in_event ()
87 {
88     const fd_t fd = accept ();
89 
90     //  If connection was reset by the peer in the meantime, just ignore it.
91     //  TODO: Handle specific errors like ENFILE/EMFILE etc.
92     if (fd == retired_fd) {
93         _socket->event_accept_failed (
94           make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
95         return;
96     }
97 
98     //  Create the engine object for this connection.
99     create_engine (fd);
100 }
101 
102 std::string
get_socket_name(zmq::fd_t fd_,socket_end_t socket_end_) const103 zmq::ipc_listener_t::get_socket_name (zmq::fd_t fd_,
104                                       socket_end_t socket_end_) const
105 {
106     return zmq::get_socket_name<ipc_address_t> (fd_, socket_end_);
107 }
108 
set_local_address(const char * addr_)109 int zmq::ipc_listener_t::set_local_address (const char *addr_)
110 {
111     //  Create addr on stack for auto-cleanup
112     std::string addr (addr_);
113 
114     //  Allow wildcard file
115     if (options.use_fd == -1 && addr[0] == '*') {
116         if (create_ipc_wildcard_address (_tmp_socket_dirname, addr) < 0) {
117             return -1;
118         }
119     }
120 
121     //  Get rid of the file associated with the UNIX domain socket that
122     //  may have been left behind by the previous run of the application.
123     //  MUST NOT unlink if the FD is managed by the user, or it will stop
124     //  working after the first client connects. The user will take care of
125     //  cleaning up the file after the service is stopped.
126     if (options.use_fd == -1) {
127         ::unlink (addr.c_str ());
128     }
129     _filename.clear ();
130 
131     //  Initialise the address structure.
132     ipc_address_t address;
133     int rc = address.resolve (addr.c_str ());
134     if (rc != 0) {
135         if (!_tmp_socket_dirname.empty ()) {
136             // We need to preserve errno to return to the user
137             const int tmp_errno = errno;
138             ::rmdir (_tmp_socket_dirname.c_str ());
139             _tmp_socket_dirname.clear ();
140             errno = tmp_errno;
141         }
142         return -1;
143     }
144 
145     address.to_string (_endpoint);
146 
147     if (options.use_fd != -1) {
148         _s = options.use_fd;
149     } else {
150         //  Create a listening socket.
151         _s = open_socket (AF_UNIX, SOCK_STREAM, 0);
152         if (_s == retired_fd) {
153             if (!_tmp_socket_dirname.empty ()) {
154                 // We need to preserve errno to return to the user
155                 const int tmp_errno = errno;
156                 ::rmdir (_tmp_socket_dirname.c_str ());
157                 _tmp_socket_dirname.clear ();
158                 errno = tmp_errno;
159             }
160             return -1;
161         }
162 
163         //  Bind the socket to the file path.
164         rc = bind (_s, const_cast<sockaddr *> (address.addr ()),
165                    address.addrlen ());
166         if (rc != 0)
167             goto error;
168 
169         //  Listen for incoming connections.
170         rc = listen (_s, options.backlog);
171         if (rc != 0)
172             goto error;
173     }
174 
175     _filename = ZMQ_MOVE (addr);
176     _has_file = true;
177 
178     _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint),
179                               _s);
180     return 0;
181 
182 error:
183     const int err = errno;
184     close ();
185     errno = err;
186     return -1;
187 }
188 
close()189 int zmq::ipc_listener_t::close ()
190 {
191     zmq_assert (_s != retired_fd);
192     const fd_t fd_for_event = _s;
193 #ifdef ZMQ_HAVE_WINDOWS
194     int rc = closesocket (_s);
195     wsa_assert (rc != SOCKET_ERROR);
196 #else
197     int rc = ::close (_s);
198     errno_assert (rc == 0);
199 #endif
200 
201     _s = retired_fd;
202 
203     if (_has_file && options.use_fd == -1) {
204         if (!_tmp_socket_dirname.empty ()) {
205             //  TODO review this behaviour, it is inconsistent with the use of
206             //  unlink in open since 656cdb959a7482c45db979c1d08ede585d12e315;
207             //  however, we must at least remove the file before removing the
208             //  directory, otherwise it will always fail
209             rc = ::unlink (_filename.c_str ());
210 
211             if (rc == 0) {
212                 rc = ::rmdir (_tmp_socket_dirname.c_str ());
213                 _tmp_socket_dirname.clear ();
214             }
215         }
216 
217         if (rc != 0) {
218             _socket->event_close_failed (
219               make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
220             return -1;
221         }
222     }
223 
224     _socket->event_closed (make_unconnected_bind_endpoint_pair (_endpoint),
225                            fd_for_event);
226     return 0;
227 }
228 
229 #if defined ZMQ_HAVE_SO_PEERCRED
230 
filter(fd_t sock_)231 bool zmq::ipc_listener_t::filter (fd_t sock_)
232 {
233     if (options.ipc_uid_accept_filters.empty ()
234         && options.ipc_pid_accept_filters.empty ()
235         && options.ipc_gid_accept_filters.empty ())
236         return true;
237 
238     struct ucred cred;
239     socklen_t size = sizeof (cred);
240 
241     if (getsockopt (sock_, SOL_SOCKET, SO_PEERCRED, &cred, &size))
242         return false;
243     if (options.ipc_uid_accept_filters.find (cred.uid)
244           != options.ipc_uid_accept_filters.end ()
245         || options.ipc_gid_accept_filters.find (cred.gid)
246              != options.ipc_gid_accept_filters.end ()
247         || options.ipc_pid_accept_filters.find (cred.pid)
248              != options.ipc_pid_accept_filters.end ())
249         return true;
250 
251     const struct passwd *pw;
252     const struct group *gr;
253 
254     if (!(pw = getpwuid (cred.uid)))
255         return false;
256     for (options_t::ipc_gid_accept_filters_t::const_iterator
257            it = options.ipc_gid_accept_filters.begin (),
258            end = options.ipc_gid_accept_filters.end ();
259          it != end; it++) {
260         if (!(gr = getgrgid (*it)))
261             continue;
262         for (char **mem = gr->gr_mem; *mem; mem++) {
263             if (!strcmp (*mem, pw->pw_name))
264                 return true;
265         }
266     }
267     return false;
268 }
269 
270 #elif defined ZMQ_HAVE_LOCAL_PEERCRED
271 
filter(fd_t sock_)272 bool zmq::ipc_listener_t::filter (fd_t sock_)
273 {
274     if (options.ipc_uid_accept_filters.empty ()
275         && options.ipc_gid_accept_filters.empty ())
276         return true;
277 
278     struct xucred cred;
279     socklen_t size = sizeof (cred);
280 
281     if (getsockopt (sock_, 0, LOCAL_PEERCRED, &cred, &size))
282         return false;
283     if (cred.cr_version != XUCRED_VERSION)
284         return false;
285     if (options.ipc_uid_accept_filters.find (cred.cr_uid)
286         != options.ipc_uid_accept_filters.end ())
287         return true;
288     for (int i = 0; i < cred.cr_ngroups; i++) {
289         if (options.ipc_gid_accept_filters.find (cred.cr_groups[i])
290             != options.ipc_gid_accept_filters.end ())
291             return true;
292     }
293 
294     return false;
295 }
296 
297 #endif
298 
accept()299 zmq::fd_t zmq::ipc_listener_t::accept ()
300 {
301     //  Accept one connection and deal with different failure modes.
302     //  The situation where connection cannot be accepted due to insufficient
303     //  resources is considered valid and treated by ignoring the connection.
304     zmq_assert (_s != retired_fd);
305 #if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4
306     fd_t sock = ::accept4 (_s, NULL, NULL, SOCK_CLOEXEC);
307 #else
308     struct sockaddr_storage ss;
309     memset (&ss, 0, sizeof (ss));
310 #if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS
311     int ss_len = sizeof (ss);
312 #else
313     socklen_t ss_len = sizeof (ss);
314 #endif
315 
316     const fd_t sock =
317       ::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len);
318 #endif
319     if (sock == retired_fd) {
320 #if defined ZMQ_HAVE_WINDOWS
321         const int last_error = WSAGetLastError ();
322         wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET
323                     || last_error == WSAEMFILE || last_error == WSAENOBUFS);
324 #else
325         errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
326                       || errno == ECONNABORTED || errno == EPROTO
327                       || errno == ENFILE);
328 #endif
329         return retired_fd;
330     }
331 
332     make_socket_noninheritable (sock);
333 
334     // IPC accept() filters
335 #if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
336     if (!filter (sock)) {
337         int rc = ::close (sock);
338         errno_assert (rc == 0);
339         return retired_fd;
340     }
341 #endif
342 
343     if (zmq::set_nosigpipe (sock)) {
344 #ifdef ZMQ_HAVE_WINDOWS
345         const int rc = closesocket (sock);
346         wsa_assert (rc != SOCKET_ERROR);
347 #else
348         int rc = ::close (sock);
349         errno_assert (rc == 0);
350 #endif
351         return retired_fd;
352     }
353 
354     return sock;
355 }
356 
357 #endif
358