Blob Blame History Raw
commit b048be548508dd1958bb7271568f388d0f6cbcf8
Author: Ingo Franzki <ifranzki@linux.ibm.com>
Date:   Mon Feb 8 16:50:00 2021 +0100

    Event support: API and token level changes
    
    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>

diff --git a/usr/include/apictl.h b/usr/include/apictl.h
index 8898cae3..81c65dad 100644
--- a/usr/include/apictl.h
+++ b/usr/include/apictl.h
@@ -57,6 +57,8 @@ typedef struct {
     API_Slot_t SltList[NUMBER_SLOTS_MANAGED];
     DLL_Load_t DLLs[NUMBER_SLOTS_MANAGED];  // worst case we have a separate DLL
                                             // per slot
+    int socketfd;
+    pthread_t event_thread;
 } API_Proc_Struct_t;
 
 #endif
diff --git a/usr/include/events.h b/usr/include/events.h
new file mode 100644
index 00000000..dac6ad52
--- /dev/null
+++ b/usr/include/events.h
@@ -0,0 +1,83 @@
+/*
+ * COPYRIGHT (c) International Business Machines Corp. 2021
+ *
+ * This program is provided under the terms of the Common Public License,
+ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
+ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
+ * found in the file LICENSE file or at
+ * https://opensource.org/licenses/cpl1.0.php
+ */
+
+#include <stdint.h>
+#include <pkcs11types.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "local_types.h"
+#include "pkcs32.h"
+
+#ifndef _EVENTS_H
+#define _EVENTS_H
+
+typedef struct {
+    unsigned int version;       /* EVENT_VERSION_xxx */
+    unsigned int type;          /* EVENT_TYPE_xxx */
+    unsigned int flags;         /* EVENT_FLAGS_xxx */
+    unsigned int token_type;    /* Destination token type: EVENT_TOK_TYPE_xxx */
+    char token_label[member_size(CK_TOKEN_INFO_32, label)];
+                                /* Label of destination token (or blanks) */
+    pid_t process_id;           /* Process ID of destination process (or 0) */
+    unsigned int payload_len;   /* Length of payload in bytes */
+    /* Followed by payload_len bytes of payload (event specific) */
+} __attribute__ ((__packed__)) event_msg_t;
+
+typedef struct {
+    unsigned int version;               /* EVENT_VERSION_xxx */
+    unsigned int positive_replies;      /* Number of tokens that replied a */
+    unsigned int negative_replies;      /* positive, or negative feedback, */
+    unsigned int nothandled_replies;    /* or that did not handle the event. */
+                                        /* Note: Only tokens matching the event
+                                         * destination fields (pid, label,
+                                         * token-type) are counted. */
+} __attribute__ ((__packed__)) event_reply_t;
+
+/* Event and reply versions */
+#define EVENT_VERSION_1         1
+
+/* Event classes (encoded into event type) */
+#define EVENT_CLASS_MASK        0xffff0000
+#define EVENT_CLASS_UDEV        0x00010000
+#define EVENT_CLASS_ADMIN       0x00020000
+
+/* Event types */
+#define EVENT_TYPE_APQN_ADD     EVENT_CLASS_UDEV + 0x00000001
+#define EVENT_TYPE_APQN_REMOVE  EVENT_CLASS_UDEV + 0x00000002
+
+/* Event flags */
+#define EVENT_FLAGS_NONE        0x00000000
+#define EVENT_FLAGS_REPLY_REQ   0x00000001
+
+/* Event token destination types */
+#define EVENT_TOK_TYPE_ALL      0x00000000
+#define EVENT_TOK_TYPE_CCA      0x00000001
+#define EVENT_TOK_TYPE_EP11     0x00000002
+
+/* Maximum event payload length 128k */
+#define EVENT_MAX_PAYLOAD_LENGTH    (128 * 1024)
+
+/* Event payload for EVENT_TYPE_APQN_ADD and EVENT_TYPE_APQN_REMOVE */
+typedef struct {
+    unsigned short card;
+    unsigned short domain;
+    unsigned int device_type;            /* from uevent DEV_TYPE property */
+} __attribute__ ((__packed__)) event_udev_apqn_data_t;
+
+/* AP device types */
+#define AP_DEVICE_TYPE_CEX3A        8
+#define AP_DEVICE_TYPE_CEX3C        9
+#define AP_DEVICE_TYPE_CEX4         10
+#define AP_DEVICE_TYPE_CEX5         11
+#define AP_DEVICE_TYPE_CEX6         12
+#define AP_DEVICE_TYPE_CEX7         13
+
+#endif
diff --git a/usr/include/include.mk b/usr/include/include.mk
index a36afb25..79e593d7 100644
--- a/usr/include/include.mk
+++ b/usr/include/include.mk
@@ -7,4 +7,5 @@ opencryptokiinclude_HEADERS =						\
 
 noinst_HEADERS +=							\
 	usr/include/apictl.h usr/include/local_types.h			\
