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 
31 struct tcsd_thread_mgr *tm = NULL;
32 
33 TSS_RESULT
tcsd_threads_final()34 tcsd_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 
60 TSS_RESULT
tcsd_threads_init(void)61 tcsd_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 
88 TSS_RESULT
tcsd_thread_create(int socket,char * hostname)89 tcsd_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 
161 out_unlock:
162 	MUTEX_UNLOCK(tm->lock);
163 #endif
164 out:
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  */
181 void
thread_signal_init()182 thread_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", 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", THREAD_ID);
196 		THREAD_EXIT(NULL);
197 	}
198 }
199 
200 void *
tcsd_thread_run(void * v)201 tcsd_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);
298 error:
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 	}
325 no_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