fixes #556 Support security attributes for Windows IPC
Allow passing security descriptor to the ipc channel in Windows.
The tunables NN_IPC_SEC_ATTR, as well as NN_IPC_OUTBUFSZ and
NN_IPC_INBUFSZ are exposed on Windows when using the named pipe
based IPC.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f0bd32..91d86cd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -107,6 +107,7 @@
add_libnanomsg_test (shutdown)
add_libnanomsg_test (cmsg)
add_libnanomsg_test (bug328)
+add_libnanomsg_test (win_sec_attr)
# Build the performance tests.
diff --git a/src/aio/usock_win.h b/src/aio/usock_win.h
index 615831b..e44576d 100644
--- a/src/aio/usock_win.h
+++ b/src/aio/usock_win.h
@@ -75,6 +75,13 @@
/* For now we allocate a new buffer for each write to a named pipe. */
void *pipesendbuf;
+ /* Pointer to the security attribute structure */
+ SECURITY_ATTRIBUTES *sec_attr;
+
+ /* Out Buffer and In Buffer size */
+ int outbuffersz;
+ int inbuffersz;
+
/* Errno remembered in NN_USOCK_ERROR state */
int errnum;
};
diff --git a/src/aio/usock_win.inc b/src/aio/usock_win.inc
index 5eedfb6..6cc9b24 100644
--- a/src/aio/usock_win.inc
+++ b/src/aio/usock_win.inc
@@ -93,6 +93,11 @@
/* NamedPipe-related stuff. */
memset (&self->pipename, 0, sizeof (self->pipename));
self->pipesendbuf = NULL;
+ memset (&self->sec_attr, 0, sizeof (SECURITY_ATTRIBUTES));
+
+ /* default size for both in and out buffers is 4096 */
+ self->outbuffersz = 4096;
+ self->inbuffersz = 4096;
}
void nn_usock_term (struct nn_usock *self)
@@ -501,22 +506,19 @@
static void nn_usock_create_pipe (struct nn_usock *self, const char *name)
{
char fullname [256];
-
/* First, create a fully qualified name for the named pipe. */
_snprintf(fullname, sizeof (fullname), "\\\\.\\pipe\\%s", name);
- /* TODO: Expose custom nOutBufferSize, nInBufferSize, nDefaultTimeOut,
- lpSecurityAttributes */
self->p = CreateNamedPipeA (
(char*) fullname,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
PIPE_UNLIMITED_INSTANCES,
- 4096,
- 4096,
+ self->outbuffersz,
+ self->inbuffersz,
0,
- NULL);
+ self->sec_attr);
/* TODO: How to properly handle self->p == INVALID_HANDLE_VALUE? */
win_assert (self->p != INVALID_HANDLE_VALUE);
@@ -535,12 +537,11 @@
/* First, create a fully qualified name for the named pipe. */
_snprintf(fullname, sizeof (fullname), "\\\\.\\pipe\\%s", name);
- /* TODO: Expose a way to pass lpSecurityAttributes */
self->p = CreateFileA (
fullname,
GENERIC_READ | GENERIC_WRITE,
0,
- NULL,
+ self->sec_attr,
OPEN_ALWAYS,
FILE_FLAG_OVERLAPPED,
NULL);
diff --git a/src/core/sock.c b/src/core/sock.c
index aa91d6a..16421ad 100644
--- a/src/core/sock.c
+++ b/src/core/sock.c
@@ -159,6 +159,12 @@
/* Should be pretty much enough space for just the number */
sprintf(self->socket_name, "%d", fd);
+ /* Security attribute */
+ self->sec_attr = NULL;
+ self->sec_attr_size = 0;
+ self->inbuffersz = 4096;
+ self->outbuffersz = 4096;
+
/* The transport-specific options are not initialised immediately,
rather, they are allocated later on when needed. */
for (i = 0; i != NN_MAX_TRANSPORT; ++i)
diff --git a/src/core/sock.h b/src/core/sock.h
index 832049a..b0ebd6b 100644
--- a/src/core/sock.h
+++ b/src/core/sock.h
@@ -141,6 +141,13 @@
/* The socket name for statistics */
char socket_name[64];
+
+ /* Win32 Security Attribute */
+ void * sec_attr;
+ size_t sec_attr_size;
+ int outbuffersz;
+ int inbuffersz;
+
};
/* Initialise the socket. */
diff --git a/src/ipc.h b/src/ipc.h
index fc5cb62..684dcee 100644
--- a/src/ipc.h
+++ b/src/ipc.h
@@ -29,6 +29,11 @@
#define NN_IPC -2
+// The object set here must be valid as long as you are using the socket
+#define NN_IPC_SEC_ATTR 1
+#define NN_IPC_OUTBUFSZ 2
+#define NN_IPC_INBUFSZ 3
+
#ifdef __cplusplus
}
#endif
diff --git a/src/transports/ipc/aipc.c b/src/transports/ipc/aipc.c
index 81e0b35..af0ab43 100644
--- a/src/transports/ipc/aipc.c
+++ b/src/transports/ipc/aipc.c
@@ -81,6 +81,7 @@
void nn_aipc_start (struct nn_aipc *self, struct nn_usock *listener)
{
+ size_t sz;
nn_assert_state (self, NN_AIPC_STATE_IDLE);
/* Take ownership of the listener socket. */
@@ -89,6 +90,14 @@
self->listener_owner.fsm = &self->fsm;
nn_usock_swap_owner (listener, &self->listener_owner);
+#if defined NN_HAVE_WINDOWS
+ /* Get/Set security attribute pointer*/
+ nn_epbase_getopt (self->epbase, NN_IPC, NN_IPC_SEC_ATTR, &self->usock.sec_attr, &sz);
+
+ nn_epbase_getopt (self->epbase, NN_IPC, NN_IPC_OUTBUFSZ, &self->usock.outbuffersz, &sz);
+ nn_epbase_getopt (self->epbase, NN_IPC, NN_IPC_INBUFSZ, &self->usock.inbuffersz, &sz);
+#endif
+
/* Start the state machine. */
nn_fsm_start (&self->fsm);
}
diff --git a/src/transports/ipc/aipc.h b/src/transports/ipc/aipc.h
index d0b57c8..2c95a7f 100644
--- a/src/transports/ipc/aipc.h
+++ b/src/transports/ipc/aipc.h
@@ -26,6 +26,7 @@
#include "sipc.h"
#include "../../transport.h"
+#include "../../ipc.h"
#include "../../aio/fsm.h"
#include "../../aio/usock.h"
diff --git a/src/transports/ipc/cipc.c b/src/transports/ipc/cipc.c
index 86df2a8..1f6adad 100644
--- a/src/transports/ipc/cipc.c
+++ b/src/transports/ipc/cipc.c
@@ -416,6 +416,14 @@
ss.ss_family = AF_UNIX;
strncpy (un->sun_path, addr, sizeof (un->sun_path));
+#if defined NN_HAVE_WINDOWS
+ /* Get/Set security attribute pointer*/
+ nn_epbase_getopt (&self->epbase, NN_IPC, NN_IPC_SEC_ATTR, &self->usock.sec_attr, &sz);
+
+ nn_epbase_getopt (&self->epbase, NN_IPC, NN_IPC_OUTBUFSZ, &self->usock.outbuffersz, &sz);
+ nn_epbase_getopt (&self->epbase, NN_IPC, NN_IPC_INBUFSZ, &self->usock.inbuffersz, &sz);
+#endif
+
/* Start connecting. */
nn_usock_connect (&self->usock, (struct sockaddr*) &ss,
sizeof (struct sockaddr_un));
diff --git a/src/transports/ipc/cipc.h b/src/transports/ipc/cipc.h
index 3d5b6d7..4036bba 100644
--- a/src/transports/ipc/cipc.h
+++ b/src/transports/ipc/cipc.h
@@ -24,6 +24,7 @@
#define NN_CIPC_INCLUDED
#include "../../transport.h"
+#include "../../ipc.h"
/* State machine managing connected IPC socket. */
diff --git a/src/transports/ipc/ipc.c b/src/transports/ipc/ipc.c
index 1c3012f..c4fe740 100644
--- a/src/transports/ipc/ipc.c
+++ b/src/transports/ipc/ipc.c
@@ -31,6 +31,7 @@
#include "../../utils/alloc.h"
#include "../../utils/fast.h"
#include "../../utils/list.h"
+#include "../../utils/cont.h"
#include <string.h>
#if defined NN_HAVE_WINDOWS
@@ -41,9 +42,32 @@
#include <unistd.h>
#endif
+/* IPC-specific socket options. */
+struct nn_ipc_optset {
+ struct nn_optset base;
+
+ /* Win32 Security Attribute */
+ void* sec_attr;
+
+ int outbuffersz;
+ int inbuffersz;
+};
+
+static void nn_ipc_optset_destroy (struct nn_optset *self);
+static int nn_ipc_optset_setopt (struct nn_optset *self, int option,
+ const void *optval, size_t optvallen);
+static int nn_ipc_optset_getopt (struct nn_optset *self, int option,
+ void *optval, size_t *optvallen);
+static const struct nn_optset_vfptr nn_ipc_optset_vfptr = {
+ nn_ipc_optset_destroy,
+ nn_ipc_optset_setopt,
+ nn_ipc_optset_getopt
+};
+
/* nn_transport interface. */
static int nn_ipc_bind (void *hint, struct nn_epbase **epbase);
static int nn_ipc_connect (void *hint, struct nn_epbase **epbase);
+static struct nn_optset *nn_ipc_optset (void);
static struct nn_transport nn_ipc_vfptr = {
"ipc",
@@ -52,7 +76,7 @@
NULL,
nn_ipc_bind,
nn_ipc_connect,
- NULL,
+ nn_ipc_optset,
NN_LIST_ITEM_INITIALIZER
};
@@ -68,3 +92,76 @@
return nn_cipc_create (hint, epbase);
}
+static struct nn_optset *nn_ipc_optset ()
+{
+ struct nn_ipc_optset *optset;
+
+ optset = nn_alloc (sizeof (struct nn_ipc_optset), "optset (ipc)");
+ alloc_assert (optset);
+ optset->base.vfptr = &nn_ipc_optset_vfptr;
+
+ /* Default values for the IPC options */
+ optset->sec_attr = NULL;
+ optset->outbuffersz = 4096;
+ optset->inbuffersz = 4096;
+
+ return &optset->base;
+}
+
+static void nn_ipc_optset_destroy (struct nn_optset *self)
+{
+ struct nn_ipc_optset *optset;
+
+ optset = nn_cont (self, struct nn_ipc_optset, base);
+ nn_free (optset);
+}
+
+static int nn_ipc_optset_setopt (struct nn_optset *self, int option,
+ const void *optval, size_t optvallen)
+{
+ struct nn_ipc_optset *optset;
+
+ optset = nn_cont (self, struct nn_ipc_optset, base);
+ if (optvallen < sizeof (int)) {
+ return -EINVAL;
+ }
+
+ switch (option) {
+ case NN_IPC_SEC_ATTR:
+ optset->sec_attr = (void *)optval;
+ return 0;
+ case NN_IPC_OUTBUFSZ:
+ optset->outbuffersz = *(int *)optval;
+ return 0;
+ case NN_IPC_INBUFSZ:
+ optset->inbuffersz = *(int *)optval;
+ return 0;
+ default:
+ return -ENOPROTOOPT;
+ }
+}
+
+static int nn_ipc_optset_getopt (struct nn_optset *self, int option,
+ void *optval, size_t *optvallen)
+{
+ struct nn_ipc_optset *optset;
+
+ optset = nn_cont (self, struct nn_ipc_optset, base);
+
+ switch (option) {
+ case NN_IPC_SEC_ATTR:
+ memcpy(optval, &optset->sec_attr, sizeof(optset->sec_attr));
+ *optvallen = sizeof(optset->sec_attr);
+ return 0;
+ case NN_IPC_OUTBUFSZ:
+ *(int *)optval = optset->outbuffersz;
+ *optvallen = sizeof (int);
+ return 0;
+ case NN_IPC_INBUFSZ:
+ *(int *)optval = optset->inbuffersz;
+ *optvallen = sizeof (int);
+ return 0;
+ default:
+ return -ENOPROTOOPT;
+ }
+}
diff --git a/tests/win_sec_attr.c b/tests/win_sec_attr.c
new file mode 100644
index 0000000..4166e4c
--- /dev/null
+++ b/tests/win_sec_attr.c
@@ -0,0 +1,152 @@
+/*
+ Copyright (c) 2015 Timothee "TTimo" Besset All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+
+
+*/
+
+#include "../src/nn.h"
+#include "../src/pair.h"
+#include "../src/pubsub.h"
+#include "../src/ipc.h"
+
+#include "testutil.h"
+
+#include <AccCtrl.h>
+#include <Sddl.h>
+#include <Aclapi.h>
+
+/* Windows only. Custom SECURITY_ATTRIBUTES on a socket. */
+
+#define PIPE_NAME "win_sec_attr.ipc"
+#define SOCKET_ADDRESS "ipc://" PIPE_NAME
+
+int main ()
+{
+ int sb;
+ int sc;
+ SECURITY_ATTRIBUTES sec;
+ BOOL ret;
+ SID SIDAuthUsers;
+ DWORD SIDSize;
+ EXPLICIT_ACCESS xa;
+ PACL pACL;
+ DWORD ret2;
+ int ret3;
+ void * void_ret_value = NULL;
+ size_t void_ret_size = sizeof(void_ret_value);
+ HANDLE pipeHandle = NULL;
+ PSID pSidOwner = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+
+ BOOL equal = FALSE;
+ BOOL bret = FALSE;
+ ACL* dacl = NULL;
+ PACE_HEADER ace = NULL;
+ PACCESS_ALLOWED_ACE allowed_ace = NULL;
+ PSID the_sid = NULL;
+ SECURITY_DESCRIPTOR* sd = NULL;
+
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, SOCKET_ADDRESS);
+
+ sb = test_socket (AF_SP, NN_PAIR);
+
+ memset (&sec, 0, sizeof(sec));
+ sec.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
+ ret = InitializeSecurityDescriptor (sec.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
+ nn_assert (ret);
+
+ SIDSize = sizeof (SIDAuthUsers);
+ ret = CreateWellKnownSid (WinAuthenticatedUserSid, NULL, &SIDAuthUsers, &SIDSize);
+ nn_assert (ret);
+
+ xa.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
+ xa.grfAccessMode = SET_ACCESS;
+ xa.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ xa.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ xa.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+ xa.Trustee.ptstrName = (LPSTR) &SIDAuthUsers;
+ ret2 = SetEntriesInAcl (1, &xa, NULL, &pACL);
+ nn_assert (ret2 == ERROR_SUCCESS);
+
+ ret = SetSecurityDescriptorDacl (sec.lpSecurityDescriptor, TRUE, pACL, FALSE);
+ nn_assert (ret);
+
+ sec.nLength = sizeof(sec);
+ sec.bInheritHandle = TRUE;
+
+ ret3 = nn_setsockopt (sb, NN_IPC, NN_IPC_SEC_ATTR, (void*)&sec, sizeof(&sec));
+ nn_assert (ret3 == 0);
+ test_bind (sb, SOCKET_ADDRESS);
+
+ nn_sleep (200);
+
+ test_send (sc, "0123456789012345678901234567890123456789");
+ test_recv (sb, "0123456789012345678901234567890123456789");
+
+ ret3 = nn_getsockopt(sb, NN_IPC, NN_IPC_SEC_ATTR, &void_ret_value, &void_ret_size);
+ nn_assert(ret3 == 0);
+ nn_assert(void_ret_value == &sec);
+
+
+ // verify that the pipe has the same security descriptor that we set by comparing the ace of the kernel object
+ // to the one we created it with
+ pipeHandle = CreateFileA(
+ "\\\\.\\\\pipe\\" PIPE_NAME,
+ READ_CONTROL,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL
+ );
+
+ nn_assert(pipeHandle != INVALID_HANDLE_VALUE);
+
+
+ ret2 = GetSecurityInfo(pipeHandle, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &sd);
+
+ nn_assert(ret2 == ERROR_SUCCESS);
+ nn_assert(1 == dacl->AceCount);
+
+ bret = GetAce(dacl, 0, &ace);
+
+ nn_assert(bret == TRUE);
+ nn_assert(ace->AceType == ACCESS_ALLOWED_ACE_TYPE);
+
+ allowed_ace = (PACCESS_ALLOWED_ACE)ace;
+ the_sid = (PSID)&(allowed_ace->SidStart);
+
+ nn_assert(IsValidSid(the_sid));
+
+ equal = EqualSid((PSID)&(allowed_ace->SidStart), &SIDAuthUsers);
+ nn_assert(equal);
+ LocalFree(dacl);
+
+ test_close (sc);
+ test_close (sb);
+
+ LocalFree (pACL);
+
+ free (sec.lpSecurityDescriptor);
+
+ return 0;
+}