summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2014-07-25 16:11:09 +0200
committerStef Walter <stefw@redhat.com>2014-09-25 09:45:30 +0200
commita3208e5d8612c1fac70f4b72705ab4d155c69c3e (patch)
tree9c73be22fdc6e129fc20e8dec5e6d9d181e38c98
parentc41e0e1d9a4a9a4533bc6f370e5eebe1d6b9752c (diff)
downloadp11-kit-a3208e5d8612c1fac70f4b72705ab4d155c69c3e.tar.gz
remote: operate as a single forking server
-rw-r--r--common/Makefile.am2
-rw-r--r--common/compat.h4
-rw-r--r--common/rnd.c72
-rw-r--r--common/rnd.h42
-rw-r--r--common/unix-peer.c92
-rw-r--r--common/unix-peer.h42
-rw-r--r--configure.ac11
-rw-r--r--p11-kit/p11-kit.c7
-rw-r--r--p11-kit/remote.c224
-rw-r--r--p11-kit/remote.h3
-rw-r--r--p11-kit/rpc-transport.c170
11 files changed, 549 insertions, 120 deletions
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 <sys/types.h>
#include <sys/stat.h>
-#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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#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 <getopt.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __linux__
+ /* needed for struct ucred */
+# define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include "debug.h"
+#include "message.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+
+#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 <getopt.h>
+
+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 <sys/types.h>
+ #include <signal.h>
+ ])
+
+ 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <unistd.h>
-int
-p11_kit_remote_serve_module (CK_FUNCTION_LIST *module,
- int in_fd,
- int out_fd)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+
+#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) {
@@ -141,6 +176,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 <errno.h>
#include <fcntl.h>
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <limits.h>
#ifdef OS_UNIX
+#ifdef __linux__
+# include <sys/prctl.h>
+#endif
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
@@ -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,50 +659,10 @@ 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;