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			if (thread_num != -1)
169				tm->thread_data[thread_num].hostname = NULL;
170			free(hostname);
171		}
172		close(socket);
173	}
174	return rc;
175}
176
177/* Since we don't want any of the worker threads to catch any signals, we must mask off any
178 * potential signals here after creating the threads.  If any of the created threads catch a signal,
179 * they'd eventually call join on themselves, causing a deadlock.
180 */
181void
182thread_signal_init()
183{
184	sigset_t thread_sigmask;
185	int rc;
186
187	if ((rc = sigfillset(&thread_sigmask))) {
188		LogError("sigfillset failed: error=%d: %s", rc, strerror(rc));
189		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
190		THREAD_EXIT(NULL);
191	}
192
193	if ((rc = THREAD_SET_SIGNAL_MASK(SIG_BLOCK, &thread_sigmask, NULL))) {
194		LogError("Setting thread sigmask failed: error=%d: %s", rc, strerror(rc));
195		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
196		THREAD_EXIT(NULL);
197	}
198}
199
200void *
201tcsd_thread_run(void *v)
202{
203	struct tcsd_thread_data *data = (struct tcsd_thread_data *)v;
204	BYTE *buffer;
205	int recd_so_far, empty_space, total_recv_size, recv_chunk_size, send_size;
206	TSS_RESULT result;
207	UINT64 offset;
208#ifndef TCSD_SINGLE_THREAD_DEBUG
209	int rc;
210
211	thread_signal_init();
212#endif
213
214	data->comm.buf_size = TCSD_INIT_TXBUF_SIZE;
215	data->comm.buf = calloc(1, data->comm.buf_size);
216	while (data->comm.buf) {
217		/* get the packet header to get the size of the incoming packet */
218		if (recv_from_socket(data->sock, data->comm.buf,
219				     sizeof(struct tcsd_packet_hdr)) < 0)
220			break;
221
222		recd_so_far = sizeof(struct tcsd_packet_hdr);
223
224		/* check the packet size */
225		total_recv_size = Decode_UINT32(data->comm.buf);
226		if (total_recv_size < (int)sizeof(struct tcsd_packet_hdr)) {
227			LogError("Packet to receive from socket %d is too small (%d bytes)",
228				 data->sock, total_recv_size);
229			break;
230		}
231
232		LogDebug("total_recv_size %d, buf_size %u, recd_so_far %d", total_recv_size,
233			 data->comm.buf_size, recd_so_far);
234
235		empty_space = data->comm.buf_size - recd_so_far;
236
237		/* instead of blindly allocating recv_size bytes off the bat, stage the realloc
238		 * and wait for the data to come in over the socket. This protects against
239		 * trivially asking tcsd to alloc 2GB */
240		while (total_recv_size > (int) data->comm.buf_size) {
241			BYTE *new_buffer;
242			int new_bufsize;
243
244			if ((int)data->comm.buf_size + TCSD_INCR_TXBUF_SIZE < total_recv_size) {
245				new_bufsize = data->comm.buf_size + TCSD_INCR_TXBUF_SIZE;
246				recv_chunk_size = empty_space + TCSD_INCR_TXBUF_SIZE;
247			} else {
248				new_bufsize = total_recv_size;
249				recv_chunk_size = total_recv_size - recd_so_far;
250			}
251
252			LogDebug("Increasing communication buffer to %d bytes.", new_bufsize);
253			new_buffer = realloc(data->comm.buf, new_bufsize);
254			if (new_buffer == NULL) {
255				LogError("realloc of %d bytes failed.", new_bufsize);
256				data->comm.buf = NULL;
257				goto no_mem_error;
258			}
259
260			data->comm.buf_size = new_bufsize;
261			data->comm.buf = new_buffer;
262			buffer = data->comm.buf + recd_so_far;
263
264			LogDebug("recv_chunk_size %d recd_so_far %d", recv_chunk_size, recd_so_far);
265			if (recv_from_socket(data->sock, buffer, recv_chunk_size) < 0) {
266				result = TCSERR(TSS_E_INTERNAL_ERROR);
267				goto error;
268			}
269
270			recd_so_far += recv_chunk_size;
271			empty_space = 0;
272		}
273
274		if (recd_so_far < total_recv_size) {
275			buffer = data->comm.buf + recd_so_far;
276			recv_chunk_size = total_recv_size - recd_so_far;
277
278			LogDebug("recv_chunk_size %d recd_so_far %d", recv_chunk_size, recd_so_far);
279
280			if (recv_from_socket(data->sock, buffer, recv_chunk_size) < 0) {
281				result = TCSERR(TSS_E_INTERNAL_ERROR);
282				goto error;
283			}
284		}
285		LogDebug("Rx'd packet");
286
287		/* create a platform version of the tcsd header */
288		offset = 0;
289		UnloadBlob_UINT32(&offset, &data->comm.hdr.packet_size, data->comm.buf);
290		UnloadBlob_UINT32(&offset, &data->comm.hdr.u.result, data->comm.buf);
291		UnloadBlob_UINT32(&offset, &data->comm.hdr.num_parms, data->comm.buf);
292		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_size, data->comm.buf);
293		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_offset, data->comm.buf);
294		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_size, data->comm.buf);
295		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_offset, data->comm.buf);
296
297		result = getTCSDPacket(data);
298error:
299		if (result) {
300			/* something internal to the TCSD went wrong in preparing the packet
301			 * to return to the TSP.  Use our already allocated buffer to return a
302			 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path,
303			 * these LoadBlob's are done in getTCSDPacket().
304			 */
305			/* set everything to zero, fill in what is non-zero */
306			memset(data->comm.buf, 0, data->comm.buf_size);
307			offset = 0;
308			/* load packet size */
309			LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), data->comm.buf);
310			/* load result */
311			LoadBlob_UINT32(&offset, result, data->comm.buf);
312		}
313		send_size = Decode_UINT32(data->comm.buf);
314		LogDebug("Sending 0x%X bytes back", send_size);
315		send_size = send_to_socket(data->sock, data->comm.buf, send_size);
316		if (send_size < 0)
317			break;
318
319		/* check for shutdown */
320		if (tm->shutdown) {
321			LogDebug("Thread %ld exiting via shutdown signal!", THREAD_ID);
322			break;
323		}
324	}
325no_mem_error:
326	LogDebug("Thread exiting.");
327
328	/* Closing connection to TSP */
329	close(data->sock);
330	data->sock = -1;
331	free(data->comm.buf);
332	data->comm.buf = NULL;
333	data->comm.buf_size = -1;
334	/* If the connection was not shut down cleanly, free TCS resources here */
335	if (data->context != NULL_TCS_HANDLE) {
336		TCS_CloseContext_Internal(data->context);
337		data->context = NULL_TCS_HANDLE;
338	}
339	if(data->hostname != NULL) {
340		free(data->hostname);
341		data->hostname = NULL;
342	}
343
344#ifndef TCSD_SINGLE_THREAD_DEBUG
345	pthread_mutex_lock(&(tm->lock));
346	tm->num_active_threads--;
347	/* if we're not in shutdown mode, then nobody is waiting to join this thread, so
348	 * detach it so that its resources are free at pthread_exit() time. */
349	if (!tm->shutdown) {
350		if ((rc = pthread_detach(*(data->thread_id)))) {
351			LogError("pthread_detach failed (errno %d)."
352				 " Resources may not be properly released.", rc);
353		}
354	}
355	free(data->thread_id);
356	data->thread_id = THREAD_NULL;
357	pthread_mutex_unlock(&(tm->lock));
358	pthread_exit(NULL);
359#endif
360	return NULL;
361}
362