-	usr/include/pkcs32.h usr/include/slotmgr.h usr/include/stdll.h
+	usr/include/pkcs32.h usr/include/slotmgr.h usr/include/stdll.h	\
+	usr/include/events.h
diff --git a/usr/include/local_types.h b/usr/include/local_types.h
index f03c6629..c7c7f5ec 100644
--- a/usr/include/local_types.h
+++ b/usr/include/local_types.h
@@ -11,6 +11,8 @@
 #ifndef __LOCAL_TYPES
 #define __LOCAL_TYPES
 
+#define member_size(type, member) sizeof(((type *)0)->member)
+
 typedef unsigned char uint8;
 
 typedef unsigned short uint16;
diff --git a/usr/include/stdll.h b/usr/include/stdll.h
index 57f6c6e8..9a3b760c 100644
--- a/usr/include/stdll.h
+++ b/usr/include/stdll.h
@@ -350,6 +350,11 @@ typedef CK_RV (CK_PTR ST_C_IBM_ReencryptSingle)(STDLL_TokData_t *tokdata,
                                                 CK_BYTE_PTR pReencryptedData,
                                             CK_ULONG_PTR pulReencryptedDataLen);
 
+typedef CK_RV (CK_PTR ST_C_HandleEvent)(STDLL_TokData_t *tokdata,
+                                        unsigned int event_type,
+                                        unsigned int event_flags,
+                                        const char *payload,
+                                        unsigned int payload_len);
 
 struct ST_FCN_LIST {
 
@@ -424,6 +429,9 @@ struct ST_FCN_LIST {
     ST_C_CancelFunction ST_CancelFunction;
 
     ST_C_IBM_ReencryptSingle ST_IBM_ReencryptSingle;
+
+    /* The functions defined below are not part of the external API */
+    ST_C_HandleEvent ST_HandleEvent;
 };
 
 typedef struct ST_FCN_LIST STDLL_FcnList_t;
diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
index f1ee9132..b74b763f 100644
--- a/usr/lib/api/api_interface.c
+++ b/usr/lib/api/api_interface.c
@@ -286,7 +286,31 @@ void child_fork_initializer()
     if (Anchor != NULL)
         C_Finalize(NULL);
     in_child_fork_initializer = FALSE;
- }
+}
+
+void parent_fork_prepare()
+{
+    if (Anchor == NULL)
+        return;
+
+    /*
+     * Stop the event thread in the fork parent, since having the event thread
+     * active when a fork is performed causes various problems (e.g. deadlocks
+     * in glibc).
+     */
+    if (Anchor->event_thread > 0)
+        stop_event_thread();
+}
+
+void parent_fork_after()
+{
+    if (Anchor == NULL)
+        return;
+
+    /* Restart the event thread in the parent when fork is complete */
+    if (Anchor->event_thread == 0)
+        start_event_thread();
+}
 
 //------------------------------------------------------------------------
 // API function C_CancelFunction
@@ -1501,6 +1525,20 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
 
     shData = &(Anchor->SocketDataP);
 
+    /*
+     * Stop the event thread and close the socket.
+     * If C_Finalize is called as part of the fork initializer, don't stop
+     * the thread, since a forked process does not have any threads, and don't
+     * close the socket, as this would close the connection of the parent
+     * process to the pkcsslotd as well.
+     * */
+    if (!in_child_fork_initializer) {
+        if (Anchor->event_thread > 0)
+            stop_event_thread();
+        if (Anchor->socketfd >= 0)
+            close(Anchor->socketfd);
+    }
+
     // unload all the STDLL's from the application
     // This is in case the APP decides to do the re-initialize and
     // continue on
@@ -2642,6 +2680,8 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
     CK_C_INITIALIZE_ARGS *pArg;
     char fcnmap = 0;
     CK_RV rc = CKR_OK;
+    CK_SLOT_ID slotID;
+    API_Slot_t *sltp;
 
     /*
      * Lock so that only one thread can run C_Initialize or C_Finalize at
@@ -2674,6 +2714,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
     // This must be done prior to all goto error calls, else bt_destroy()
     // will fail because it accesses uninitialized memory when t->size > 0.
     memset(Anchor, 0, sizeof(API_Proc_Struct_t));
+    Anchor->socketfd = -1;
 
     TRACE_DEBUG("Anchor allocated at %s\n", (char *) Anchor);
 
@@ -2789,11 +2830,21 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
     }
     TRACE_DEBUG("Shared memory %p \n", Anchor->SharedMemP);
 
-    if (!init_socket_data()) {
+    /* Connect to slot daemon and retrieve slot infos */
+    Anchor->socketfd = connect_socket(SOCKET_FILE_PATH);
+    if (Anchor->socketfd < 0) {
         OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to create a "
                    "socket. Verify that the slot management daemon is "
                    "running.\n");
