diff options
-rw-r--r-- | p11-kit/Makefile.am | 9 | ||||
-rw-r--r-- | p11-kit/server.c | 541 |
2 files changed, 511 insertions, 39 deletions
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am index 6496e8d..573bf61 100644 --- a/p11-kit/Makefile.am +++ b/p11-kit/Makefile.am @@ -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 ---------------------------------------------------------------- diff --git a/p11-kit/server.c b/p11-kit/server.c index ce7900a..5eb5bcd 100644 --- a/p11-kit/server.c +++ b/p11-kit/server.c @@ -40,18 +40,21 @@ #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 */ |