1 /* tlspool/service.c -- TLS pool service, socket handling, command dispatcher */
14 #include <errortable.h>
18 #endif /* WINDOWS_PORT */
23 #include <tlspool/commands.h>
24 #include <tlspool/internal.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
38 #define WEOF ((wint_t)(0xFFFF))
41 #define _tprintf printf
43 #endif /* WINDOWS_PORT */
45 /* The data stored in this module consists of lists of sockets to listen to
46 * for connection setup and command exchange, but not data communication.
47 * Commands are received from the various clients and processed, always
48 * ensuring exactly one reply.
50 * Some command requests are actually callbacks in reaction to something
51 * the TLS pool sent to an application. Those callbacks are recognised
52 * by their pio_cbid parameter, and after security scrutiny they are passed
53 * on directly to the requester, bypassing normal command processing and
54 * instead processing it where it was requested. This parser-like spread
55 * of acceptable cases over processing nodes simplifies the complexity
56 * and available alternatives in each node. It also helps to benefit from
57 * overlap between (semantics versions of) similar commands.
59 * Anything that may take up more than a trivial amount of time (perhaps
60 * because it must wait for remote operations to complete) is sent off to
61 * a separate thread, which may interact with its client or with a client
62 * serving a special purpose (that is, the PIN entry client).
64 * When a thread wants to request a callback, it sends a command response
65 * to that end, after creating a suitable structure in the callback list.
66 * This structure includes a place where the callback command can be
67 * stored, and a mutex that must be unlocked when that has been done. The
68 * callback structure may at that point be released. The structures for
69 * these exchanges with the callback list and, complicating matters, the
70 * free list of callback structures, is arranged in this module and offered
71 * to the rest of the TLS pool as an abstract service.
75 static int stop_service = 0;
76 static uint32_t facilities;
78 static struct callback cblist [1024];
79 static struct callback *cbfree;
80 static pthread_mutex_t cbfree_mutex = PTHREAD_MUTEX_INITIALIZER;
82 static int os_send_command (struct command *cmd, int passfd);
83 static void os_run_service ();
85 /* Setup the service module.
87 void setup_service (void) {
88 facilities = cfg_facilities ();
91 /* Cleanup the service module.
93 void cleanup_service (void) {
98 /* Allocate a free command structure for the processing cycle. Commands are
99 * considered claimed between allocate_command_for_clientfd() and the freeing
100 * of the command that takes place while sending a reply. Note that sending
101 * a callback request does not count as a reply; it defers the freeing-up of
102 * the command structure.
104 * As for locking... this function is only called by the master thread, so
105 * it requires no locks. It merely sets the "claimed" flag (after setting
106 * up the "clientfd" field) after which it is airborne. Unlocking is done
107 * by the thread that happens to be working on the command at that time,
108 * and is effectively done by resetting the "claimed" flag to zero, and not
109 * doing _anything_ with the command afterwards.
111 static struct command *cmdpool = NULL;
112 static int cmdpool_len = 1000;
113 static struct command *allocate_command_for_clientfd (pool_handle_t fd) {
114 static int cmdpool_pos = 0;
118 cmdpool = (struct command *) calloc (1000, sizeof (struct command));
120 tlog (TLOG_UNIXSOCK, LOG_CRIT, "Failed to allocate command pool");
123 memset (cmdpool, 0, 1000 * sizeof (struct command));
126 while (cmdpool [pos].claimed) {
128 if (pos >= cmdpool_len) {
131 if (pos == cmdpool_pos) {
132 /* A full rotation -- delay of 10ms */
136 cmdpool [pos].clientfd = fd;
137 cmdpool [pos].passfd = -1;
138 cmdpool [pos].handler = pthread_self (); // Not fit for cancel
139 cmdpool [pos].claimed = 1;
140 return &cmdpool [pos];
144 /* Free any commands that were allocated to the given client file descriptor.
145 * This is disruptive; the commands will not continue their behaviour by
146 * responding to the requests. This means that it should only be used for
147 * situations where the client file descriptor was closed.
148 * Any threads that may be running or waiting on the command are cancelled.
150 * TODO: This is O(cmdpool_len) so linked lists could help to avoid trouble.
152 static void free_commands_by_clientfd (pool_handle_t clientfd) {
154 if (cmdpool == NULL) {
157 for (i=0; i<cmdpool_len; i++) {
158 if (cmdpool [i].claimed) {
159 if (cmdpool [i].clientfd == clientfd) {
160 //TODO// don't be so disruptive
161 pthread_cancel (cmdpool [i].handler);
162 cmdpool [i].claimed = 0;
168 int send_command (struct command *cmd, int passfd) {
172 return 1; // Success guaranteed when nobody is listening
174 assert (passfd == -1); // Working passfd code retained but not used
176 return os_send_command(cmd, passfd);
179 /* Report success to the user. Note that this function does not terminate
180 * actions, but it should be the last response to the client.
182 * We accept the situation where cmd==NULL to accommodate code that deals
183 * with re-run commands that were internally stored. This saves massively
184 * in re-coding such code.
186 * We accept the situation where cmd==NULL to accommodate code that deals
187 * with re-run commands that were internally stored. This saves massively
188 * in re-coding such code.
190 void send_success (struct command *cmd) {
194 cmd->cmd.pio_cmd = PIOC_SUCCESS_V2;
195 cmd->cmd.pio_cbid = 0;
196 if (!send_command (cmd, -1)) {
197 perror ("Failed to send success reply");
202 /* Report an error response to the user. Report with the given errno and msg.
203 * Note that this function does not terminate actions, but it should be the
204 * last response to the client.
206 * We accept the situation where cmd==NULL to accommodate code that deals
207 * with re-run commands that were internally stored. This saves massively
208 * in re-coding such code.
210 void send_error (struct command *cmd, int tlserrno, char *msg) {
218 cmd->cmd.pio_cmd = PIOC_ERROR_V2;
219 cmd->cmd.pio_cbid = 0;
220 cmd->cmd.pio_data.pioc_error.tlserrno = tlserrno;
221 strncpy (cmd->cmd.pio_data.pioc_error.message, msg, sizeof (cmd->cmd.pio_data.pioc_error.message));
222 if (!send_command (cmd, -1)) {
223 perror ("Failed to send error reply");
227 void copy_tls_command(struct command *cmd, struct tlspool_command *tls_command) {
228 memcpy(&cmd->cmd, tls_command, sizeof(struct tlspool_command));
231 /* Check if a command request is a proper callback response.
232 * Return 1 if it is, othterwise return 0.
234 static int is_callback (struct command *cmd) {
235 uint16_t cbid = cmd->cmd.pio_cbid;
239 if (cbid > 1024) { /* TODO: dynamicity */
243 if (cblist [cbid].fd < 0) {
246 if (cblist [cbid].fd != cmd->clientfd) {
249 if (cblist [cbid].followup) {
256 /* Desire a callback and in the process of doing so, send a callback response.
257 * This must be called from another thread than the main TLS pool thread.
259 * The code below and the signaling post_callback call claim the cbfree_mutex
260 * as a condition to protect (keep atomic) the conditioning and signaling.
261 * The condition awaited for which the callback's condition presents a hint
262 * is the setting of the followup pointer in the callback structure, which
263 * links in the command that responds to the callback placed.
265 * The caller may supply the absolute time_t value at which it times out.
266 * If opt_timeout is 0, it is not considered to be a timeout value. If it
267 * is supplied, the return value may be NULL to signal timeout. There is
268 * no information fed back from the caller, but at least the TLS Pool does
269 * not block on it, but can continue to process failure. Later submissions
270 * of the callback response are swallowed silently (although a log entry
273 struct command *send_callback_and_await_response (struct command *cmdresp, time_t opt_timeout) {
275 struct command *followup;
276 assert (pthread_mutex_lock (&cbfree_mutex) == 0);
279 //TODO// Allocate more...
280 tlog (TLOG_UNIXSOCK, LOG_CRIT, "Ran out of callback structures. Crashing as a coward");
283 //TODO// It's simpler to administer index numbers and not pointers
285 cmdresp->cmd.pio_cbid = 1 + (((intptr_t) cb) - ((intptr_t) cblist)) / ((intptr_t) sizeof (struct callback));
286 cb->fd = cmdresp->clientfd;
288 cb->next = NULL; //TODO// Enqueue in fd-queue
290 send_command (cmdresp, -1);
292 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Waiting with fd=%d and cbid=%d on semaphone 0x%08x", cb->fd, cmdresp->cmd.pio_cbid, cb);
293 if (opt_timeout != 0) {
295 memset (&ts, 0, sizeof (ts));
296 ts.tv_sec = opt_timeout;
297 if (pthread_cond_timedwait (&cb->semaphore, &cbfree_mutex, &ts) != 0) {
298 // Timed out (or interrupted) so give up
303 pthread_cond_wait (&cb->semaphore, &cbfree_mutex);
305 followup = cb->followup;
307 //TODO// Remove cb from the fd's cblist
312 cb->timedout = 1; // Defer freeing it to the signaler
314 pthread_mutex_unlock (&cbfree_mutex);
316 tlog (TLOG_UNIXSOCK, LOG_NOTICE, "Requested callback over %d timed out, cleanup of structures deferred", cmdresp->clientfd);
322 /* Present a callback command request to the thread that is waiting for it.
323 * This must be called from the main thread of the TLS pool, and it will
324 * spark life to the thread that is awaiting the callback.
326 static void post_callback (struct command *cmd) {
327 uint16_t cbid = cmd->cmd.pio_cbid - 1;
328 cblist [cbid].fd = INVALID_POOL_HANDLE;
329 cblist [cbid].followup = cmd;
330 assert (pthread_mutex_lock (&cbfree_mutex) == 0);
331 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Signaling on the semaphore of callback 0x%08x", &cblist [cbid]);
332 if (!cblist [cbid].timedout) {
333 // Still waiting, send a signal to the requester
334 pthread_cond_signal (&cblist [cbid].semaphore);
336 // Timed out, but the callback structure awaits cleanup
337 cblist [cbid].next = cbfree;
338 cbfree = &cblist [cbid];
340 //TODO// Might report an error back, to indicate ignorance
342 pthread_mutex_unlock (&cbfree_mutex);
346 /* Forget all callbacks that were sent to the given clientfd, by posting an
347 * ERROR message to them. This is used to avoid infinitely waiting threads
348 * in the TLS Pool when a clientfd is closed by the client (perhaps due to
349 * a crash in response to the callback).
351 static void free_callbacks_by_clientfd (pool_handle_t clientfd) {
353 for (i=0; i<1024; i++) {
354 //TODO// == clientfd was >= 0 (and managed to get closes sent back to all)
355 if (cblist [i].fd == clientfd) {
356 struct command *errcmd;
357 errcmd = allocate_command_for_clientfd (clientfd);
358 errcmd->clientfd = clientfd;
361 errcmd->cmd.pio_reqid = 0; // Don't know how to set it
362 errcmd->cmd.pio_cbid = i + 1;
363 errcmd->cmd.pio_cmd = PIOC_ERROR_V2;
364 errcmd->cmd.pio_data.pioc_error.tlserrno = E_TLSPOOL_CLIENT_DISCONNECT;
365 snprintf (errcmd->cmd.pio_data.pioc_error.message, 127, "TLS Pool client fd %d closed", clientfd);
366 printf ("DEBUG: Freeing callback with cbid=%d for clientfd %d\n", i+1, clientfd);
367 post_callback (errcmd);
368 printf ("DEBUG: Freed callback with cbid=%d for clientfd %d\n", i+1, clientfd);
374 /* Process an info query; it depends on what is being asked,
375 * where it should be directed. Not everything is TLS :-)
377 static void process_command_info (struct command *cmd) {
378 uint8_t *ctlkey = cmd->cmd.pio_data.pioc_info.ctlkey;
379 uint32_t kind = cmd->cmd.pio_data.pioc_info.info_kind;
380 uint16_t len = cmd->cmd.pio_data.pioc_info.len;
381 uint8_t *buf = cmd->cmd.pio_data.pioc_info.buffer;
383 // Is the control key valid?
384 struct ctlkeynode *node = ctlkey_find (cmd->cmd.pio_data.pioc_info.ctlkey, security_tls, cmd->clientfd);
386 send_error (cmd, E_TLSPOOL_CTLKEY_NOT_FOUND,
387 "TLS Pool cannot find the control key");
391 // Ensure proper sizing of the request
392 if ((len > sizeof (cmd->cmd.pio_data.pioc_info.buffer)) && (len != 0xffff)) {
393 send_error (cmd, E_TLSPOOL_COMMAND_NOTIMPL, "TLS Pool command or variety not implemented");
397 // Invoke a handler specific to the kind of information
399 case PIOK_INFO_PEERCERT_SUBJECT:
400 case PIOK_INFO_MYCERT_SUBJECT:
401 starttls_info_cert_subject (cmd, node, len, buf);
403 case PIOK_INFO_PEERCERT_ISSUER:
404 case PIOK_INFO_MYCERT_ISSUER:
405 starttls_info_cert_issuer (cmd, node, len, buf);
407 case PIOK_INFO_PEERCERT_SUBJECTALTNAME:
408 case PIOK_INFO_MYCERT_SUBJECTALTNAME:
409 starttls_info_cert_subjectaltname (cmd, node, len, buf);
411 case PIOK_INFO_CHANBIND_TLS_UNIQUE:
412 starttls_info_chanbind_tls_unique (cmd, node, len, buf);
414 case PIOK_INFO_CHANBIND_TLS_SERVER_END_POINT:
415 starttls_info_chanbind_tls_server_end_point (cmd, node, len, buf);
418 send_error (cmd, E_TLSPOOL_INFOKIND_UNKNOWN, "TLS Pool does not support that kind of info");
422 // Cleanup; this involves unlocking the ctlkey to other messages
424 ctlkey_unfind (node);
430 /* Process a command packet that entered on a TLS pool socket
432 static void process_command (struct command *cmd) {
433 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Processing command 0x%08x, passfd=%d", cmd->cmd.pio_cmd, cmd->passfd);
434 union pio_data *d = &cmd->cmd.pio_data;
435 if (is_callback (cmd)) {
436 tlog (TLOG_UNIXSOCK, LOG_DEBUG, "DEBUG: Processing callback command sent over fd=%d\n", cmd->clientfd);
440 switch (cmd->cmd.pio_cmd) {
442 strcpy (d->pioc_ping.YYYYMMDD_producer, TLSPOOL_IDENTITY_V2);
443 d->pioc_ping.facilities &= facilities;
444 send_command (cmd, -1);
446 case PIOC_STARTTLS_V2:
450 if (facilities & PIOF_FACILITY_STARTTLS) {
453 send_error (cmd, E_TLSPOOL_FACILITY_STARTTLS,
454 "TLS Pool setup excludes STARTTLS facility");
458 process_command_info (cmd);
460 case PIOC_CONTROL_DETACH_V2:
463 case PIOC_CONTROL_REATTACH_V2:
464 ctlkey_reattach (cmd);
466 case PIOC_PINENTRY_V2:
467 register_pinentry_command (cmd);
469 case PIOC_LIDENTRY_REGISTER_V2:
470 register_lidentry_command (cmd);
473 send_error (cmd, E_TLSPOOL_COMMAND_UNKNOWN, "TLS Pool command unrecognised");
478 /* Request orderly hangup of the service.
480 void hangup_service (void) {
482 tlog (TLOG_UNIXSOCK, LOG_NOTICE, "Requested service to hangup soon");
485 /* The main service loop. It uses poll() to find things to act upon. */
486 void run_service (void) {
490 errno = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);
492 tlog (TLOG_UNIXSOCK | TLOG_DAEMON, LOG_ERR, "Service routine thread cancellability refused");
495 for (i=0; i<1024; i++) {
496 cblist [i].next = cbfree;
497 cblist [i].fd = INVALID_POOL_HANDLE; // Mark as unused
498 pthread_cond_init (&cblist [i].semaphore, NULL);
499 cblist [i].followup = NULL;
500 cbfree = &cblist [i];
506 #include "service_windows.c"
508 #include "service_posix.c"