aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/platform.h19
-rw-r--r--src/platform/posix/posix_aio.h3
-rw-r--r--src/platform/posix/posix_epdesc.c55
-rw-r--r--src/platform/posix/posix_ipc.c16
-rw-r--r--src/platform/posix/posix_tcp.c2
-rw-r--r--src/platform/windows/win_ipc.c52
-rw-r--r--src/transport/ipc/ipc.c47
-rw-r--r--src/transport/ipc/ipc.h16
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