From a3208e5d8612c1fac70f4b72705ab4d155c69c3e Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 25 Jul 2014 16:11:09 +0200 Subject: remote: operate as a single forking server --- common/Makefile.am | 2 + common/compat.h | 4 - common/rnd.c | 72 ++++++++++++++++ common/rnd.h | 42 +++++++++ common/unix-peer.c | 92 ++++++++++++++++++++ common/unix-peer.h | 42 +++++++++ configure.ac | 11 ++- p11-kit/p11-kit.c | 7 +- p11-kit/remote.c | 224 ++++++++++++++++++++++++++++++++++++++++++------ p11-kit/remote.h | 3 +- p11-kit/rpc-transport.c | 170 +++++++++++++++++++----------------- 11 files changed, 549 insertions(+), 120 deletions(-) create mode 100644 common/rnd.c create mode 100644 common/rnd.h create mode 100644 common/unix-peer.c create mode 100644 common/unix-peer.h diff --git a/common/Makefile.am b/common/Makefile.am index 47162dd..a7106e7 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -25,7 +25,9 @@ libp11_common_la_SOURCES = \ common/message.c common/message.h \ common/path.c common/path.h \ common/pkcs11.h common/pkcs11x.h common/pkcs11i.h \ + common/rnd.c common/rnd.h \ common/url.c common/url.h \ + common/unix-peer.c common/unix-peer.h \ $(NULL) libp11_library_la_SOURCES = \ diff --git a/common/compat.h b/common/compat.h index 4771370..d54bbbf 100644 --- a/common/compat.h +++ b/common/compat.h @@ -40,10 +40,6 @@ #include #include -#ifdef _GNU_SOURCE -#error Make the crap stop. _GNU_SOURCE is completely unportable and breaks all sorts of behavior -#endif - #if !defined(__cplusplus) && (__GNUC__ > 2) #define GNUC_PRINTF(x, y) __attribute__((__format__(__printf__, x, y))) #else diff --git a/common/rnd.c b/common/rnd.c new file mode 100644 index 0000000..60ea8ee --- /dev/null +++ b/common/rnd.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Red Hat + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Nikos Mavrogiannopoulos + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include "rnd.h" + +int p11_rnd(void *_data, unsigned size) +{ + int fd; + uint8_t *data = _data; + ssize_t res, done = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return -1; + } + + for (done = 0; done < size;) { + do { + res = read(fd, data + done, size - done); + } while (res < 0 && errno == EINTR); + + if (res <= 0) { + res = -1; + goto cleanup; + } + done += res; + } + + res = 0; + + cleanup: + close(fd); + return res; +} diff --git a/common/rnd.h b/common/rnd.h new file mode 100644 index 0000000..8caff05 --- /dev/null +++ b/common/rnd.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Nikos Mavrogiannopoulos + */ + +#ifndef P11_RND_H_ +#define P11_RND_H_ + +#include + +int p11_rnd (void *data, unsigned size); + +#endif /* P11_RND_H_ */ diff --git a/common/unix-peer.c b/common/unix-peer.c new file mode 100644 index 0000000..689159d --- /dev/null +++ b/common/unix-peer.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * + * This file is part of ocserv. + * + * ocserv is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ocserv is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef __linux__ + /* needed for struct ucred */ +# define _GNU_SOURCE +#endif + +#include "config.h" +#include "debug.h" +#include "message.h" +#include +#include +#include +#include +#include +#include + +#include "unix-peer.h" + +/* Returns the unix domain socket peer information. + * Returns zero on success. + */ +int p11_get_upeer_id(int cfd, uid_t *uid, uid_t *gid, pid_t *pid) +{ + int e, ret; +#if defined(SO_PEERCRED) + struct ucred cr; + socklen_t cr_len; + + cr_len = sizeof(cr); + ret = getsockopt(cfd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len); + if (ret == -1) { + e = errno; + p11_message("getsockopt SO_PEERCRED error: %s", + strerror(e)); + return -1; + } + + if (uid) + *uid = cr.uid; + + if (gid) + *gid = cr.gid; + + if (pid) + *pid = cr.pid; + +#elif defined(HAVE_GETPEEREID) + /* *BSD/MacOSX */ + uid_t euid; + gid_t egid; + + ret = getpeereid(cfd, &euid, &egid); + + if (ret == -1) { + e = errno; + p11_message("getpeereid error: %s", + strerror(e)); + return -1; + } + + if (uid) + *uid = euid; + + if (gid) + *gid = egid; + + if (pid) + *pid = -1; + +#else +#error "Unsupported UNIX variant" +#endif + return 0; +} diff --git a/common/unix-peer.h b/common/unix-peer.h new file mode 100644 index 0000000..0840c08 --- /dev/null +++ b/common/unix-peer.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Nikos Mavrogiannopoulos + */ + +#ifndef P11_UNIX_PEER_H_ +#define P11_UNIX_PEER_H_ + +#include + +int p11_get_upeer_id(int cfd, uid_t *uid, uid_t *gid, pid_t *pid); + +#endif /* P11_UNIX_PEER_H_ */ diff --git a/configure.ac b/configure.ac index b316d54..e649fe9 100644 --- a/configure.ac +++ b/configure.ac @@ -68,9 +68,14 @@ AC_C_BIGENDIAN AC_HEADER_STDBOOL if test "$os_unix" = "yes"; then - AC_CHECK_FUNC([pthread_create], , [ - AC_CHECK_LIB(pthread, pthread_create, , [ - AC_MSG_ERROR([could not find pthread_create]) + AC_CHECK_TYPES([sighandler_t, sig_t, __sighandler_t],,, + [#include + #include + ]) + + AC_CHECK_FUNC([pthread_mutexattr_init], , [ + AC_CHECK_LIB(pthread, pthread_mutexattr_init, , [ + AC_MSG_ERROR([could not find pthread_mutexattr_init]) ]) ]) diff --git a/p11-kit/p11-kit.c b/p11-kit/p11-kit.c index f447b4c..cb317da 100644 --- a/p11-kit/p11-kit.c +++ b/p11-kit/p11-kit.c @@ -173,16 +173,11 @@ p11_kit_remote (int argc, return 2; } - if (isatty (0)) { - p11_message ("the 'remote' tool is not meant to be run from a terminal"); - return 2; - } - module = p11_kit_module_load (argv[0], 0); if (module == NULL) return 1; - ret = p11_kit_remote_serve_module (module, 0, 1); + ret = p11_kit_remote_serve_module (module, getenv("P11_KIT_SOCKET")); p11_kit_module_release (module); return ret; diff --git a/p11-kit/remote.c b/p11-kit/remote.c index 944e501..1ccb7fa 100644 --- a/p11-kit/remote.c +++ b/p11-kit/remote.c @@ -47,34 +47,61 @@ #include #include #include +#include #include -int -p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, - int in_fd, - int out_fd) +#include +#include +#include +#include + +#ifdef HAVE_SIGHANDLER_T +# define SIGHANDLER_T sighandler_t +#elif HAVE_SIG_T +# define SIGHANDLER_T sig_t +#elif HAVE___SIGHANDLER_T +# define SIGHANDLER_T __sighandler_t +#else +typedef void (*sighandler_t)(int); +# define SIGHANDLER_T sighandler_t +#endif + +static unsigned need_children_cleanup = 0; +static unsigned children_avail = 0; + +static +SIGHANDLER_T ocsignal(int signum, SIGHANDLER_T handler) +{ + struct sigaction new_action, old_action; + + new_action.sa_handler = handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + sigaction (signum, &new_action, &old_action); + return old_action.sa_handler; +} + +static int +serve_module (CK_FUNCTION_LIST *module, + p11_buffer *options, p11_buffer *buffer, + p11_virtual *virt, + int fd) { p11_rpc_status status; unsigned char version; - p11_virtual virt; - p11_buffer options; - p11_buffer buffer; + uint32_t pid; size_t state; int ret = 1; int code; + struct iovec iov[2]; - return_val_if_fail (module != NULL, 1); - - p11_buffer_init (&options, 0); - p11_buffer_init (&buffer, 0); - - p11_virtual_init (&virt, &p11_virtual_base, module, NULL); - - switch (read (in_fd, &version, 1)) { + switch (read (fd, &version, 1)) { case 0: + status = P11_RPC_EOF; goto out; case 1: - if (version != 0) { + if (version != 1) { p11_message ("unspported version received: %d", (int)version); goto out; } @@ -85,11 +112,19 @@ p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, } version = 0; - switch (write (out_fd, &version, out_fd)) { - case 1: + pid = getpid(); + + iov[0].iov_base = &version; + iov[0].iov_len = 1; + + iov[1].iov_base = &pid; + iov[1].iov_len = 4; + + switch (writev (fd, iov, 2)) { + case 5: break; default: - p11_message_err (errno, "couldn't write credential byte"); + p11_message_err (errno, "couldn't write credential bytes"); goto out; } @@ -99,8 +134,8 @@ p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, code = 0; do { - status = p11_rpc_transport_read (in_fd, &state, &code, - &options, &buffer); + status = p11_rpc_transport_read (fd, &state, &code, + options, buffer); } while (status == P11_RPC_AGAIN); switch (status) { @@ -116,16 +151,16 @@ p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, goto out; } - if (!p11_rpc_server_handle (&virt.funcs, &buffer, &buffer)) { + if (!p11_rpc_server_handle (&virt->funcs, buffer, buffer)) { p11_message ("unexpected error handling rpc message"); goto out; } state = 0; - options.len = 0; + options->len = 0; do { - status = p11_rpc_transport_write (out_fd, &state, code, - &options, &buffer); + status = p11_rpc_transport_write (fd, &state, code, + options, buffer); } while (status == P11_RPC_AGAIN); switch (status) { @@ -140,6 +175,145 @@ p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, } } +out: + return ret; +} + +static void cleanup_children(void) +{ +int status; +pid_t pid; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + if (children_avail > 0) + children_avail--; + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == SIGSEGV) + p11_message("child %u died with sigsegv\n", (unsigned)pid); + else + p11_message("child %u died with signal %d\n", (unsigned)pid, (int)WTERMSIG(status)); + } + } + need_children_cleanup = 0; +} + +static void handle_children(int signo) +{ + need_children_cleanup = 1; +} + +int +p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, + const char *socket_file) +{ + p11_virtual virt; + p11_buffer options; + p11_buffer buffer; + int ret = 1, rc, sd; + int e, cfd; + pid_t pid; + socklen_t sa_len; + struct sockaddr_un sa; + fd_set rd_set; + struct timespec ts; + sigset_t emptyset, blockset; + + sigemptyset(&blockset); + sigemptyset(&emptyset); + sigaddset(&blockset, SIGCHLD); + ocsignal(SIGCHLD, handle_children); + + return_val_if_fail (module != NULL, 1); + + /* listen to unix socket */ + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", socket_file); + + sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd == -1) { + e = errno; + p11_message ("could not create socket %s: %s", socket_file, strerror(e)); + goto out; + } + + umask(066); + rc = bind(sd, (struct sockaddr *)&sa, SUN_LEN(&sa)); + if (rc == -1) { + e = errno; + p11_message ("could not create socket %s: %s", socket_file, strerror(e)); + goto out; + } + +#if 0 + rc = chown(SOCKET_FILE, config->uid, config->gid); + if (rc == -1) { + e = errno; + p11_message ("could not chown socket %s: %s", socket_file, strerror(e)); + } +#endif + + rc = listen(sd, 1024); + if (rc == -1) { + e = errno; + p11_message ("could not listen to socket %s: %s", socket_file, strerror(e)); + goto out; + } + + p11_buffer_init (&options, 0); + p11_buffer_init (&buffer, 0); + + p11_virtual_init (&virt, &p11_virtual_base, module, NULL); + + sigprocmask(SIG_BLOCK, &blockset, NULL); + /* accept connections */ + for (;;) { + if (need_children_cleanup) + cleanup_children(); + + FD_ZERO(&rd_set); + FD_SET(sd, &rd_set); + ts.tv_nsec = 0; + ts.tv_sec = 30; + + ret = pselect(sd + 1, &rd_set, NULL, NULL, &ts, &emptyset); + if (ret == -1 && errno == EINTR) + continue; + + if (ret == 0 && children_avail == 0) { /* timeout */ + p11_message ("no connections for 30 secs, exiting"); + exit(0); + } + + sa_len = sizeof(sa); + cfd = accept(sd, (struct sockaddr *)&sa, &sa_len); + if (cfd == -1) { + e = errno; + if (e != EINTR) { + p11_message ("could not accept from socket %s: %s", socket_file, strerror(e)); + } + continue; + } + + /* XXX: check the uid of the peer */ + + pid = fork(); + switch(pid) { + case -1: + p11_message_err (errno, "failed to fork for accept"); + continue; + case 0: + /* child */ + sigprocmask(SIG_UNBLOCK, &blockset, NULL); + serve_module (module, &options, &buffer, &virt, cfd); + _exit(0); + default: + children_avail++; + break; + } + close(cfd); + } + out: p11_buffer_uninit (&buffer); p11_buffer_uninit (&options); diff --git a/p11-kit/remote.h b/p11-kit/remote.h index 12cbe6d..b72750a 100644 --- a/p11-kit/remote.h +++ b/p11-kit/remote.h @@ -44,8 +44,7 @@ extern "C" { #ifdef P11_KIT_FUTURE_UNSTABLE_API int p11_kit_remote_serve_module (CK_FUNCTION_LIST *module, - int in_fd, - int out_fd); + const char *socket); #endif diff --git a/p11-kit/rpc-transport.c b/p11-kit/rpc-transport.c index 8c3fb0c..e7933f8 100644 --- a/p11-kit/rpc-transport.c +++ b/p11-kit/rpc-transport.c @@ -42,6 +42,8 @@ #include "message.h" #include "pkcs11.h" #include "private.h" +#include "rnd.h" +#include "unix-peer.h" #include "rpc.h" #include "rpc-message.h" @@ -51,9 +53,14 @@ #include #include #include +#include #include +#include #ifdef OS_UNIX +#ifdef __linux__ +# include +#endif #include #include #include @@ -68,6 +75,7 @@ typedef struct { /* Never changes */ int fd; + pid_t pid; /* Protected by the lock */ p11_mutex_t write_lock; @@ -84,14 +92,49 @@ typedef struct { } rpc_socket; static rpc_socket * -rpc_socket_new (int fd) +rpc_socket_new (const char *file, unsigned nowait) { rpc_socket *sock; + struct sockaddr_un sa; + int ret; + unsigned i; + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", file); sock = calloc (1, sizeof (rpc_socket)); return_val_if_fail (sock != NULL, NULL); - sock->fd = fd; + sock->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock->fd == -1) { + free(sock); + p11_message ("could not open socket"); + return_val_if_reached (NULL); + } + + p11_debug ("connecting to: %s", file); + + /* try to connect to child */ + for (i=0;i<5;i++) { + ret = connect(sock->fd, (struct sockaddr *)&sa, sizeof(sa)); + if (ret == 0 || nowait != 0) + break; + p11_sleep_ms(1000); + } + if (ret == -1) { + close(sock->fd); + free(sock); + if (nowait == 0) { + p11_message ("could not connect to socket: %s", file); + return_val_if_reached (NULL); + } else { + return NULL; + } + } + + p11_debug ("connected to: %s", file); + sock->last_code = 0x10; sock->read_creds = false; sock->sent_creds = false; @@ -128,8 +171,11 @@ static void rpc_socket_close (rpc_socket *sock) { assert (sock != NULL); - if (sock->fd != -1) + + if (sock->fd != -1) { + /* kill our peer */ close (sock->fd); + } sock->fd = -1; } @@ -217,20 +263,10 @@ rpc_socket_write_inlock (rpc_socket *sock, p11_buffer *buffer) { unsigned char header[12]; - unsigned char dummy = '\0'; /* The socket is locked and referenced at this point */ assert (buffer != NULL); - /* Place holder byte, will later carry unix credentials (on some systems) */ - if (!sock->sent_creds) { - if (write_all (sock->fd, &dummy, 1) != 1) { - p11_message_err (errno, "couldn't send socket credentials"); - return CKR_DEVICE_ERROR; - } - sock->sent_creds = true; - } - p11_rpc_buffer_encode_uint32 (header, code); p11_rpc_buffer_encode_uint32 (header + 4, options->len); p11_rpc_buffer_encode_uint32 (header + 8, buffer->len); @@ -341,7 +377,6 @@ rpc_socket_read (rpc_socket *sock, { CK_RV ret = CKR_DEVICE_ERROR; unsigned char header[12]; - unsigned char dummy; fd_set rfds; assert (code != NULL); @@ -354,12 +389,6 @@ rpc_socket_read (rpc_socket *sock, p11_mutex_lock (&sock->read_lock); - if (!sock->read_creds) { - if (read_all (sock->fd, &dummy, 1) != 1) - return CKR_DEVICE_ERROR; - sock->read_creds = true; - } - for (;;) { /* No message header has been read yet? ... read one in */ if (sock->read_code == 0) { @@ -630,49 +659,9 @@ rpc_transport_buffer (p11_rpc_client_vtable *vtable, typedef struct { p11_rpc_transport base; p11_array *argv; - pid_t pid; + char sfile[_POSIX_PATH_MAX]; } rpc_exec; -static void -rpc_exec_wait_or_terminate (pid_t pid) -{ - bool terminated = false; - int status; - int sig; - int ret; - int i; - - - for (i = 0; i < 3 * 1000; i += 100) { - ret = waitpid (pid, &status, WNOHANG); - if (ret != 0) - break; - p11_sleep_ms (100); - } - - if (ret == 0) { - p11_message ("process %d did not exit, terminating", (int)pid); - kill (pid, SIGTERM); - terminated = true; - ret = waitpid (pid, &status, 0); - } - - if (ret < 0) { - p11_message_err (errno, "failed to wait for executed child: %d", (int)pid); - status = 0; - } else if (WIFEXITED (status)) { - status = WEXITSTATUS (status); - if (status == 0) - p11_debug ("process %d exited with status 0", (int)pid); - else - p11_message ("process %d exited with status %d", (int)pid, status); - } else if (WIFSIGNALED (status)) { - sig = WTERMSIG (status); - if (!terminated || sig != SIGTERM) - p11_message ("process %d was terminated with signal %d", (int)pid, sig); - } -} - static void rpc_exec_disconnect (p11_rpc_client_vtable *vtable, void *fini_reserved) @@ -682,10 +671,6 @@ rpc_exec_disconnect (p11_rpc_client_vtable *vtable, if (rex->base.socket) rpc_socket_close (rex->base.socket); - if (rex->pid) - rpc_exec_wait_or_terminate (rex->pid); - rex->pid = 0; - /* Do the common disconnect stuff */ rpc_transport_disconnect (vtable, fini_reserved); } @@ -707,14 +692,17 @@ rpc_exec_connect (p11_rpc_client_vtable *vtable, rpc_exec *rex = (rpc_exec *)vtable; pid_t pid; int max_fd; - int fds[2]; + uint32_t upid; int errn; + unsigned char dummy = 1; + struct iovec iov[2]; p11_debug ("executing rpc transport: %s", (char *)rex->argv->elem[0]); - if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - p11_message_err (errno, "failed to create pipe for remote"); - return CKR_DEVICE_ERROR; + /* check whether a server is already there and we can connect to it */ + rex->base.socket = rpc_socket_new (rex->sfile, 1); + if (rex->base.socket != NULL) { + goto success; } pid = fork (); @@ -722,19 +710,17 @@ rpc_exec_connect (p11_rpc_client_vtable *vtable, /* Failure */ case -1: - close (fds[0]); - close (fds[1]); p11_message_err (errno, "failed to fork for remote"); return CKR_DEVICE_ERROR; /* Child */ case 0: - if (dup2 (fds[1], STDIN_FILENO) < 0 || - dup2 (fds[1], STDOUT_FILENO) < 0) { - errn = errno; - p11_message_err (errn, "couldn't dup file descriptors in remote child"); - _exit (errn); - } +#ifdef __linux__ + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + p11_debug ("forked sec-mod server"); + /* save the socket file */ + setenv("P11_KIT_SOCKET", rex->sfile, 1); /* Close file descriptors, except for above on exec */ max_fd = STDERR_FILENO + 1; @@ -751,11 +737,30 @@ rpc_exec_connect (p11_rpc_client_vtable *vtable, break; } - close (fds[1]); - rex->pid = pid; - rex->base.socket = rpc_socket_new (fds[0]); + rex->base.socket = rpc_socket_new (rex->sfile, 0); return_val_if_fail (rex->base.socket != NULL, CKR_GENERAL_ERROR); + success: + /* this is read as version from the peer --nmav */ + if (write_all (rex->base.socket->fd, &dummy, 1) != 1) { + p11_message_err (errno, "couldn't send version"); + return CKR_DEVICE_ERROR; + } + rex->base.socket->sent_creds = true; + + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + iov[1].iov_base = &upid; + iov[1].iov_len = 4; + + errn = readv(rex->base.socket->fd, iov, 2); + if (errn != 5) { + p11_message_err (errno, "couldn't read version: %d", errn); + return CKR_DEVICE_ERROR; + } + rex->base.socket->read_creds = true; + rex->base.socket->pid = upid; + return CKR_OK; } @@ -766,6 +771,7 @@ rpc_exec_free (void *data) rpc_exec_disconnect (data, NULL); rpc_transport_uninit (&rex->base); p11_array_free (rex->argv); + remove(rex->sfile); free (rex); } @@ -785,6 +791,7 @@ rpc_exec_init (const char *remote, { p11_array *argv; rpc_exec *rex; + unsigned t; argv = p11_array_new (free); if (!p11_argv_parse (remote, on_argv_parsed, argv) || argv->num < 1) { @@ -799,6 +806,9 @@ rpc_exec_init (const char *remote, p11_array_push (argv, NULL); rex->argv = argv; + p11_rnd(&t, sizeof(t)); + snprintf(rex->sfile, sizeof(rex->sfile), "/tmp/p11-kit-rpc.%u", t); + rex->base.vtable.connect = rpc_exec_connect; rex->base.vtable.disconnect = rpc_exec_disconnect; rex->base.vtable.transport = rpc_transport_buffer; -- cgit v1.2.1