summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-04-27 14:14:08 -0700
committerGarrett D'Amore <garrett@damore.org>2018-04-30 11:33:10 -0700
commit2b0d31553e542c130e2595ff9a3ac9756a2c1619 (patch)
treef9ef54cfe7c4336e4765091445aa4d86a53645b5 /src
parent88c7a328dfaca4a9fce13ebbc4bce6b24d048c3e (diff)
downloadnng-2b0d31553e542c130e2595ff9a3ac9756a2c1619.tar.gz
nng-2b0d31553e542c130e2595ff9a3ac9756a2c1619.tar.bz2
nng-2b0d31553e542c130e2595ff9a3ac9756a2c1619.zip
fixes #6 Security attributes support
fixes #382 Permissions support for IPC on POSIX This adds support for permission management on Windows and POSIX systems. There are two different properties, and they are very different. Tests and documentation are included.
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