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