-        TRACE_ERROR("Cannot attach to socket.\n");
+        TRACE_ERROR("Failed to connect to slot daemon\n");
+        rc = CKR_FUNCTION_FAILED;
+        goto error_shm;
+    }
+
+    if (!init_socket_data(Anchor->socketfd)) {
+        OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to retrieve slot "
+                   "infos from slot deamon.\n");
+        TRACE_ERROR("Failed to receive slot infos from socket.\n");
         rc = CKR_FUNCTION_FAILED;
         goto error_shm;
     }
@@ -2810,15 +2861,35 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
     }
     //
     // load all the slot DLL's here
-    {
-        CK_SLOT_ID slotID;
-        API_Slot_t *sltp;
+    for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+        sltp = &(Anchor->SltList[slotID]);
+        slot_loaded[slotID] = DL_Load_and_Init(sltp, slotID);
+    }
 
+    /* Start event receiver thread */
+    if (start_event_thread() != 0) {
+        TRACE_ERROR("Failed to start event thread\n");
+
+        // unload all the STDLL's from the application
+        // This is in case the APP decides to do the re-initialize and
+        // continue on
         for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
             sltp = &(Anchor->SltList[slotID]);
-            slot_loaded[slotID] = DL_Load_and_Init(sltp, slotID);
+            if (slot_loaded[slotID]) {
+                if (sltp->pSTfini) {
+                    // call the terminate function..
+                    sltp->pSTfini(sltp->TokData, slotID,
+                                  &Anchor->SocketDataP.slot_info[slotID],
+                                  &trace, 0);
+                }
+            }
+            DL_UnLoad(sltp, slotID);
         }
 
+        API_UnRegister();
+
+        rc = CKR_FUNCTION_FAILED;
+        goto error_shm;
     }
 
     pthread_mutex_unlock(&GlobMutex);
@@ -2829,6 +2900,8 @@ error_shm:
 
 error:
     bt_destroy(&Anchor->sess_btree);
+    if (Anchor->socketfd >= 0)
+        close(Anchor->socketfd);
 
     free((void *) Anchor);
     Anchor = NULL;
@@ -5052,7 +5125,8 @@ void api_init(void)
 {
     // Should only have to do the atfork stuff at load time...
     if (!Initialized) {
-        pthread_atfork(NULL, NULL, (void (*)()) child_fork_initializer);
+        pthread_atfork(parent_fork_prepare, parent_fork_after,
+                       child_fork_initializer);
         Initialized = 1;
     }
 }
diff --git a/usr/lib/api/apiproto.h b/usr/lib/api/apiproto.h
index 871f3778..8523fb8e 100644
--- a/usr/lib/api/apiproto.h
+++ b/usr/lib/api/apiproto.h
@@ -50,6 +50,9 @@ void CK_Info_From_Internal(CK_INFO_PTR dest, CK_INFO_PTR_64 src);
 int sessions_exist(CK_SLOT_ID);
 
 void CloseAllSessions(CK_SLOT_ID slot_id, CK_BBOOL in_fork_initializer);
-int init_socket_data();
+int connect_socket(const char *file_path);
+int init_socket_data(int socketfd);
+int start_event_thread();
+int stop_event_thread();
 
 #endif
diff --git a/usr/lib/api/socket_client.c b/usr/lib/api/socket_client.c
index 6bacf151..e344ddbf 100644
--- a/usr/lib/api/socket_client.c
+++ b/usr/lib/api/socket_client.c
@@ -23,114 +23,421 @@
 #include <grp.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <stdbool.h>
+#include <poll.h>
 
 #include "apiproto.h"
 #include "slotmgr.h"
 #include "apictl.h"
+#include "trace.h"
 #include "ock_syslog.h"
+#include "events.h"
 
 extern API_Proc_Struct_t *Anchor;
