diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/platform.h | 19 | ||||
| -rw-r--r-- | src/platform/posix/posix_aio.h | 3 | ||||
| -rw-r--r-- | src/platform/posix/posix_epdesc.c | 55 | ||||
| -rw-r--r-- | src/platform/posix/posix_ipc.c | 16 | ||||
| -rw-r--r-- | src/platform/posix/posix_tcp.c | 2 | ||||
| -rw-r--r-- | src/platform/windows/win_ipc.c | 52 | ||||
| -rw-r--r-- | src/transport/ipc/ipc.c | 47 | ||||
| -rw-r--r-- | src/transport/ipc/ipc.h | 16 |
8 files changed, 190 insertions, 20 deletions
diff --git a/src/core/platform.h b/src/core/platform.h index d6191038..671556d8 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -284,6 +284,8 @@ typedef struct nni_plat_ipc_ep nni_plat_ipc_ep; typedef struct nni_plat_ipc_pipe nni_plat_ipc_pipe; // nni_plat_ipc_ep_init creates a new endpoint associated with the url. +// The final field is the mode, either for dialing (NNI_EP_MODE_DIAL) or +// listening (NNI_EP_MODE_LISTEN). extern int nni_plat_ipc_ep_init(nni_plat_ipc_ep **, const nni_sockaddr *, int); // nni_plat_ipc_ep_fini closes the endpoint and releases resources. @@ -306,6 +308,23 @@ extern void nni_plat_ipc_ep_accept(nni_plat_ipc_ep *, nni_aio *); // An accepted connection will be passed back in the a_pipe member. extern void nni_plat_ipc_ep_connect(nni_plat_ipc_ep *, nni_aio *); +// nni_plat_ipc_ep_set_security_descriptor sets the Windows security +// descriptor. This is *only* supported for Windows platforms. All +// others return NNG_ENOTSUP. The void argument is a pointer to +// a SECURITY_DESCRIPTOR object, and must be valid. +extern int nni_plat_ipc_ep_set_security_descriptor(nni_plat_ipc_ep *, void *); + +// nni_plat_ipc_ep_set_permissions sets UNIX style permissions +// on the named pipes. This basically just does a chmod() on the +// named pipe, and is only supported o the server side, and only on +// systems that support this (POSIX, not Windows). Note that changing +// ownership is not supported at this time. Most systems use only +// 16-bits, the lower 12 of which are user, group, and other, e.g. +// 0640 gives read/write access to user, read to group, and prevents +// any other user from accessing it. This option only has meaning +// for listeners, on dialers it is ignored. +extern int nni_plat_ipc_ep_set_permissions(nni_plat_ipc_ep *, uint32_t); + // nni_plat_ipc_pipe_fini closes the pipe, and releases all resources // associated with it. extern void nni_plat_ipc_pipe_fini(nni_plat_ipc_pipe *); diff --git a/src/platform/posix/posix_aio.h b/src/platform/posix/posix_aio.h index 15dae06c..3954f225 100644 --- a/src/platform/posix/posix_aio.h +++ b/src/platform/posix/posix_aio.h @@ -34,7 +34,7 @@ extern int nni_posix_pipedesc_sockname(nni_posix_pipedesc *, nni_sockaddr *); extern int nni_posix_pipedesc_set_nodelay(nni_posix_pipedesc *, bool); extern int nni_posix_pipedesc_set_keepalive(nni_posix_pipedesc *, bool); -extern int nni_posix_epdesc_init(nni_posix_epdesc **); +extern int nni_posix_epdesc_init(nni_posix_epdesc **, int); extern void nni_posix_epdesc_set_local(nni_posix_epdesc *, void *, size_t); extern void nni_posix_epdesc_set_remote(nni_posix_epdesc *, void *, size_t); extern void nni_posix_epdesc_fini(nni_posix_epdesc *); @@ -43,5 +43,6 @@ extern void nni_posix_epdesc_connect(nni_posix_epdesc *, nni_aio *); extern int nni_posix_epdesc_listen(nni_posix_epdesc *); extern void nni_posix_epdesc_accept(nni_posix_epdesc *, nni_aio *); extern int nni_posix_epdesc_sockname(nni_posix_epdesc *, nni_sockaddr *); +extern int nni_posix_epdesc_set_permissions(nni_posix_epdesc *, mode_t); #endif // PLATFORM_POSIX_AIO_H diff --git a/src/platform/posix/posix_epdesc.c b/src/platform/posix/posix_epdesc.c index f97f12b8..0f63304f 100644 --- a/src/platform/posix/posix_epdesc.c +++ b/src/platform/posix/posix_epdesc.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/un.h> @@ -38,10 +39,13 @@ struct nni_posix_epdesc { nni_list connectq; nni_list acceptq; bool closed; + bool started; struct sockaddr_storage locaddr; struct sockaddr_storage remaddr; socklen_t loclen; socklen_t remlen; + mode_t perms; // UNIX sockets only + int mode; // end point mode (dialer/listener) nni_mtx mtx; }; @@ -287,17 +291,35 @@ nni_posix_epdesc_listen(nni_posix_epdesc *ed) #endif if (bind(fd, (struct sockaddr *) ss, len) < 0) { - nni_mtx_unlock(&ed->mtx); rv = nni_plat_errno(errno); + nni_mtx_unlock(&ed->mtx); (void) close(fd); return (rv); } + // For UNIX domain sockets, optionally set the permission bits. + // This is done after the bind and before listen, and on the file + // rather than the file descriptor. + // Experiments have shown that chmod() works correctly, provided that + // it is done *before* the listen() operation, whereas fchmod seems to + // have no impact. This behavior was observed on both macOS and Linux. + // YMMV on other platforms. + if ((ss->ss_family == AF_UNIX) && (ed->perms != 0)) { + struct sockaddr_un *sun = (void *) ss; + mode_t perms = ed->perms & ~(S_IFMT); + if ((rv = chmod(sun->sun_path, perms)) != 0) { + rv = nni_plat_errno(errno); + nni_mtx_unlock(&ed->mtx); + close(fd); + return (rv); + } + } + // Listen -- 128 depth is probably sufficient. If it isn't, other // bad things are going to happen. if (listen(fd, 128) != 0) { - nni_mtx_unlock(&ed->mtx); rv = nni_plat_errno(errno); + nni_mtx_unlock(&ed->mtx); (void) close(fd); return (rv); } @@ -311,6 +333,7 @@ nni_posix_epdesc_listen(nni_posix_epdesc *ed) nni_mtx_unlock(&ed->mtx); return (rv); } + ed->started = true; nni_mtx_unlock(&ed->mtx); return (0); } @@ -384,6 +407,7 @@ nni_posix_epdesc_connect(nni_posix_epdesc *ed, nni_aio *aio) if ((rv = connect(fd, (void *) &ed->remaddr, ed->remlen)) == 0) { // Immediate connect, cool! This probably only happens on // loopback, and probably not on every platform. + ed->started = true; nni_posix_epdesc_finish(aio, 0, fd); nni_mtx_unlock(&ed->mtx); return; @@ -403,6 +427,7 @@ nni_posix_epdesc_connect(nni_posix_epdesc *ed, nni_aio *aio) // We have to submit to the pollq, because the connection is pending. ed->node.fd = fd; + ed->started = true; if ((rv = nni_posix_pollq_add(&ed->node)) != 0) { ed->node.fd = -1; nni_mtx_unlock(&ed->mtx); @@ -418,7 +443,7 @@ nni_posix_epdesc_connect(nni_posix_epdesc *ed, nni_aio *aio) } int -nni_posix_epdesc_init(nni_posix_epdesc **edp) +nni_posix_epdesc_init(nni_posix_epdesc **edp, int mode) { nni_posix_epdesc *ed; int rv; @@ -439,6 +464,9 @@ nni_posix_epdesc_init(nni_posix_epdesc **edp) ed->node.data = ed; ed->node.fd = -1; ed->closed = false; + ed->started = false; + ed->perms = 0; // zero means use default (no change) + ed->mode = mode; nni_aio_list_init(&ed->connectq); nni_aio_list_init(&ed->acceptq); @@ -476,6 +504,27 @@ nni_posix_epdesc_set_remote(nni_posix_epdesc *ed, void *sa, size_t len) nni_mtx_unlock(&ed->mtx); } +int +nni_posix_epdesc_set_permissions(nni_posix_epdesc *ed, mode_t mode) +{ + nni_mtx_lock(&ed->mtx); + if (ed->mode != NNI_EP_MODE_LISTEN) { + nni_mtx_unlock(&ed->mtx); + return (NNG_ENOTSUP); + } + if (ed->started) { + nni_mtx_unlock(&ed->mtx); + return (NNG_EBUSY); + } + if ((mode & S_IFMT) != 0) { + nni_mtx_unlock(&ed->mtx); + return (NNG_EINVAL); + } + ed->perms = mode | S_IFSOCK; // we set IFSOCK to ensure non-zero + nni_mtx_unlock(&ed->mtx); + return (0); +} + void nni_posix_epdesc_fini(nni_posix_epdesc *ed) { diff --git a/src/platform/posix/posix_ipc.c b/src/platform/posix/posix_ipc.c index f7cfc0db..c1bb9292 100644 --- a/src/platform/posix/posix_ipc.c +++ b/src/platform/posix/posix_ipc.c @@ -49,7 +49,7 @@ nni_plat_ipc_ep_init(nni_plat_ipc_ep **epp, const nni_sockaddr *sa, int mode) int rv; struct sockaddr_un sun; - if ((rv = nni_posix_epdesc_init(&ed)) != 0) { + if ((rv = nni_posix_epdesc_init(&ed, mode)) != 0) { return (rv); } switch (mode) { @@ -87,6 +87,20 @@ nni_plat_ipc_ep_close(nni_plat_ipc_ep *ep) nni_posix_epdesc_close((void *) ep); } +int +nni_plat_ipc_ep_set_permissions(nni_plat_ipc_ep *ep, uint32_t bits) +{ + return (nni_posix_epdesc_set_permissions((void *) ep, (mode_t) bits)); +} + +int +nni_plat_ipc_ep_set_security_descriptor(nni_plat_ipc_ep *ep, void *attr) +{ + NNI_ARG_UNUSED(ep); + NNI_ARG_UNUSED(attr); + return (NNG_ENOTSUP); +} + // UNIX DOMAIN SOCKETS -- these have names in the file namespace. // We are going to check to see if there was a name already there. // If there was, and nothing is listening (ECONNREFUSED), then we diff --git a/src/platform/posix/posix_tcp.c b/src/platform/posix/posix_tcp.c index 81ec330b..c00f9433 100644 --- a/src/platform/posix/posix_tcp.c +++ b/src/platform/posix/posix_tcp.c @@ -36,7 +36,7 @@ nni_plat_tcp_ep_init(nni_plat_tcp_ep **epp, const nni_sockaddr *lsa, NNI_ARG_UNUSED(mode); - if ((rv = nni_posix_epdesc_init(&ed)) != 0) { + if ((rv = nni_posix_epdesc_init(&ed, mode)) != 0) { return (rv); } diff --git a/src/platform/windows/win_ipc.c b/src/platform/windows/win_ipc.c index d372f639..7d118672 100644 --- a/src/platform/windows/win_ipc.c +++ b/src/platform/windows/win_ipc.c @@ -21,14 +21,15 @@ struct nni_plat_ipc_pipe { }; struct nni_plat_ipc_ep { - char path[NNG_MAXADDRLEN + 16]; - nni_sockaddr addr; - int mode; - int started; - HANDLE p; // accept side only - nni_win_event acc_ev; // accept side only - nni_aio * con_aio; // conn side only - nni_list_node node; // conn side uses this + char path[NNG_MAXADDRLEN + 16]; + nni_sockaddr addr; + int mode; + bool started; + HANDLE p; // accept side only + nni_win_event acc_ev; // accept side only + nni_aio * con_aio; // conn side only + nni_list_node node; // conn side uses this + SECURITY_ATTRIBUTES sec_attr; }; static int nni_win_ipc_pipe_start(nni_win_event *, nni_aio *); @@ -203,7 +204,10 @@ nni_plat_ipc_ep_init(nni_plat_ipc_ep **epp, const nni_sockaddr *sa, int mode) } ZeroMemory(ep, sizeof(*ep)); - ep->mode = mode; + ep->mode = mode; + ep->sec_attr.nLength = sizeof(ep->sec_attr); + ep->sec_attr.lpSecurityDescriptor = NULL; + ep->sec_attr.bInheritHandle = FALSE; NNI_LIST_NODE_INIT(&ep->node); ep->addr = *sa; @@ -214,6 +218,30 @@ nni_plat_ipc_ep_init(nni_plat_ipc_ep **epp, const nni_sockaddr *sa, int mode) } int +nni_plat_ipc_ep_set_permissions(nni_plat_ipc_ep *ep, uint32_t bits) +{ + NNI_ARG_UNUSED(ep); + NNI_ARG_UNUSED(bits); + return (NNG_ENOTSUP); +} + +int +nni_plat_ipc_ep_set_security_descriptor(nni_plat_ipc_ep *ep, void *desc) +{ + if (ep->started) { + return (NNG_EBUSY); + } + if (ep->mode != NNI_EP_MODE_LISTEN) { + return (NNG_ENOTSUP); + } + if (!IsValidSecurityDescriptor((SECURITY_DESCRIPTOR *) desc)) { + return (NNG_EINVAL); + } + ep->sec_attr.lpSecurityDescriptor = desc; + return (0); +} + +int nni_plat_ipc_ep_listen(nni_plat_ipc_ep *ep) { int rv; @@ -233,7 +261,7 @@ nni_plat_ipc_ep_listen(nni_plat_ipc_ep *ep) FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &ep->sec_attr); if (p == INVALID_HANDLE_VALUE) { if ((rv = GetLastError()) == ERROR_ACCESS_DENIED) { rv = NNG_EADDRINUSE; @@ -252,7 +280,7 @@ nni_plat_ipc_ep_listen(nni_plat_ipc_ep *ep) } ep->p = p; - ep->started = 1; + ep->started = true; return (0); failed: @@ -281,7 +309,7 @@ nni_win_ipc_acc_finish(nni_win_event *evt, nni_aio *aio) PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &ep->sec_attr); if (newp == INVALID_HANDLE_VALUE) { rv = nni_win_error(GetLastError()); // We connected, but as we cannot get a new pipe, diff --git a/src/transport/ipc/ipc.c b/src/transport/ipc/ipc.c index 61b89f20..3dbccb50 100644 --- a/src/transport/ipc/ipc.c +++ b/src/transport/ipc/ipc.c @@ -13,6 +13,7 @@ #include <string.h> #include "core/nng_impl.h" +#include "ipc.h" // IPC transport. Platform specific IPC operations must be // supplied as well. Normally the IPC is UNIX domain sockets or @@ -739,6 +740,40 @@ nni_ipc_ep_get_addr(void *arg, void *data, size_t *szp, int typ) return (nni_copyout_sockaddr(&ep->sa, data, szp, typ)); } +static int +nni_ipc_ep_setopt_permissions(void *arg, const void *data, size_t sz, int typ) +{ + nni_ipc_ep *ep = arg; + int val; + int rv; + + // Probably we could further limit this -- most systems don't have + // meaningful chmod beyond the lower 9 bits. + rv = nni_copyin_int(&val, data, sz, 0, 0x7FFFFFFF, typ); + if ((rv == 0) && (ep != NULL)) { + rv = nni_plat_ipc_ep_set_permissions(ep->iep, val); + } + return (rv); +} + +static int +nni_ipc_ep_setopt_security_desc( + void *arg, const void *data, size_t sz, int typ) +{ + nni_ipc_ep *ep = arg; + void * ptr; + int rv; + + if ((rv = nni_copyin_ptr((void **) &ptr, data, sz, typ)) != 0) { + return (rv); + } + + if (ep == NULL) { + return (0); + } + return (nni_plat_ipc_ep_set_security_descriptor(ep->iep, ptr)); +} + static nni_tran_pipe_option nni_ipc_pipe_options[] = { { .po_name = NNG_OPT_REMADDR, @@ -779,6 +814,18 @@ static nni_tran_ep_option nni_ipc_ep_options[] = { .eo_getopt = nni_ipc_ep_get_addr, .eo_setopt = NULL, }, + { + .eo_name = NNG_OPT_IPC_SECURITY_DESCRIPTOR, + .eo_type = NNI_TYPE_POINTER, + .eo_getopt = NULL, + .eo_setopt = nni_ipc_ep_setopt_security_desc, + }, + { + .eo_name = NNG_OPT_IPC_PERMISSIONS, + .eo_type = NNI_TYPE_INT32, + .eo_getopt = NULL, + .eo_setopt = nni_ipc_ep_setopt_permissions, + }, // terminate list { .eo_name = NULL, diff --git a/src/transport/ipc/ipc.h b/src/transport/ipc/ipc.h index 4c4c5708..42cbdb08 100644 --- a/src/transport/ipc/ipc.h +++ b/src/transport/ipc/ipc.h @@ -1,6 +1,6 @@ // -// Copyright 2017 Garrett D'Amore <garrett@damore.org> -// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -16,4 +16,16 @@ NNG_DECL int nng_ipc_register(void); +// Security Descriptor. This option may only be set on listeners +// on the Windows platform, where the object is a pointer to a +// a Windows SECURITY_DESCRIPTOR. +#define NNG_OPT_IPC_SECURITY_DESCRIPTOR "ipc:security-descriptor" + +// Permissions bits. This option is only valid for listeners on +// POSIX platforms and others that honor UNIX style permission bits. +// Note that some platforms may not honor the permissions here, although +// at least Linux and macOS seem to do so. Check before you rely on +// this for security. +#define NNG_OPT_IPC_PERMISSIONS "ipc:permissions" + #endif // NNG_TRANSPORT_IPC_IPC_H |
