1
2/*
3 * Licensed Materials - Property of IBM
4 *
5 * trousers - An open source TCG Software Stack
6 *
7 * (C) Copyright International Business Machines Corp. 2004-2006
8 *
9 */
10
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <syslog.h>
15#include <errno.h>
16#include <string.h>
17#include <unistd.h>
18#include <sys/types.h>
19#include <sys/socket.h>
20
21#include "trousers/tss.h"
22#include "trousers_types.h"
23#include "tcs_int_literals.h"
24#include "tcs_tsp.h"
25#include "tcs_utils.h"
26#include "tcsd_wrap.h"
27#include "tcsd.h"
28#include "tcslog.h"
29#include "rpc_tcstp_tcs.h"
30
31struct tcsd_thread_mgr *tm = NULL;
32
33TSS_RESULT
34tcsd_threads_final()
35{
36	int rc;
37	UINT32 i;
38
39	MUTEX_LOCK(tm->lock);
40
41	tm->shutdown = 1;
42
43	MUTEX_UNLOCK(tm->lock);
44
45	/* wait for all currently running threads to exit */
46	for (i = 0; i < tm->max_threads; i++) {
47		if (tm->thread_data[i].thread_id != THREAD_NULL) {
48			if ((rc = THREAD_JOIN(*(tm->thread_data[i].thread_id), NULL))) {
49				LogError("Thread join failed: error: %d", rc);
50			}
51		}
52	}
53
54	free(tm->thread_data);
55	free(tm);
56
57	return TSS_SUCCESS;
58}
59
60TSS_RESULT
61tcsd_threads_init(void)
62{
63	/* allocate the thread mgmt structure */
64	tm = calloc(1, sizeof(struct tcsd_thread_mgr));
65	if (tm == NULL) {
66		LogError("malloc of %zd bytes failed.", sizeof(struct tcsd_thread_mgr));
67		return TCSERR(TSS_E_OUTOFMEMORY);
68	}
69	/* initialize mutex */
70	MUTEX_INIT(tm->lock);
71
72	/* set the max threads variable from config */
73	tm->max_threads = tcsd_options.num_threads;
74
75	/* allocate each thread's data structure */
76	tm->thread_data = calloc(tcsd_options.num_threads, sizeof(struct tcsd_thread_data));
77	if (tm->thread_data == NULL) {
78		LogError("malloc of %zu bytes failed.",
79			 tcsd_options.num_threads * sizeof(struct tcsd_thread_data));
80		free(tm);
81		return TCSERR(TSS_E_OUTOFMEMORY);
82	}
83
84	return TSS_SUCCESS;
85}
86
87
88TSS_RESULT
89tcsd_thread_create(int socket, char *hostname)
90{
91	UINT32 thread_num = -1;
92	int rc = TCS_SUCCESS;
93#ifndef TCSD_SINGLE_THREAD_DEBUG
94	THREAD_ATTR_DECLARE(tcsd_thread_attr);
95
96	/* init the thread attribute */
97	if ((rc = THREAD_ATTR_INIT(tcsd_thread_attr))) {
98		LogError("Initializing thread attribute failed: error=%d: %s", rc, strerror(rc));
99		rc = TCSERR(TSS_E_INTERNAL_ERROR);
100		goto out;
101	}
102	/* make all threads joinable */
103	if ((rc = THREAD_ATTR_SETJOINABLE(tcsd_thread_attr))) {
104		LogError("Making thread attribute joinable failed: error=%d: %s", rc, strerror(rc));
105		rc = TCSERR(TSS_E_INTERNAL_ERROR);
106		goto out;
107	}
108
109	MUTEX_LOCK(tm->lock);
110#endif
111	if (tm->num_active_threads == tm->max_threads) {
112		if (hostname != NULL) {
113			LogError("max number of connections reached (%d), new connection"
114				 " from %s refused.", tm->max_threads, hostname);
115		} else {
116			LogError("max number of connections reached (%d), new connection"
117				 " refused.", tm->max_threads);
118		}
119		rc = TCSERR(TSS_E_CONNECTION_FAILED);
120#ifndef TCSD_SINGLE_THREAD_DEBUG
121		goto out_unlock;
122#else
123		goto out;
124#endif
125	}
126
127	/* search for an open slot to store the thread data in */
128	for (thread_num = 0; thread_num < tm->max_threads; thread_num++) {
129		if (tm->thread_data[thread_num].thread_id == THREAD_NULL)
130			break;
131	}
132
133	DBG_ASSERT(thread_num != tm->max_threads);
134
135	tm->thread_data[thread_num].sock = socket;
136	tm->thread_data[thread_num].context = NULL_TCS_HANDLE;
137	if (hostname != NULL)
138		tm->thread_data[thread_num].hostname = hostname;
139
140#ifdef TCSD_SINGLE_THREAD_DEBUG
141	(void)tcsd_thread_run((void *)(&(tm->thread_data[thread_num])));
142#else
143	tm->thread_data[thread_num].thread_id = calloc(1, sizeof(THREAD_TYPE));
144	if (tm->thread_data[thread_num].thread_id == NULL) {
145		rc = TCSERR(TSS_E_OUTOFMEMORY);
146		LogError("malloc of %zd bytes failed.", sizeof(THREAD_TYPE));
147		goto out_unlock;
148	}
149
150	if ((rc = THREAD_CREATE(tm->thread_data[thread_num].thread_id,
151				 &tcsd_thread_attr,
152				 tcsd_thread_run,
153				 (void *)(&(tm->thread_data[thread_num]))))) {
154		LogError("Thread create failed: %d", rc);
155		rc = TCSERR(TSS_E_INTERNAL_ERROR);
156		goto out_unlock;
157	}
158
159	tm->num_active_threads++;
160
161out_unlock:
162	MUTEX_UNLOCK(tm->lock);
163#endif
164out:
165	/* cleanup in case of error */
166	if (rc != TCS_SUCCESS) {
167		if (hostname != NULL) {
168			tm->thread_data[thread_num].hostname = NULL;
169			free(hostname);
170		}
171		close(socket);
172	}
173	return rc;
174}
175
176/* Since we don't want any of the worker threads to catch any signals, we must mask off any
177 * potential signals here after creating the threads.  If any of the created threads catch a signal,
178 * they'd eventually call join on themselves, causing a deadlock.
179 */
180void
181thread_signal_init()
182{
183	sigset_t thread_sigmask;
184	int rc;
185
186	if ((rc = sigfillset(&thread_sigmask))) {
187		LogError("sigfillset failed: error=%d: %s", rc, strerror(rc));
188		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
189		THREAD_EXIT(NULL);
190	}
191
192	if ((rc = THREAD_SET_SIGNAL_MASK(SIG_BLOCK, &thread_sigmask, NULL))) {
193		LogError("Setting thread sigmask failed: error=%d: %s", rc, strerror(rc));
194		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
195		THREAD_EXIT(NULL);
196	}
197}
198#if 0
199void *
200tcsd_thread_run(void *v)
201{
202	struct tcsd_thread_data *data = (struct tcsd_thread_data *)v;
203	BYTE buffer[TCSD_TXBUF_SIZE];
204	struct tcsd_packet_hdr *ret_buf = NULL;
205	TSS_RESULT result;
206	int sizeToSend, sent_total, sent;
207	UINT64 offset;
208#ifndef TCSD_SINGLE_THREAD_DEBUG
209	int rc;
210
211	thread_signal_init();
212#endif
213
214	if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) {
215		LogError("Failed Receive: %s", strerror(errno));
216		goto done;
217	}
218	LogDebug("Rx'd packet");
219
220	data->buf = buffer;
221
222	while (1) {
223		sent_total = 0;
224		if (data->buf_size > TCSD_TXBUF_SIZE) {
225			LogError("Packet received from socket %d was too large (%u bytes)",
226				 data->sock, data->buf_size);
227			goto done;
228		} else if (data->buf_size < (int)((2 * sizeof(UINT32)) + sizeof(UINT16))) {
229			LogError("Packet received from socket %d was too small (%u bytes)",
230				 data->sock, data->buf_size);
231			goto done;
232		}
233
234		if ((result = getTCSDPacket(data, &ret_buf)) != TSS_SUCCESS) {
235			/* something internal to the TCSD went wrong in preparing the packet
236			 * to return to the TSP.  Use our already allocated buffer to return a
237			 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path,
238			 * these LoadBlob's are done in getTCSDPacket().
239			 */
240			offset = 0;
241			/* load result */
242			LoadBlob_UINT32(&offset, result, buffer);
243			/* load packet size */
244			LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), buffer);
245			/* load num parms */
246			LoadBlob_UINT16(&offset, 0, buffer);
247
248			sizeToSend = sizeof(struct tcsd_packet_hdr);
249			LogDebug("Sending 0x%X bytes back", sizeToSend);
250
251			while (sent_total < sizeToSend) {
252				if ((sent = send(data->sock,
253						 &data->buf[sent_total],
254						 sizeToSend - sent_total, 0)) < 0) {
255					LogError("Packet send to TSP failed: send: %s. Thread exiting.",
256							strerror(errno));
257					goto done;
258				}
259				sent_total += sent;
260			}
261		} else {
262			sizeToSend = Decode_UINT32((BYTE *)&(ret_buf->packet_size));
263
264			LogDebug("Sending 0x%X bytes back", sizeToSend);
265
266			while (sent_total < sizeToSend) {
267				if ((sent = send(data->sock,
268						 &(((BYTE *)ret_buf)[sent_total]),
269						 sizeToSend - sent_total, 0)) < 0) {
270					LogError("response to TSP failed: send: %s. Thread exiting.",
271							strerror(errno));
272					free(ret_buf);
273					ret_buf = NULL;
274					goto done;
275				}
276				sent_total += sent;
277			}
278			free(ret_buf);
279			ret_buf = NULL;
280		}
281
282		if (tm->shutdown) {
283			LogDebug("Thread %zd exiting via shutdown signal!", THREAD_ID);
284			break;
285		}
286
287		/* receive the next packet */
288		if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) {
289			LogError("TSP has closed its connection: %s. Thread exiting.",
290				 strerror(errno));
291			break;
292		} else if (data->buf_size == 0) {
293			LogDebug("The TSP has closed the socket's connection. Thread exiting.");
294			break;
295		}
296	}
297
298done:
299	/* Closing connection to TSP */
300	close(data->sock);
301	data->sock = -1;
302	data->buf = NULL;
303	data->buf_size = -1;
304	/* If the connection was not shut down cleanly, free TCS resources here */
305	if (data->context != NULL_TCS_HANDLE) {
306		TCS_CloseContext_Internal(data->context);
307		data->context = NULL_TCS_HANDLE;
308	}
309
310#ifndef TCSD_SINGLE_THREAD_DEBUG
311	MUTEX_LOCK(tm->lock);
312	tm->num_active_threads--;
313	/* if we're not in shutdown mode, then nobody is waiting to join this thread, so
314	 * detach it so that its resources are free at THREAD_EXIT() time. */
315	if (!tm->shutdown) {
316		if ((rc = THREAD_DETACH(*(data->thread_id)))) {
317			LogError("Thread detach failed (errno %d)."
318				 " Resources may not be properly released.", rc);
319		}
320	}
321	free(data->hostname);
322	data->hostname = NULL;
323	data->thread_id = THREAD_NULL;
324	MUTEX_UNLOCK(tm->lock);
325	THREAD_EXIT(NULL);
326#else
327	return NULL;
328#endif
329}
330#else
331void *
332tcsd_thread_run(void *v)
333{
334	struct tcsd_thread_data *data = (struct tcsd_thread_data *)v;
335	BYTE *buffer;
336	int recv_size, send_size;
337	TSS_RESULT result;
338	UINT64 offset;
339#ifndef TCSD_SINGLE_THREAD_DEBUG
340	int rc;
341
342	thread_signal_init();
343#endif
344
345	data->comm.buf_size = TCSD_INIT_TXBUF_SIZE;
346	data->comm.buf = calloc(1, data->comm.buf_size);
347	while (data->comm.buf) {
348		/* get the packet header to get the size of the incoming packet */
349		buffer = data->comm.buf;
350		recv_size = sizeof(struct tcsd_packet_hdr);
351		if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0)
352			break;
353		buffer += sizeof(struct tcsd_packet_hdr);       /* increment the buffer pointer */
354
355		/* check the packet size */
356		recv_size = Decode_UINT32(data->comm.buf);
357		if (recv_size < (int)sizeof(struct tcsd_packet_hdr)) {
358			LogError("Packet to receive from socket %d is too small (%d bytes)",
359					data->sock, recv_size);
360			break;
361		}
362
363		if (recv_size > data->comm.buf_size ) {
364			BYTE *new_buffer;
365
366			LogDebug("Increasing communication buffer to %d bytes.", recv_size);
367			new_buffer = realloc(data->comm.buf, recv_size);
368			if (new_buffer == NULL) {
369				LogError("realloc of %d bytes failed.", recv_size);
370				break;
371			}
372			buffer = new_buffer + sizeof(struct tcsd_packet_hdr);
373			data->comm.buf_size = recv_size;
374			data->comm.buf = new_buffer;
375		}
376
377		/* get the rest of the packet */
378		recv_size -= sizeof(struct tcsd_packet_hdr);    /* already received the header */
379		if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0)
380			break;
381		LogDebug("Rx'd packet");
382
383		/* create a platform version of the tcsd header */
384		offset = 0;
385		UnloadBlob_UINT32(&offset, &data->comm.hdr.packet_size, data->comm.buf);
386		UnloadBlob_UINT32(&offset, &data->comm.hdr.u.result, data->comm.buf);
387		UnloadBlob_UINT32(&offset, &data->comm.hdr.num_parms, data->comm.buf);
388		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_size, data->comm.buf);
389		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_offset, data->comm.buf);
390		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_size, data->comm.buf);
391		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_offset, data->comm.buf);
392
393		if ((result = getTCSDPacket(data)) != TSS_SUCCESS) {
394			/* something internal to the TCSD went wrong in preparing the packet
395			 * to return to the TSP.  Use our already allocated buffer to return a
396			 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path,
397			 * these LoadBlob's are done in getTCSDPacket().
398			 */
399			/* set everything to zero, fill in what is non-zero */
400			memset(data->comm.buf, 0, data->comm.buf_size);
401			offset = 0;
402			/* load packet size */
403			LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), data->comm.buf);
404			/* load result */
405			LoadBlob_UINT32(&offset, result, data->comm.buf);
406		}
407		send_size = Decode_UINT32(data->comm.buf);
408		LogDebug("Sending 0x%X bytes back", send_size);
409		send_size = send_to_socket(data->sock, data->comm.buf, send_size);
410		if (send_size < 0)
411			break;
412
413		/* check for shutdown */
414		if (tm->shutdown) {
415			LogDebug("Thread %ld exiting via shutdown signal!", THREAD_ID);
416			break;
417		}
418	}
419
420	LogDebug("Thread exiting.");
421
422	/* Closing connection to TSP */
423	close(data->sock);
424	data->sock = -1;
425	free(data->comm.buf);
426	data->comm.buf = NULL;
427	data->comm.buf_size = -1;
428	/* If the connection was not shut down cleanly, free TCS resources here */
429	if (data->context != NULL_TCS_HANDLE) {
430		TCS_CloseContext_Internal(data->context);
431		data->context = NULL_TCS_HANDLE;
432	}
433	if(data->hostname != NULL) {
434		free(data->hostname);
435		data->hostname = NULL;
436	}
437
438#ifndef TCSD_SINGLE_THREAD_DEBUG
439	pthread_mutex_lock(&(tm->lock));
440	tm->num_active_threads--;
441	/* if we're not in shutdown mode, then nobody is waiting to join this thread, so
442	 * detach it so that its resources are free at pthread_exit() time. */
443	if (!tm->shutdown) {
444		if ((rc = pthread_detach(*(data->thread_id)))) {
445			LogError("pthread_detach failed (errno %d)."
446				 " Resources may not be properly released.", rc);
447		}
448	}
449	free(data->thread_id);
450	data->thread_id = THREAD_NULL;
451	pthread_mutex_unlock(&(tm->lock));
452	pthread_exit(NULL);
453#endif
454	return NULL;
455}
456
457#endif
458