-//
-// Will fill out the Slot_Mgr_Socket_t structure in the Anchor global data
-// structure with the values passed by the pkcsslotd via a socket RPC.
-int init_socket_data()
+
+int connect_socket(const char *file_path)
 {
     int socketfd;
     struct sockaddr_un daemon_address;
     struct stat file_info;
     struct group *grp;
-    int n;
-    unsigned int bytes_received = 0;
-    Slot_Mgr_Socket_t *daemon_socket_data = NULL;
-    int ret = FALSE;
 
-    if (stat(SOCKET_FILE_PATH, &file_info)) {
+    if (stat(file_path, &file_info)) {
         OCK_SYSLOG(LOG_ERR,
-                   "init_socket_data: failed to find socket file, errno=%d",
+                   "connect_socket: failed to find socket file, errno=%d",
                    errno);
-        return FALSE;
+        return -1;
     }
 
     grp = getgrnam("pkcs11");
     if (!grp) {
         OCK_SYSLOG(LOG_ERR,
-                   "init_socket_data: pkcs11 group does not exist, errno=%d",
+                   "connect_socket: pkcs11 group does not exist, errno=%d",
                    errno);
-        return FALSE;
+        return -1;
     }
 
     if (file_info.st_uid != 0 || file_info.st_gid != grp->gr_gid) {
         OCK_SYSLOG(LOG_ERR,
-                   "init_socket_data: incorrect permissions on socket file");
-        return FALSE;
+                   "connect_socket: incorrect permissions on socket file");
+        return -1;
     }
 
     if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
         OCK_SYSLOG(LOG_ERR,
-                   "init_socket_data: failed to create socket, errno=%d",
+                   "connect_socket: failed to create socket, errno=%d",
                    errno);
-        return FALSE;
+        return -1;
     }
 
     memset(&daemon_address, 0, sizeof(struct sockaddr_un));
     daemon_address.sun_family = AF_UNIX;
-    strcpy(daemon_address.sun_path, SOCKET_FILE_PATH);
+    strcpy(daemon_address.sun_path, file_path);
 
     if (connect(socketfd, (struct sockaddr *) &daemon_address,
                 sizeof(struct sockaddr_un)) != 0) {
         OCK_SYSLOG(LOG_ERR,
-                   "init_socket_data: failed to connect to slotmanager daemon, "
+                   "connect_socket: failed to connect to slotmanager daemon, "
                    "errno=%d",
                    errno);
-        goto exit;
-    }
-    // allocate data buffer
-    daemon_socket_data =
-        (Slot_Mgr_Socket_t *) malloc(sizeof(*daemon_socket_data));
-    if (!daemon_socket_data) {
-        OCK_SYSLOG(LOG_ERR, "init_socket_data: failed to \
-                   allocate %lu bytes \
-                   for daemon data, errno=%d",
-                   sizeof(*daemon_socket_data), errno);
-        goto exit;
+        goto error;
     }
 
-    while (bytes_received < sizeof(*daemon_socket_data)) {
-        n = read(socketfd, ((char *) daemon_socket_data) + bytes_received,
-                 sizeof(*daemon_socket_data) - bytes_received);
+    return socketfd;
+
+error:
+    close(socketfd);
+    return -1;
+}
+
+static ssize_t read_all(int socketfd, char *buffer, size_t size)
+{
+    size_t bytes_received = 0;
+    ssize_t n;
+
+    while (bytes_received < size) {
+        n = read(socketfd, buffer + bytes_received, size - bytes_received);
         if (n < 0) {
             // read error
             if (errno == EINTR)
                 continue;
-            OCK_SYSLOG(LOG_ERR, "init_socket_data: read error \
-                       on daemon socket, errno=%d", errno);
-            goto exit;
-        } else if (n == 0) {
-            // eof but we still expect some bytes
-            OCK_SYSLOG(LOG_ERR, "init_socket_data: read returned \
-                       with eof but we still \
-                       expect %lu bytes from daemon",
-                       sizeof(*daemon_socket_data) - bytes_received);
-            goto exit;
-        } else {
-            // n > 0, we got some bytes
-            bytes_received += n;
+            return -errno;
         }
+        if (n == 0)
+            break;
+
+        bytes_received += n;
     }
 
-    ret = TRUE;
+    return bytes_received;
+}
+
+static ssize_t send_all(int socketfd, char *buffer, size_t size)
+{
+    size_t bytes_sent = 0;
+    ssize_t n;
 
-    // copy the Slot_Mgr_Socket_t struct into global
-    // Anchor SocketDataPdata buffer
-    memcpy(&(Anchor->SocketDataP), daemon_socket_data,
-           sizeof(*daemon_socket_data));
+    while (bytes_sent < size) {
+        n = send(socketfd, buffer + bytes_sent, size - bytes_sent, 0);
+        if (n < 0) {
+            // send error
+            if (errno == EINTR)
+                continue;
+            return -errno;
+        }
+        if (n == 0)
+            break;
 
-exit:
-    //free the data buffer after copy
-    if (daemon_socket_data)
-        free(daemon_socket_data);
+        bytes_sent += n;
+    }
 
-    close(socketfd);
+    return bytes_sent;
+}
+
+//
+// Will fill out the Slot_Mgr_Socket_t structure in the Anchor global data
+// structure with the values passed by the pkcsslotd via a socket RPC.
+int init_socket_data(int socketfd)
+{
+    ssize_t n;
+    int ret = TRUE;
+
+    n = read_all(socketfd, (char *)&Anchor->SocketDataP,
+                 sizeof(Anchor->SocketDataP));
+    if (n < 0) {
+        // read error
+        OCK_SYSLOG(LOG_ERR, "init_socket_data: read error \
+                   on daemon socket, errno=%d", -n);
+        ret = FALSE;
+    }
+    if (n != sizeof(Anchor->SocketDataP)) {
+        // eof but we still expect some bytes
+        OCK_SYSLOG(LOG_ERR, "init_socket_data: read returned \
+                   with eof but we still \
+                   expect %lu bytes from daemon",
+                   sizeof(Anchor->SocketDataP) - n);
+        ret = FALSE;
+    }
 
     return ret;
 }
