[Cryptech-Commits] [sw/libhal] 03/04: Add RPC client daemon.

git at cryptech.is git at cryptech.is
Fri Jun 3 08:54:31 UTC 2016


This is an automated email from the git hooks/post-receive script.

paul at psgd.org pushed a commit to branch master
in repository sw/libhal.

commit f94203f344a7eabba834702dd171ae6c5c01c729
Author: Paul Selkirk <paul at psgd.org>
AuthorDate: Thu Jun 2 14:01:39 2016 -0400

    Add RPC client daemon.
---
 .gitignore          |   1 +
 GNUmakefile         |  20 ++-
 daemon.c            | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 rpc_client_daemon.c |  89 ++++++++++++++
 4 files changed, 448 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6919235..7131463 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ TAGS
 autom4te.cache
 config.log
 config.status
+cryptech_rpcd
 tests/test-aes-key-wrap
 tests/test-bus
 tests/test-ecdsa
diff --git a/GNUmakefile b/GNUmakefile
index e0a569a..e92dd4f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -51,7 +51,7 @@ PKEY2_OBJ = aes_keywrap.o modexp.o
 # IO_BUS = eim | i2c | fmc
 #   eim: EIM bus from Novena
 #   i2c: older I2C bus from Novena
-#   fmc: FMC bus from dev-bridge board
+#   fmc: FMC bus from dev-bridge and alpha boards
 
 IO_BUS ?= eim
 ifeq (${IO_BUS},eim)
@@ -95,26 +95,29 @@ endif
 #
 # RPC_SERVER = yes
 #
-# RPC_TRANSPORT = loopback | serial
+# RPC_TRANSPORT = loopback | serial | daemon
 #   loopback: communicate over loopback socket on Novena
 #   serial: communicate over USB in serial pass-through mode
+#   daemon: communicate over USB via a daemon, to arbitrate multiple clients
 
-RPC_TRANSPORT ?= serial
+RPC_TRANSPORT ?= daemon
 
 RPC_CLIENT_OBJ = rpc_api.o rpc_client.o xdr.o
 ifeq (${RPC_TRANSPORT},loopback)
   RPC_CLIENT_OBJ += rpc_client_loopback.o
 else ifeq (${RPC_TRANSPORT},serial)
   RPC_CLIENT_OBJ += rpc_client_serial.o slip.o
+else ifeq (${RPC_TRANSPORT},daemon)
+  RPC_CLIENT_OBJ += rpc_client_daemon.o
 endif
 
 RPC_DISPATCH_OBJ = rpc_hash.o rpc_misc.o rpc_pkey.o
 
-RPC_SERVER_OBJ = rpc_server.o xdr.o ${RPC_DISPATCH_OBJ}
+RPC_SERVER_OBJ = rpc_api.o rpc_server.o xdr.o ${RPC_DISPATCH_OBJ}
 ifeq (${RPC_TRANSPORT},loopback)
   RPC_SERVER_OBJ += rpc_server_loopback.o
 else ifeq (${RPC_TRANSPORT},serial)
-  RPC_SERVER_OBJ += rpc_server_serial.o rpc_serial.o slip.o
+  RPC_SERVER_OBJ += rpc_server_serial.o slip.o
 endif
 
 # Not building any of the RPC stuff, access FPGA cores directly.
@@ -186,6 +189,13 @@ server:
 loopback:
 	${MAKE} RPC_CLIENT=remote RPC_SERVER=yes RPC_TRANSPORT=loopback
 
+daemon: cryptech_rpcd
+#	${MAKE} RPC_CLIENT=mixed RPC_TRANSPORT=daemon
+	${MAKE} RPC_CLIENT=remote RPC_TRANSPORT=daemon
+
+cryptech_rpcd: daemon.o slip.o rpc_serial.o xdr.o
+	${CC} ${CFLAGS} -o $@ $^ ${LDFLAGS}
+
 ${OBJ}: ${INC}
 
 ${LIB}: ${OBJ}
