| |
| /* |
| * Licensed Materials - Property of IBM |
| * |
| * trousers - An open source TCG Software Stack |
| * |
| * (C) Copyright International Business Machines Corp. 2004 |
| * |
| */ |
| |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <sys/wait.h> |
| #include <sys/stat.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <netdb.h> |
| #include <pwd.h> |
| #if (defined (__OpenBSD__) || defined (__FreeBSD__)) |
| #include <netinet/in.h> |
| #endif |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include "trousers/tss.h" |
| #include "trousers_types.h" |
| #include "tcs_tsp.h" |
| #include "tcs_utils.h" |
| #include "tcs_int_literals.h" |
| #include "capabilities.h" |
| #include "tcslog.h" |
| #include "tcsd_wrap.h" |
| #include "tcsps.h" |
| #include "tcsd.h" |
| #include "req_mgr.h" |
| |
| struct tcsd_config tcsd_options; |
| struct tpm_properties tpm_metrics; |
| static volatile int hup = 0, term = 0; |
| extern char *optarg; |
| |
| static void |
| tcsd_shutdown(void) |
| { |
| /* order is important here: |
| * allow all threads to complete their current request */ |
| tcsd_threads_final(); |
| PS_close_disk_cache(); |
| auth_mgr_final(); |
| (void)req_mgr_final(); |
| conf_file_final(&tcsd_options); |
| EVENT_LOG_final(); |
| } |
| |
| static void |
| tcsd_signal_term(int signal) |
| { |
| term = 1; |
| } |
| |
| void |
| tcsd_signal_hup(int signal) |
| { |
| hup = 1; |
| } |
| |
| static TSS_RESULT |
| signals_init(void) |
| { |
| int rc; |
| sigset_t sigmask; |
| struct sigaction sa; |
| |
| sigemptyset(&sigmask); |
| if ((rc = sigaddset(&sigmask, SIGTERM))) { |
| LogError("sigaddset: %s", strerror(errno)); |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| if ((rc = sigaddset(&sigmask, SIGHUP))) { |
| LogError("sigaddset: %s", strerror(errno)); |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| |
| if ((rc = THREAD_SET_SIGNAL_MASK(SIG_UNBLOCK, &sigmask, NULL))) { |
| LogError("Setting thread signal mask: %s", strerror(rc)); |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| |
| sa.sa_flags = 0; |
| sigemptyset(&sa.sa_mask); |
| sa.sa_handler = tcsd_signal_term; |
| if ((rc = sigaction(SIGTERM, &sa, NULL))) { |
| LogError("signal SIGTERM not registered: %s", strerror(errno)); |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| |
| sa.sa_handler = tcsd_signal_hup; |
| if ((rc = sigaction(SIGHUP, &sa, NULL))) { |
| LogError("signal SIGHUP not registered: %s", strerror(errno)); |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| |
| return TSS_SUCCESS; |
| } |
| |
| static TSS_RESULT |
| tcsd_startup(void) |
| { |
| TSS_RESULT result; |
| |
| #ifdef TSS_DEBUG |
| /* Set stdout to be unbuffered to match stderr and interleave output correctly */ |
| setvbuf(stdout, (char *)NULL, _IONBF, 0); |
| #endif |
| |
| if ((result = signals_init())) |
| return result; |
| |
| if ((result = conf_file_init(&tcsd_options))) |
| return result; |
| |
| if ((result = tcsd_threads_init())) { |
| conf_file_final(&tcsd_options); |
| return result; |
| } |
| |
| if ((result = req_mgr_init())) { |
| conf_file_final(&tcsd_options); |
| return result; |
| } |
| |
| if ((result = ps_dirs_init())) { |
| conf_file_final(&tcsd_options); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| result = PS_init_disk_cache(); |
| if (result != TSS_SUCCESS) { |
| conf_file_final(&tcsd_options); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| if ((result = get_tpm_metrics(&tpm_metrics))) { |
| conf_file_final(&tcsd_options); |
| PS_close_disk_cache(); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| /* must happen after get_tpm_metrics() */ |
| if ((result = auth_mgr_init())) { |
| conf_file_final(&tcsd_options); |
| PS_close_disk_cache(); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| result = EVENT_LOG_init(); |
| if (result != TSS_SUCCESS) { |
| auth_mgr_final(); |
| conf_file_final(&tcsd_options); |
| PS_close_disk_cache(); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| result = owner_evict_init(); |
| if (result != TSS_SUCCESS) { |
| auth_mgr_final(); |
| conf_file_final(&tcsd_options); |
| PS_close_disk_cache(); |
| (void)req_mgr_final(); |
| return result; |
| } |
| |
| return TSS_SUCCESS; |
| } |
| |
| |
| void |
| usage(void) |
| { |
| fprintf(stderr, "\tusage: tcsd [-f] [-h]\n\n"); |
| fprintf(stderr, "\t-f|--foreground\trun in the foreground. Logging goes to stderr " |
| "instead of syslog.\n"); |
| fprintf(stderr, "\t-e| attempts to connect to software TPMs over TCP"); |
| fprintf(stderr, "\t-h|--help\tdisplay this help message\n"); |
| fprintf(stderr, "\n"); |
| } |
| |
| static TSS_RESULT |
| reload_config(void) |
| { |
| TSS_RESULT result; |
| hup = 0; |
| |
| // FIXME: reload the config - work in progress |
| result = TSS_SUCCESS; |
| |
| return result; |
| } |
| |
| |
| int |
| main(int argc, char **argv) |
| { |
| struct sockaddr_un serv_addr, client_addr; |
| TSS_RESULT result; |
| int sd, newsd, c, option_index = 0; |
| unsigned client_len; |
| struct passwd *pwd; |
| struct hostent *client_hostent = NULL; |
| struct option long_options[] = { |
| {"help", 0, NULL, 'h'}, |
| {"foreground", 0, NULL, 'f'}, |
| {0, 0, 0, 0} |
| }; |
| |
| unsetenv("TCSD_USE_TCP_DEVICE"); |
| while ((c = getopt_long(argc, argv, "fhe", long_options, &option_index)) != -1) { |
| switch (c) { |
| case 'f': |
| setenv("TCSD_FOREGROUND", "1", 1); |
| break; |
| case 'h': |
| /* fall through */ |
| case 'e': |
| setenv("TCSD_USE_TCP_DEVICE", "1", 1); |
| break; |
| default: |
| usage(); |
| return -1; |
| break; |
| } |
| } |
| |
| if ((result = tcsd_startup())) |
| return (int)result; |
| |
| sd = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (sd < 0) { |
| LogError("Failed socket: %s", strerror(errno)); |
| return -1; |
| } |
| |
| memset(&serv_addr, 0, sizeof (serv_addr)); |
| serv_addr.sun_family = AF_UNIX; |
| strcpy(serv_addr.sun_path, TCSD_UNIX_SOCKET); |
| unlink(serv_addr.sun_path); |
| |
| c = 1; |
| setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &c, sizeof(c)); |
| if (bind(sd, (struct sockaddr *) &serv_addr, |
| strlen(serv_addr.sun_path) + sizeof (serv_addr.sun_family)) |
| < 0) { |
| LogError("Failed bind: %s", strerror(errno)); |
| return -1; |
| } |
| if (chmod(serv_addr.sun_path, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) { |
| LogError("Failed chmod: %s", strerror(errno)); |
| return -1; |
| } |
| #ifndef SOLARIS |
| pwd = getpwnam(TSS_USER_NAME); |
| if (pwd == NULL) { |
| if (errno == 0) { |
| LogError("User \"%s\" not found, please add this user" |
| " manually.", TSS_USER_NAME); |
| } else { |
| LogError("getpwnam(%s): %s", TSS_USER_NAME, strerror(errno)); |
| } |
| return TCSERR(TSS_E_INTERNAL_ERROR); |
| } |
| if (chown(serv_addr.sun_path, -1, pwd->pw_gid) < 0) { |
| LogError("Failed chown: %s", strerror(errno)); |
| return -1; |
| } |
| setuid(pwd->pw_uid); |
| #endif |
| if (listen(sd, TCSD_MAX_SOCKETS_QUEUED) < 0) { |
| LogError("Failed listen: %s", strerror(errno)); |
| return -1; |
| } |
| client_len = (unsigned)sizeof(client_addr); |
| |
| if (getenv("TCSD_FOREGROUND") == NULL) { |
| if (daemon(0, 0) == -1) { |
| perror("daemon"); |
| tcsd_shutdown(); |
| return -1; |
| } |
| } |
| |
| LogInfo("%s: TCSD up and running.", PACKAGE_STRING); |
| do { |
| newsd = accept(sd, (struct sockaddr *) &client_addr, &client_len); |
| if (newsd < 0) { |
| if (errno == EINTR) { |
| if (term) |
| break; |
| else if (hup) { |
| if (reload_config() != TSS_SUCCESS) |
| LogError("Failed reloading config"); |
| } |
| continue; |
| } else { |
| LogError("Failed accept: %s", strerror(errno)); |
| continue; |
| } |
| } |
| LogDebug("accepted socket %i", newsd); |
| |
| // We're listening on a domain socket, so just use "localhost" |
| tcsd_thread_create(newsd, strdup("localhost")); |
| if (hup) { |
| if (reload_config() != TSS_SUCCESS) |
| LogError("Failed reloading config"); |
| } |
| } while (term ==0); |
| |
| /* To close correctly, we must receive a SIGTERM */ |
| return 0; |
| } |