+
+static bool match_token_label_filter(event_msg_t *event, API_Slot_t *sltp)
+{
+    if (event->token_label[0] == ' ' || event->token_label[0] == '\0')
+        return true;
+
+    return memcmp(event->token_label,
+                  sltp->TokData->nv_token_data->token_info.label,
+                  sizeof(event->token_label)) == 0;
+}
+
+struct type_model {
+    unsigned int type;
+    char model[member_size(CK_TOKEN_INFO_32, model)];
+};
+
+static const struct type_model type_model_flt[] = {
+        { .type = EVENT_TOK_TYPE_CCA,  .model = "CCA             " },
+        { .type = EVENT_TOK_TYPE_EP11, .model = "EP11            " },
+};
+
+static bool match_token_type_filter(event_msg_t *event, API_Slot_t *sltp)
+{
+    size_t i;
+
+    if (event->token_type == EVENT_TOK_TYPE_ALL)
+        return true;
+
+    for (i = 0; i < sizeof(type_model_flt) / sizeof(struct type_model); i++) {
+        if (memcmp(sltp->TokData->nv_token_data->token_info.model,
+                   type_model_flt[i].model,
+                   sizeof(type_model_flt[i].model)) == 0 &&
+            (event->token_type & type_model_flt[i].type) != 0)
+            return true;
+    }
+
+    return false;
+}
+
+static int handle_event(API_Proc_Struct_t *anchor, event_msg_t *event,
+                        char *payload, event_reply_t *reply)
+{
+    CK_SLOT_ID slotID;
+    API_Slot_t *sltp;
+    CK_RV rc;
+
+    /* If its not for our process, ignore it, don't increment reply counters */
+    if (event->process_id != 0 && event->process_id != anchor->Pid)
+        return 0;
+
+    for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+        sltp = &anchor->SltList[slotID];
+        if (sltp->DLLoaded == FALSE || sltp->FcnList == NULL)
+            continue;
+
+        if (!match_token_label_filter(event, sltp))
+            continue;
+        if (!match_token_type_filter(event, sltp))
+            continue;
+
+        if (sltp->FcnList->ST_HandleEvent != NULL)
+            rc = sltp->FcnList->ST_HandleEvent(sltp->TokData, event->type,
+                                               event->flags, payload,
+                                               event->payload_len);
+        else
+            rc = CKR_FUNCTION_NOT_SUPPORTED;
+
+        TRACE_DEVEL("Slot %lu ST_HandleEvent rc: 0x%lx\n", slotID, rc);
+        switch (rc) {
+        case CKR_OK:
+            reply->positive_replies++;
+            break;
+        case CKR_FUNCTION_NOT_SUPPORTED:
+            reply->nothandled_replies++;
+            break;
+        default:
+            reply->negative_replies++;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static void event_thread_cleanup(void *arg)
+{
+    API_Proc_Struct_t *anchor = arg;
+
+    UNUSED(anchor);
+
+    TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
+}
+
+static void *event_thread(void *arg)
+{
+    API_Proc_Struct_t *anchor = arg;
+    int oldstate, oldtype;
+    struct pollfd pollfd;
+    event_msg_t event;
+    char *payload;
+    event_reply_t reply;
+    ssize_t num;
+    int rc;
+
+    UNUSED(arg);
+
+    TRACE_DEVEL("Event thread %lu running\n", pthread_self());
+
+    if (anchor->socketfd < 0) {
+        TRACE_ERROR("socket is already closed.\n");
+        TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
+        return NULL;
+    }
+
+    /* Enable cancellation */
+    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
+    pthread_cleanup_push(event_thread_cleanup, anchor);
+
+    pollfd.fd = anchor->socketfd;
+    pollfd.events = POLLIN | POLLHUP | POLLERR;
+
+    while (1) {
+        pollfd.revents = 0;
+        rc = poll(&pollfd, 1, -1);
+        if (rc < 0) {
+            if (errno == EINTR)
+                continue;
+            TRACE_ERROR("poll failed: %d\n", errno);
+            break;
+        }
+
+        if (rc == 0)
+            continue;
+
+        if (pollfd.revents & (POLLHUP | POLLERR)) {
+            TRACE_ERROR("Error on socket, possibly closed by slot daemon\n");
+            break;
+        }
+        if ((pollfd.revents & POLLIN) == 0)
+            continue;
+
+        /* Disable for cancellation while we are working on an event */
+        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+
+        TRACE_DEVEL("Receive new event ....\n");
+
+        num = read_all(anchor->socketfd, (char *)&event, sizeof(event));
+        if (num != sizeof(event)) {
+            TRACE_ERROR("Error receiving the event, rc: %ld\n", num);
+            break;
+        }
+
+        TRACE_DEBUG("Event version:      %u\n", event.version);
+        TRACE_DEBUG("Event type:         0x%08x\n", event.type);
+        TRACE_DEBUG("Event flags:        0x%08x\n", event.flags);
+        TRACE_DEBUG("Event token_type:   0x%08x\n", event.token_type);
+        TRACE_DEBUG("Event token_name:   '%.32s'\n", event.token_label);
+        TRACE_DEBUG("Event process_id:   %u\n", event.process_id);
+        TRACE_DEBUG("Event payload_len:  %u\n", event.payload_len);
+
+        if (event.version != EVENT_VERSION_1) {
+            TRACE_ERROR("Event version invalid: %u\n", event.version);
+            break;
+        }
+
+        payload = NULL;
+        if (event.payload_len > 0) {
+            payload = malloc(event.payload_len);
+            if (payload == NULL) {
+                TRACE_ERROR("Failed to allocate buffer for event payload\n");
+                break;
+            }
+
+            num = read_all(anchor->socketfd, payload, event.payload_len);
+            if (num != event.payload_len) {
+                TRACE_ERROR("Error receiving the event payload, rc: %ld\n", num);
+                if (payload != NULL)
+                    free(payload);
+                break;
+            }
+
+            TRACE_DEBUG("Event payload:\n");
+            TRACE_DEBUG_DUMP("  ", payload, event.payload_len);
+        }
+
+        memset(&reply, 0, sizeof(reply));
+        reply.version = EVENT_VERSION_1;
+        rc = handle_event(anchor, &event, payload, &reply);
+        if (rc != 0) {
+            TRACE_ERROR("Error handling the event, rc: %d\n", rc);
+            if (payload != NULL)
+                free(payload);
+            break;
+        }
+
+        TRACE_DEBUG("Reply version:      %u\n", reply.version);
+        TRACE_DEBUG("Reply positive:     %u\n", reply.positive_replies);
+        TRACE_DEBUG("Reply negative:     %u\n", reply.negative_replies);
+        TRACE_DEBUG("Reply not-handled:  %u\n", reply.nothandled_replies);
+
+        if (event.flags & EVENT_FLAGS_REPLY_REQ) {
+            num = send_all(anchor->socketfd, (char *)&reply, sizeof(reply));
+            if (num != sizeof(reply)) {
+                TRACE_ERROR("Error sending the event reply, rc: %ld\n", num);
+                if (payload != NULL)
+                    free(payload);
+                break;
+            }
+        }
+
+        if (payload != NULL)
+            free(payload);
+
+        /* Re-enable for  and test if we got canceled in the meantime */
+        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+        pthread_testcancel();
+    }
+
+    /*
+     * Close the socket if we encounter an unrecoverable error (e.g. received
+     * invalid event) and stop the thread because of that.
+     * If the thread is stopped via stop_event_thread(), then it gets canceled
+     * via pthread_cancel(), and will not reach this place, thus the socket is
+     * not closed. This is intended, and the socket will then be closed by
+     * C_Finalize(). The atfork 'prepare' handler in the parent process also
+     * stops the thread (via stop_event_thread()), and the socket must not be
+     * closed in this case, because the thread is restarted in the atfork
+     * 'parent' handler, and should continue to receive events from the
+     * socket.
+     */
+    close(anchor->socketfd);
+    anchor->socketfd = -1;
+
+    pthread_cleanup_pop(1);
+    return NULL;
+}
+
+int start_event_thread()
+{
+    int rc;
+
+    rc =  pthread_create(&Anchor->event_thread, NULL, event_thread, Anchor);
+    if (rc != 0) {
+        OCK_SYSLOG(LOG_ERR, "start_event_thread: pthread_create failed, "
+                   "errno=%d", rc);
+        TRACE_ERROR("Failed to start event thread, errno=%d\n", rc);
+        return rc;
+    }
+
+    TRACE_DEVEL("Event thread %lu has been started\n", Anchor->event_thread);
+    return 0;
+}
+
+int stop_event_thread()
+{
+    int rc;
+    void *status;
+
+    TRACE_DEVEL("Canceling event thread %lu\n", Anchor->event_thread);
+    rc = pthread_cancel(Anchor->event_thread);
+    if (rc != 0 && rc != ESRCH)
+        return rc;
+
+    TRACE_DEVEL("Waiting for event thread %lu to terminate\n",
+                Anchor->event_thread);
+    rc = pthread_join(Anchor->event_thread, &status);
+    if (rc != 0)
+        return rc;
+
+    if (status != PTHREAD_CANCELED) {
+        TRACE_ERROR("Event thread was stopped, but did not return the "
+                   "expected status\n");
+    }
+
+    TRACE_DEVEL("Event thread %lu has terminated\n", Anchor->event_thread);
+
+    Anchor->event_thread = 0;
+    return 0;
+}
diff --git a/usr/lib/cca_stdll/tok_struct.h b/usr/lib/cca_stdll/tok_struct.h
index 2b43fa8e..182e2ac2 100644
--- a/usr/lib/cca_stdll/tok_struct.h
+++ b/usr/lib/cca_stdll/tok_struct.h
@@ -134,6 +134,7 @@ token_spec_t token_specific = {
     &token_specific_reencrypt_single,
     NULL,                       // set_attribute_values
     NULL,                       // set_attrs_for_new_object
+    NULL,                       // handle_event
 };
 
 #endif
diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c
index aae00984..a3749d26 100644
--- a/usr/lib/common/new_host.c
+++ b/usr/lib/common/new_host.c
@@ -4039,6 +4039,24 @@ done:
     return rc;
 }
 
+CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
+                     unsigned int event_flags, const char *payload,
+                     unsigned int payload_len)
+{
+    CK_RV rc;
+
+    if (token_specific.t_handle_event == NULL)
+        return CKR_FUNCTION_NOT_SUPPORTED;
+
+    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
+                                       payload, payload_len);
+
+    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
+               "event_flags = 0x%08x\n", rc, event_type, event_flags);
+
+    return rc;
+}
+
 void SC_SetFunctionList(void)
 {
     function_list.ST_Initialize = ST_Initialize;
@@ -4104,4 +4122,6 @@ void SC_SetFunctionList(void)
     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
 
     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
+
+    function_list.ST_HandleEvent = SC_HandleEvent;
 }