diff --git a/daemon.c b/daemon.c
new file mode 100644
index 0000000..601fc0f
--- /dev/null
+++ b/daemon.c
@@ -0,0 +1,343 @@
+#define DEBUG
+/*
+ * daemon.c
+ * --------
+ * A daemon to arbitrate shared access to a serial connection to the HSM.
+ *
+ * Copyright (c) 2016, NORDUnet A/S All rights reserved.
+ *
+ * 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.
+ *
+ * - Neither the name of the NORDUnet nor the names of its contributors may
+ *   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
+ * HOLDER 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <poll.h>
+#include <getopt.h>             /* required with -std=c99 */
+#include <termios.h>            /* for default speed */
+
+#include "slip_internal.h"
+#include "xdr_internal.h"
+
+static char usage[] =
+    "usage: %s [-n socketname] [-d ttydevice] [-s ttyspeed]\n";
+
+/* select() is hopelessly broken, and epoll() is Linux-specific, so we'll use
+ * poll() until such a time as libevent or libev seems more appropriate.
+ * Unfortunately, poll() doesn't come with any macros or functions to manage
+ * the pollfd array, so we have to invent them.
+ */
+
+static struct pollfd *pollfds = NULL;
+static nfds_t nfds = 0;
+static nfds_t npollfds = 0;
+
+static void poll_add(int fd)
+{
+    /* add 4 entries at a time to avoid having to realloc too often */
+#define NNEW 4
+    
+    /* expand the array if necessary */
+    if (nfds == npollfds) {
+        npollfds = nfds + NNEW;
+        pollfds = realloc(pollfds, npollfds * sizeof(struct pollfd));
+        if (pollfds == NULL) {
+            perror("realloc");
+            exit(EXIT_FAILURE);
+        }
+        /* zero the new entries for hygiene */
+        memset(&pollfds[nfds], 0, NNEW * sizeof(struct pollfd));
+    }
+
+    /* populate the new entry */
+    pollfds[nfds].fd = fd;
+    pollfds[nfds].events = POLLIN;
+    ++nfds;
+}
+
+static void poll_remove(int fd)
+{
+    nfds_t i;
+
+    /* search the pollfd array */
+    for (i = 0; i < nfds; ++i) {
+        if (pollfds[i].fd == fd) {
+            /* shift remainder of the array left by one */
+            memmove(&pollfds[i], &pollfds[i + 1], (nfds - i - 1) * sizeof(struct pollfd));
+            /* zero the last entry for hygiene */
+            memset(&pollfds[nfds - 1], 0, sizeof(struct pollfd));
+            --nfds;
+            return;
+        }
+    }
+    /* if it's not found, return without an error */
+}
+
+#ifndef MAX_PKT_SIZE    /* move this to hal_internal.h */
+#define MAX_PKT_SIZE 4096
+#endif
+
+typedef struct {
+    size_t len;
+    uint8_t buf[MAX_PKT_SIZE];
+} rpc_buffer_t;
+static rpc_buffer_t ibuf, obuf;
+
+#ifndef DEVICE
+#define DEVICE "/dev/ttyUSB0"
+#endif
+#ifndef SPEED
+#define SPEED B921600
+#endif
+
+#ifndef SOCKET_NAME
+#define SOCKET_NAME "/tmp/cryptechd.socket"
+#endif
+char *socket_name = SOCKET_NAME;
+
+/* Set up an atexit handler to remove the filesystem entry for the unix domain
+ * socket. This will trigger on error exits, but not on the "normal" SIGKILL.
+ */
+void atexit_cleanup(void)
+{
+    unlink(socket_name);
+}
+
+#ifdef DEBUG
+static void hexdump(uint8_t *buf, uint32_t len)
+{
+    for (uint32_t i = 0; i < len; ++i)
+        printf("%02x%c", buf[i], ((i & 0x07) == 0x07) ? '\n' : ' ');
+    if ((len & 0x07) != 0)
+        printf("\n");
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+    struct sockaddr_un name;
+    int ret;
+    int lsock;
+    int dsock;
+    int opt;
+    char *device = DEVICE;
+    speed_t speed = SPEED;
+
+    while ((opt = getopt(argc, argv, "hn:d:s:")) != -1) {
+        switch (opt) {
+        case 'h':
+            printf(usage, argv[0]);
+            exit(EXIT_SUCCESS);
+        case 'n':
+            socket_name = optarg;
+            break;
+        case 'd':
+            device = optarg;
+            break;
+        case 's':
+            switch (atoi(optarg)) {
+            case 115200:
+                speed = B115200;
+                break;
+            case 921600:
+                speed = B921600;
+                break;
+            default:
+                printf("invalid speed value %s\n", optarg);
+                exit(EXIT_FAILURE);
+            }
+            break;
+        default:
+            printf(usage, argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    if (atexit(atexit_cleanup) != 0) {
+        perror("atexit");
+        exit(EXIT_FAILURE);
+    }
+
+    if (hal_serial_init(device, speed) != HAL_OK)
+        exit(EXIT_FAILURE);
+
+    int serial_fd = hal_serial_get_fd();
+    poll_add(serial_fd);
+
+    /* Remove the filesystem entry for the unix domain socket. The usual way
+     * to stop a daemon is SIGKILL, which we can't catch, so the file remains,
+     * and will prevent us from binding the socket.
+     *
+     * XXX We should also scan the process table, to make sure the daemon
+     * isn't already running.
+     */
+    unlink(socket_name);
+
+    /* Create the listening socket.
+     */
+    lsock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+    if (lsock == -1) {
+        perror("socket");
+        exit(EXIT_FAILURE);
+    }
+    poll_add(lsock);
+
+    /* For portability, clear the whole address structure, since some
+     * implementations have additional (nonstandard) fields in the structure.
+     */
+    memset(&name, 0, sizeof(struct sockaddr_un));
+
+    /* Bind the listening socket.
+     */
+    name.sun_family = AF_UNIX;
+    strncpy(name.sun_path, socket_name, sizeof(name.sun_path) - 1);
+    ret = bind(lsock, (const struct sockaddr *) &name, sizeof(struct sockaddr_un));
+    if (ret == -1) {
+        perror("bind");
+        exit(EXIT_FAILURE);
+    }
+
+    /* Prepare to accept connections.
+     */
+    ret = listen(lsock, 20);
+    if (ret == -1) {
+        perror("listen");
+        exit(EXIT_FAILURE);
+    }
+
+    /* The main loop.
+     */
+    for (;;) {
+
+        /* Blocking poll on all descriptors of interest.
+         */
+        ret = poll(pollfds, nfds, -1);
+        if (ret == -1) {
+            perror("poll");
+            exit(EXIT_FAILURE);
+        }
+        
+        for (nfds_t i = 0; i < nfds; ++i) {
+            if (pollfds[i].revents != 0) {
+                /* XXX POLLERR|POLLHUP|POLLNVAL */
+
+                /* serial port */
+                if (pollfds[i].fd == serial_fd) {
+		    int complete;
+                    hal_slip_recv_char(ibuf.buf, &ibuf.len, sizeof(ibuf.buf), &complete);
+                    if (complete) {
+#ifdef DEBUG
+                        printf("serial port received response:\n");
+                        hexdump(ibuf.buf, ibuf.len);
+#endif
+                        /* We've got a complete rpc response packet. */
+                        const uint8_t *bufptr = ibuf.buf;
+                        const uint8_t * const limit = ibuf.buf + ibuf.len;
+                        uint32_t sock;
+                        /* First word of the response is the client ID. */
+                        hal_xdr_decode_int(&bufptr, limit, &sock);
+                        /* Pass response on to the client that requested it. */
+                        send(sock, bufptr, ibuf.len - 4, 0);
+                        /* Reinitialize the receive buffer. */
+                        memset(&ibuf, 0, sizeof(ibuf));
+                    }
+                }
+
+                /* listening socket */
+                else if (pollfds[i].fd == lsock) {
+                    /* Accept incoming connection. */
+                    dsock = accept(lsock, NULL, NULL);
+                    if (ret == -1) {
+                        perror("accept");
+                        exit(EXIT_FAILURE);
+                    }
+                    poll_add(dsock);
+#ifdef DEBUG
+                    printf("listening socket accept data socket %d\n", dsock);
+#endif
+                }
+
+                /* client data socket */
+                else {
+                    uint8_t *bufptr = obuf.buf;
+                    const uint8_t * const limit = obuf.buf + MAX_PKT_SIZE;
+                    /* First word of the request is the client ID. */
+                    hal_xdr_encode_int(&bufptr, limit, pollfds[i].fd);
+                    /* Get the client's rpc request packet. */
+                    obuf.len = recv(pollfds[i].fd, bufptr, MAX_PKT_SIZE - 4, 0);
+#ifdef DEBUG
+                    printf("data socket %d received request:\n", pollfds[i].fd);
+                    hexdump(obuf.buf, obuf.len + 4);
+#endif
+
+		    /* Fill in the client handle arg as needed. */
+		    uint32_t opcode;
+		    hal_xdr_decode_int((const uint8_t ** const)&bufptr, limit, &opcode);
+		    switch (opcode) {
+		    case RPC_FUNC_SET_PIN:
+		    case RPC_FUNC_LOGIN:
+		    case RPC_FUNC_LOGOUT:
+		    case RPC_FUNC_IS_LOGGED_IN:
+		    case RPC_FUNC_HASH_INITIALIZE:
+		    case RPC_FUNC_PKEY_LOAD:
+		    case RPC_FUNC_PKEY_FIND:
+		    case RPC_FUNC_PKEY_GENERATE_RSA:
+		    case RPC_FUNC_PKEY_GENERATE_EC:
+			/* first argument is client handle */
+			hal_xdr_encode_int(&bufptr, limit, pollfds[i].fd);
+			break;
+		    default:
+			break;
+		    }
+
+                    if (obuf.len > 0) {
+#ifdef DEBUG
+                        printf("passing to serial port\n");
+#endif
+                        /* Pass it on to the serial port. */
+                        hal_slip_send(obuf.buf, obuf.len + 4);
+                    }
+                    else {
+#ifdef DEBUG
+                        printf("closing data socket\n");
+#endif
+                        /* Client has closed the socket. */
+                        close(pollfds[i].fd);
+                        poll_remove(pollfds[i].fd);
+                    }
+                    /* Reinitialize the transmit buffer. */
+                    memset(&obuf, 0, sizeof(obuf));
+                }
+            }
+        }
+    }
+
+    /*NOTREACHED*/
+    exit(EXIT_SUCCESS);
+}
diff --git a/rpc_client_daemon.c b/rpc_client_daemon.c
new file mode 100644
index 0000000..f328302
--- /dev/null
+++ b/rpc_client_daemon.c
@@ -0,0 +1,89 @@
+/*
+ * rpc_client_daemon.c
+ * -------------------
+ * Remote procedure call transport over a socket to a daemon.
+ *
+ * Copyright (c) 2016, NORDUnet A/S All rights reserved.
+ *
+ * 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.
+ *
+ * - Neither the name of the NORDUnet nor the names of its contributors may
+ *   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
+ * HOLDER 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/un.h>
+
+#include "hal.h"
+#include "hal_internal.h"
+
+#ifndef SOCKET_NAME
+#define SOCKET_NAME "/tmp/cryptechd.socket"
+#endif
+
+static int sock = -1;
+
+hal_error_t hal_rpc_client_transport_init(void)
+{
+    struct sockaddr_un name;
+    int ret;
+
+    sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+    if (sock == -1)
+        return perror("socket"), HAL_ERROR_RPC_TRANSPORT;
+    memset(&name, 0, sizeof(struct sockaddr_un));
+    name.sun_family = AF_UNIX;
+    strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+    ret = connect(sock, (const struct sockaddr *) &name, sizeof(struct sockaddr_un));
+    if (ret == -1)
+        return perror("connect"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t hal_rpc_client_transport_close(void)
+{
+    int ret = close(sock);
+    sock = -1;
+    if (ret != 0)
+        return perror("close"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t hal_rpc_send(const uint8_t * const buf, const size_t len)
+{
+    ssize_t ret = send(sock, (const void *)buf, len, 0);
+    return (ret == -1) ? HAL_ERROR_RPC_TRANSPORT : HAL_OK;
+}
+
+hal_error_t hal_rpc_recv(uint8_t * const buf, size_t * const len)
+{
+    ssize_t ret = recv(sock, (void *)buf, *len, 0);
+    if (ret == -1)
+	return HAL_ERROR_RPC_TRANSPORT;
+    *len = (size_t)ret;
+    return HAL_OK;
+}



More information about the Commits mailing list