]> granicus.if.org Git - p11-kit/commitdiff
server: Port to Windows
authorDaiki Ueno <dueno@redhat.com>
Thu, 9 Mar 2017 16:45:31 +0000 (17:45 +0100)
committerDaiki Ueno <ueno@gnu.org>
Thu, 25 May 2017 12:13:01 +0000 (14:13 +0200)
Instead of a Unix domain socket on Unix, use a named pipe on Windows.

p11-kit/Makefile.am
p11-kit/server.c

index 6496e8d7c70f49e1ee13df6211c41a8cf298e95a..573bf61826cd9533e5e609ccf2a49e8d1ef145c5 100644 (file)
@@ -174,19 +174,24 @@ p11_kit_remote_LDADD = \
        libp11-kit.la \
        $(NULL)
 
-if !OS_WIN32
 private_PROGRAMS += p11-kit-server
 
 p11_kit_server_SOURCES = \
        p11-kit/server.c \
        $(NULL)
 
+if OS_WIN32
+       WIN32_LIBS = -ladvapi32
+else
+       WIN32_LIBS =
+endif
+
 p11_kit_server_LDADD = \
        libp11-tool.la \
        libp11-common.la \
        libp11-kit.la \
+       $(WIN32_LIBS) \
        $(NULL)
-endif
 
 # Tests ----------------------------------------------------------------
 
index ce7900a3d5d2a8cb181cead1e1289cc09457806b..5eb5bcd03631f1cffb4e47d99e9ca075ecba98ae 100644 (file)
 #include "path.h"
 #include "p11-kit.h"
 #include "remote.h"
-#include "unix-peer.h"
 #include "tool.h"
 
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
-#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
+#ifdef OS_UNIX
+
+#include "unix-peer.h"
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
@@ -69,10 +72,7 @@ typedef void (*sighandler_t)(int);
 #define SIGHANDLER_T sighandler_t
 #endif
 