diff --git a/usr/lib/common/tok_spec_struct.h b/usr/lib/common/tok_spec_struct.h
index 30ffcf02..0e90d411 100644
--- a/usr/lib/common/tok_spec_struct.h
+++ b/usr/lib/common/tok_spec_struct.h
@@ -278,6 +278,10 @@ struct token_specific_struct {
 
     CK_RV(*t_set_attrs_for_new_object) (STDLL_TokData_t *, CK_OBJECT_CLASS,
                                         CK_ULONG, TEMPLATE *);
+
+    CK_RV(*t_handle_event) (STDLL_TokData_t *tokdata, unsigned int event_type,
+                            unsigned int event_flags, const char *payload,
+                            unsigned int payload_len);
 };
 
 typedef struct token_specific_struct token_spec_t;
diff --git a/usr/lib/common/tok_specific.h b/usr/lib/common/tok_specific.h
index ffb72909..997fa7e1 100644
--- a/usr/lib/common/tok_specific.h
+++ b/usr/lib/common/tok_specific.h
@@ -326,4 +326,10 @@ CK_RV token_specific_set_attrs_for_new_object(STDLL_TokData_t *,
                                               CK_OBJECT_CLASS, CK_ULONG,
                                               TEMPLATE *);
 
+CK_RV token_specific_handle_event(STDLL_TokData_t *tokdata,
+                                  unsigned int event_type,
+                                  unsigned int event_flags,
+                                  const char *payload,
+                                  unsigned int payload_len);
+
 #endif
diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c
index 6fcef68a..4e592363 100644
--- a/usr/lib/ep11_stdll/new_host.c
+++ b/usr/lib/ep11_stdll/new_host.c
@@ -4262,6 +4262,24 @@ done:
     return rc;
 }
 
+CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
+                     unsigned int event_flags, const char *payload,
+                     unsigned int payload_len)
+{
+    CK_RV rc;
+
+    if (token_specific.t_handle_event == NULL)
+        return CKR_FUNCTION_NOT_SUPPORTED;
+
+    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
+                                       payload, payload_len);
+
+    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
+               "event_flags = 0x%08x\n", rc, event_type, event_flags);
+
+    return rc;
+}
+
 void SC_SetFunctionList(void)
 {
     function_list.ST_Initialize = ST_Initialize;
@@ -4327,4 +4345,6 @@ void SC_SetFunctionList(void)
     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
 
     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
+
+    function_list.ST_HandleEvent = SC_HandleEvent;
 }
diff --git a/usr/lib/ep11_stdll/tok_struct.h b/usr/lib/ep11_stdll/tok_struct.h
index 51aae6fb..2c0af9cf 100644
--- a/usr/lib/ep11_stdll/tok_struct.h
+++ b/usr/lib/ep11_stdll/tok_struct.h
@@ -137,6 +137,7 @@ token_spec_t token_specific = {
     &token_specific_reencrypt_single,
     &token_specific_set_attribute_values,
     &token_specific_set_attrs_for_new_object,
+    NULL,                       // handle_event
 };
 
 #endif
