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", (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 
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