-static bool need_children_cleanup = false;
-static bool terminate = false;
-static unsigned children_avail = 0;
-static bool quiet = false;
+#endif /* OS_UNIX */
 
 typedef struct {
        const char **tokens;
@@ -80,12 +80,88 @@ typedef struct {
        const char *provider;
 
        const char *socket_name;
+
+#ifdef OS_UNIX
        uid_t uid;
        gid_t gid;
 
        int socket;
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+       CK_FUNCTION_LIST *module;
+#endif /* OS_WIN32 */
 } Server;
 
+static void
+server_free (Server *server)
+{
+       if (server == NULL)
+               return;
+
+#ifdef OS_UNIX
+       if (server->socket >= 0)
+               close (server->socket);
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+       if (server->module)
+               p11_kit_module_release (server->module);
+#endif /* OS_WIN32 */
+
+       free (server);
+}
+
+static Server *
+server_new (const char **tokens, size_t n_tokens, const char *provider,
+           const char *socket_name)
+{
+       Server *server;
+
+       return_val_if_fail (tokens, NULL);
+       return_val_if_fail (n_tokens > 0, NULL);
+       return_val_if_fail (socket_name, NULL);
+
+       server = calloc (1, sizeof (Server));
+
+       if (server == NULL)
+               return NULL;
+
+       server->tokens = tokens;
+       server->n_tokens = n_tokens;
+       server->provider = provider;
+       server->socket_name = socket_name;
+
+#ifdef OS_UNIX
+       server->socket = -1;
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+       /* On Windows, we need to load module by ourselves as we don't
+        * launch "p11-kit remote" */
+       if (strncmp (tokens[0], "pkcs11:", 7) == 0) {
+               if (server->provider) {
+                       server->module = p11_kit_module_load (server->provider, 0);
+                       if (server->module == NULL)
+                               return NULL;
+               }
+       } else {
+               server->module = p11_kit_module_load (tokens[0], 0);
+               if (server->module == NULL)
+                       return NULL;
+       }
+#endif /* OS_WIN32 */
+
+       return server;
+}
+
+#ifdef OS_UNIX
+
+static bool need_children_cleanup = false;
+static bool terminate = false;
+static unsigned children_avail = 0;
+static bool quiet = false;
+
 static SIGHANDLER_T
 ocsignal (int signum, SIGHANDLER_T handler)
 {
@@ -240,35 +316,6 @@ check_credentials (int fd,
        return true;
 }
 
-static void
-server_free (Server *server)
-{
-       if (server == NULL)
-               return;
-       if (server->socket >= 0)
-               close (server->socket);
-       free (server);
-}
-
-static Server *
-server_new (const char **tokens, size_t n_tokens, const char *provider,
-           const char *socket_name)
-{
-       Server *server;
-
-       server = calloc (1, sizeof (Server));
-
-       if (server == NULL)
-               return NULL;
-
-       server->tokens = tokens;
-       server->n_tokens = n_tokens;
-       server->socket_name = socket_name;
-       server->socket = -1;
-
-       return server;
-}
-
 static int
 server_loop (Server *server,
             bool foreground,
@@ -637,3 +684,423 @@ main (int argc,
 
        return ret;
 }
+
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+
+#include <aclapi.h>
+#include <io.h>
+#include <process.h>
+#include <windows.h>
+
+#define BUFSIZE 4096
+
+static bool quiet = false;
+
+struct ThreadData {
+       HANDLE handle;
+       Server *server;
+};
+
+static DWORD WINAPI
+server_thread (LPVOID lpvParam)
+{
+       struct ThreadData *data = lpvParam;
+       Server *server = data->server;
+       int fd;
+
+       fd = _open_osfhandle ((intptr_t) data->handle, _O_BINARY);
+       if (fd < 0) {
+               free (data);
+               return 1;
+       }
+
+       if (server->module != NULL && server->provider == NULL) {
+               p11_kit_remote_serve_module (server->module, fd, fd);
+       } else {
+               p11_kit_remote_serve_tokens ((const char **)server->tokens,
+                                            server->n_tokens,
+                                            server->module,
+                                            fd, fd);
+       }
+
+       free (data);
+       _close (fd);
+       return 1;
+}
+
+static bool
+make_private_security_descriptor (DWORD permissions,
+                                 PSECURITY_DESCRIPTOR *psd,
+                                 PACL *acl);
+
+static int
+server_loop (Server *server)
+{
+       HANDLE hpipe, hthread;
+       BOOL connected = FALSE;
+       DWORD thread_id = 0;
+       SECURITY_ATTRIBUTES sa;
+       PACL acl;
+       struct ThreadData *data;
+
+       memset (&sa, 0, sizeof (SECURITY_ATTRIBUTES));
+       sa.nLength = sizeof (sa);
+       sa.bInheritHandle = FALSE;
+
+       if (!make_private_security_descriptor (GENERIC_READ | GENERIC_WRITE,
+                                              &sa.lpSecurityDescriptor,
+                                              &acl))
+               return 1;
+
+       if (!quiet) {
+               char *path;
+
+               path = p11_path_encode (server->socket_name);
+               printf ("P11_KIT_SERVER_ADDRESS=windows:pipe=%s\n", path);
+               free (path);
+               printf ("P11_KIT_SERVER_PID=%d\n", getpid ());
+       }
+
+       while (1) {
+               hpipe = CreateNamedPipe (server->socket_name,
+                                        PIPE_ACCESS_DUPLEX,
+                                        PIPE_TYPE_BYTE |
+                                        PIPE_READMODE_BYTE |
+                                        PIPE_WAIT
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+                                        | PIPE_REJECT_REMOTE_CLIENTS
+#endif
+                                        ,
+                                        PIPE_UNLIMITED_INSTANCES,
+                                        BUFSIZE,
+                                        BUFSIZE,
+                                        0,
+                                        &sa);
+               if (hpipe == INVALID_HANDLE_VALUE)
+                       return 1;
+               connected = ConnectNamedPipe (hpipe, NULL);
+               if (!connected)
+                       connected = GetLastError () == ERROR_PIPE_CONNECTED;
+               if (connected) {
+                       data = malloc (sizeof (struct ThreadData));
+                       data->handle = hpipe;
+                       data->server = server;
+                       hthread = CreateThread (NULL, 0, server_thread, data, 0,
+                                               &thread_id);
+                       if (hthread == NULL) {
+                               free (data);
+                               return 1;
+                       } else
+                               CloseHandle(hthread);
+               } else {
+                       CloseHandle(hpipe);
+               }
+       }
+
+       return 0;
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+       const char *pipe_base = "\\\\.\\pipe\\";
+       char *pipe_name;
+       int opt;
+       const char *name = NULL;
+       char *provider = NULL;
+       Server *server = NULL;
+       int ret = 0;
+
+       enum {
+               opt_verbose = 'v',
+               opt_quiet = 'q',
+               opt_help = 'h',
+               opt_name = 'n',
+               opt_provider = 'p'
+       };
+
+       struct option options[] = {
+               { "verbose", no_argument, NULL, opt_verbose },
+               { "quiet", no_argument, NULL, opt_quiet },
+               { "help", no_argument, NULL, opt_help },
+               { "name", required_argument, NULL, opt_name },
+               { "provider", required_argument, NULL, opt_provider },
+               { 0 },
+       };
+
+       p11_tool_desc usages[] = {
+               { 0, "usage: p11-kit server <token> ..." },
+               { opt_name, "specify name of the pipe (default: pkcs11-<pid>)" },
+               { opt_provider, "specify the module to use" },
+               { 0 },
+       };
+
+       while ((opt = p11_tool_getopt (argc, argv, options)) != -1) {
+               switch (opt) {
+               case opt_verbose:
+                       p11_kit_be_loud ();
+                       break;
+               case opt_quiet:
+                       quiet = true;
+                       break;
+               case opt_name:
+                       name = optarg;
+                       break;
+               case opt_provider:
+                       provider = optarg;
+                       break;
+               case opt_help:
+               case '?':
+                       p11_tool_usage (usages, options);
+                       return 0;
+               default:
+                       assert_not_reached ();
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               p11_tool_usage (usages, options);
+               return 2;
+       }
+
+       if (name == NULL) {
+               if (asprintf (&pipe_name, "%spkcs11-%d",
+                             pipe_base, _getpid ()) < 0) {
+                       ret = 1;
+                       goto out;
+               }
+       } else {
+               pipe_name = strdup (name);
+       }
+
+       server = server_new ((const char **)argv, argc, provider, pipe_name);
+       if (server == NULL) {
+               ret = 1;
+               goto out;
+       }
+
+       ret = server_loop (server);
+
+ out:
+       server_free (server);
+
+       if (pipe_name)
+               free (pipe_name);
+
+       return ret;
+}
+
+/* make_private_security_descriptor() and the helper functions were
+ * copied from putty/windows/winsecur.c in the PuTTY source code as of
+ * git commit 12bd5a6c722152aa27f24598785593e72b3284ea.
+ *
+ * PuTTY is copyright 1997-2017 Simon Tatham.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+ * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
+ * Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.
+ *
+ * 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.
+ */
+
+/* Initialised once, then kept around to reuse forever */
+static PSID world_sid, network_sid, user_sid;
+
+static PSID
+get_user_sid (void)
+{
+       HANDLE proc = NULL, tok = NULL;
+       TOKEN_USER *user = NULL;
+       DWORD toklen, sidlen;
+       PSID sid = NULL, ret = NULL;
+
+       if (user_sid)
+               return user_sid;
+
+       if ((proc = OpenProcess (MAXIMUM_ALLOWED, FALSE,
+                                GetCurrentProcessId ())) == NULL)
+               goto cleanup;
+
+       if (!OpenProcessToken (proc, TOKEN_QUERY, &tok))
+               goto cleanup;
+
+       if (!GetTokenInformation (tok, TokenUser, NULL, 0, &toklen) &&
+           GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+               goto cleanup;
+
+       if ((user = (TOKEN_USER *)LocalAlloc (LPTR, toklen)) == NULL)
+               goto cleanup;
+
+       if (!GetTokenInformation (tok, TokenUser, user, toklen, &toklen))
+               goto cleanup;
+
+       sidlen = GetLengthSid (user->User.Sid);
+
+       sid = (PSID)malloc (sidlen);
+
+       if (!CopySid (sidlen, sid, user->User.Sid))
+               goto cleanup;
+
+       /* Success. Move sid into the return value slot, and null it out
+        * to stop the cleanup code freeing it. */
+       ret = user_sid = sid;
+       sid = NULL;
+
+ cleanup:
+       if (proc != NULL)
+               CloseHandle (proc);
+       if (tok != NULL)
+               CloseHandle (tok);
+       if (user != NULL)
+               LocalFree (user);
+       if (sid != NULL)
+               free (sid);
+
+       return ret;
+}
+
+static bool
+get_sids (void)
+{
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-braces"
+#endif
+       SID_IDENTIFIER_AUTHORITY world_auth = { SECURITY_WORLD_SID_AUTHORITY };
+       SID_IDENTIFIER_AUTHORITY nt_auth = { SECURITY_NT_AUTHORITY };
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+       if (!user_sid) {
+               user_sid = get_user_sid ();
+               if (user_sid == NULL) {
+                       p11_message ("unable to construct SID for %s: %lu",
+                                    "current user",
+                                    GetLastError ());
+                       return false;
+               }
+       }
+
+       if (!world_sid) {
+               if (!AllocateAndInitializeSid (&world_auth, 1,
+                                              SECURITY_WORLD_RID,
+                                              0, 0, 0, 0, 0, 0, 0,
+                                              &world_sid)) {
+                       p11_message ("unable to construct SID for %s: %lu",
+                                    "world",
+                                    GetLastError ());
+                       return false;
+               }
+       }
+
+       if (!network_sid) {
+               if (!AllocateAndInitializeSid (&nt_auth, 1,
+                                              SECURITY_NETWORK_RID,
+                                              0, 0, 0, 0, 0, 0, 0,
+                                              &network_sid)) {
+                       p11_message ("unable to construct SID for %s: %lu",
+                                    "local same-user access only",
+                                    GetLastError ());
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool
+make_private_security_descriptor (DWORD permissions,
+                                 PSECURITY_DESCRIPTOR *psd,
+                                 PACL *acl)
+{
+       EXPLICIT_ACCESS ea[3];
+       int acl_err;
+
+       *psd = NULL;
+       *acl = NULL;
+
+       if (!get_sids ())
+               goto cleanup;
+
+       memset (ea, 0, sizeof(ea));
+       ea[0].grfAccessPermissions = permissions;
+       ea[0].grfAccessMode = REVOKE_ACCESS;
+       ea[0].grfInheritance = NO_INHERITANCE;
+       ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+       ea[0].Trustee.ptstrName = (LPTSTR)world_sid;
+       ea[1].grfAccessPermissions = permissions;
+       ea[1].grfAccessMode = GRANT_ACCESS;
+       ea[1].grfInheritance = NO_INHERITANCE;
+       ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+       ea[1].Trustee.ptstrName = (LPTSTR)user_sid;
+       ea[2].grfAccessPermissions = permissions;
+       ea[2].grfAccessMode = REVOKE_ACCESS;
+       ea[2].grfInheritance = NO_INHERITANCE;
+       ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+       ea[2].Trustee.ptstrName = (LPTSTR)network_sid;
+
+       acl_err = SetEntriesInAclA (3, ea, NULL, acl);
+       if (acl_err != ERROR_SUCCESS || *acl == NULL) {
+               p11_message ("unable to construct ACL: %d", acl_err);
+               goto cleanup;
+       }
+
+       *psd = (PSECURITY_DESCRIPTOR) LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+       if (!*psd) {
+               p11_message ("unable to allocate security descriptor: %lu",
+                            GetLastError ());
+               goto cleanup;
+       }
+
+       if (!InitializeSecurityDescriptor (*psd, SECURITY_DESCRIPTOR_REVISION)) {
+               p11_message ("unable to initialise security descriptor: %lu",
+                            GetLastError ());
+               goto cleanup;
+       }
+
+       if (!SetSecurityDescriptorOwner (*psd, user_sid, FALSE)) {
+               p11_message ("unable to set owner in security descriptor: %lu",
+                            GetLastError ());
+               goto cleanup;
+       }
+
+       if (!SetSecurityDescriptorDacl (*psd, TRUE, *acl, FALSE)) {
+               p11_message ("unable to set DACL in security descriptor: %lu",
+                            GetLastError ());
+               goto cleanup;
+       }
+
+       return true;
+
+ cleanup:
+       if (*psd) {
+               LocalFree (*psd);
+               *psd = NULL;
+       }
+       if (*acl) {
+               LocalFree (*acl);
+               *acl = NULL;
+       }
+
+       return false;
+}
+
+#endif /* OS_WIN32 */