diff --git a/usr/lib/ica_s390_stdll/tok_struct.h b/usr/lib/ica_s390_stdll/tok_struct.h
index 13ee72c9..a260a276 100644
--- a/usr/lib/ica_s390_stdll/tok_struct.h
+++ b/usr/lib/ica_s390_stdll/tok_struct.h
@@ -147,6 +147,7 @@ token_spec_t token_specific = {
     NULL,                       // reencrypt_single
     NULL,                       // set_attribute_values
     NULL,                       // set_attrs_for_new_object
+    NULL,                       // handle_event
 };
 
 #endif
diff --git a/usr/lib/icsf_stdll/new_host.c b/usr/lib/icsf_stdll/new_host.c
index 0f93ce5c..cfef7425 100644
--- a/usr/lib/icsf_stdll/new_host.c
+++ b/usr/lib/icsf_stdll/new_host.c
@@ -3332,6 +3332,24 @@ done:
     return rc;
 }
 
+CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
+                     unsigned int event_flags, const char *payload,
+                     unsigned int payload_len)
+{
+    CK_RV rc;
+
+    if (token_specific.t_handle_event == NULL)
+        return CKR_FUNCTION_NOT_SUPPORTED;
+
+    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
+                                       payload, payload_len);
+
+    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
+               "event_flags = 0x%08x\n", rc, event_type, event_flags);
+
+    return rc;
+}
+
 void SC_SetFunctionList(void)
 {
     function_list.ST_Initialize = ST_Initialize;
@@ -3397,4 +3415,6 @@ void SC_SetFunctionList(void)
     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
 
     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
+
+    function_list.ST_HandleEvent = SC_HandleEvent;
 }
diff --git a/usr/lib/icsf_stdll/tok_struct.h b/usr/lib/icsf_stdll/tok_struct.h
index fb1619ee..0f930a29 100644
--- a/usr/lib/icsf_stdll/tok_struct.h
+++ b/usr/lib/icsf_stdll/tok_struct.h
@@ -129,6 +129,7 @@ token_spec_t token_specific = {
     NULL,                       // reencrypt_single
     NULL,                       // set_attribute_values
     NULL,                       // set_attrs_for_new_object
+    NULL,                       // handle_event
 };
 
 #endif
diff --git a/usr/lib/soft_stdll/tok_struct.h b/usr/lib/soft_stdll/tok_struct.h
index acf7c5d7..e43df038 100644
--- a/usr/lib/soft_stdll/tok_struct.h
+++ b/usr/lib/soft_stdll/tok_struct.h
@@ -172,6 +172,7 @@ token_spec_t token_specific = {
     NULL,                       // reencrypt_single
     NULL,                       // set_attribute_values
     NULL,                       // set_attrs_for_new_object
+    NULL,                       // handle_event
 };
 
 #endif
diff --git a/usr/lib/tpm_stdll/tok_struct.h b/usr/lib/tpm_stdll/tok_struct.h
index d48b93e5..8903f123 100644
--- a/usr/lib/tpm_stdll/tok_struct.h
+++ b/usr/lib/tpm_stdll/tok_struct.h
@@ -120,4 +120,5 @@ struct token_specific_struct token_specific = {
     NULL,                       // reencrypt_single
     NULL,                       // set_attribute_values
     NULL,                       // set_attrs_for_new_object
+    NULL,                       // handle_event
 };