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;
+}