diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-04-27 14:14:08 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2018-04-30 11:33:10 -0700 |
| commit | 2b0d31553e542c130e2595ff9a3ac9756a2c1619 (patch) | |
| tree | f9ef54cfe7c4336e4765091445aa4d86a53645b5 /src/platform | |
| parent | 88c7a328dfaca4a9fce13ebbc4bce6b24d048c3e (diff) | |
| download | nng-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/platform')
| -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 |
5 files changed, 110 insertions, 18 deletions
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, |
