/* * Licensed Materials - Property of IBM * * trousers - An open source TCG Software Stack * * (C) Copyright International Business Machines Corp. 2004 * */ #include #include #include #include #include #include #include #include #include #include #include #ifdef SOLARIS #include #endif #include "trousers/tss.h" #include "trousers_types.h" #include "tcs_tsp.h" #include "tcs_utils.h" #include "tcsps.h" #include "tcslog.h" #include "tcsd_wrap.h" #include "tcsd.h" #include "tcsd_ops.h" struct tcsd_config_options options_list[] = { {"port", opt_port}, {"num_threads", opt_max_threads}, {"system_ps_file", opt_system_ps_file}, {"firmware_log_file", opt_firmware_log}, {"firmware_pcrs", opt_firmware_pcrs}, {"kernel_log_file", opt_kernel_log}, {"kernel_pcrs", opt_kernel_pcrs}, {"platform_cred", opt_platform_cred}, {"conformance_cred", opt_conformance_cred}, {"endorsement_cred", opt_endorsement_cred}, {"remote_ops", opt_remote_ops}, {"enforce_exclusive_transport", opt_exclusive_transport}, {"host_platform_class", opt_host_platform_class}, {"all_platform_classes", opt_all_platform_classes}, {NULL, 0} }; struct tcg_platform_spec tcg_platform_specs[] = { {"PC_11", TPM_PS_PC_11, TPM_PS_PC_11_URI}, {"PC_12", TPM_PS_PC_12, TPM_PS_PC_12_URI}, {"PDA_12", TPM_PS_PDA_12, TPM_PS_PDA_12_URI}, {"SERVER_12", TPM_PS_Server_12, TPM_PS_Server_12_URI}, {"MOBILE_12", TPM_PS_Mobile_12, TPM_PS_Mobile_12_URI}, {NULL, 0, 0} }; void init_tcsd_config(struct tcsd_config *conf) { conf->port = -1; conf->num_threads = -1; conf->system_ps_file = NULL; conf->system_ps_dir = NULL; conf->firmware_log_file = NULL; conf->firmware_pcrs = 0; conf->kernel_log_file = NULL; conf->kernel_pcrs = 0; conf->platform_cred = NULL; conf->conformance_cred = NULL; conf->endorsement_cred = NULL; memset(conf->remote_ops, 0, sizeof(conf->remote_ops)); conf->unset = 0xffffffff; conf->exclusive_transport = 0; conf->host_platform_class = NULL; conf->all_platform_classes = NULL; } TSS_RESULT platform_class_list_append(struct tcsd_config *conf, char *specName, TSS_BOOL is_main) { int i; struct platform_class *tmp, *new_class; LogDebugFn("platform_class_list_append start:"); for (i = 0; tcg_platform_specs[i].name; i++) { if (!strncasecmp(specName, tcg_platform_specs[i].name, strlen(tcg_platform_specs[i].name))) { /* Allocate the new structure */ new_class = malloc(sizeof(struct platform_class)); if (new_class == NULL) { LogError("malloc of %zd bytes failed", sizeof(struct platform_class)); return TCSERR(TSS_E_OUTOFMEMORY); } new_class->simpleID = tcg_platform_specs[i].specNo; new_class->classURISize = strlen(tcg_platform_specs[i].specURI) + 1; new_class->classURI = malloc(new_class->classURISize); if (new_class->classURI == NULL) { LogError("malloc of %u bytes failed", new_class->classURISize); return TCSERR(TSS_E_OUTOFMEMORY); } memcpy(new_class->classURI, tcg_platform_specs[i].specURI, new_class->classURISize); /* Append to the start of the list */ if (is_main) { tmp = conf->host_platform_class; conf->host_platform_class = new_class; } else { tmp = conf->all_platform_classes; conf->all_platform_classes = new_class; } new_class->next = tmp; LogDebugFn("Platform Class Added."); return TSS_SUCCESS; } } LogError("TCG Specification not supported: \"%s\"", specName); return TCSERR(TSS_E_INTERNAL_ERROR); } void config_set_defaults(struct tcsd_config *conf) { /* give all unset options their default values */ if (conf->unset & TCSD_OPTION_PORT) conf->port = TCSD_DEFAULT_PORT; if (conf->unset & TCSD_OPTION_MAX_THREADS) conf->num_threads = TCSD_DEFAULT_MAX_THREADS; if (conf->unset & TCSD_OPTION_FIRMWARE_PCRS) conf->firmware_pcrs = TCSD_DEFAULT_FIRMWARE_PCRS; if (conf->unset & TCSD_OPTION_KERNEL_PCRS) conf->kernel_pcrs = TCSD_DEFAULT_KERNEL_PCRS; /* these are strdup'd so we know we can free them at shutdown time */ if (conf->unset & TCSD_OPTION_SYSTEM_PSFILE) { conf->system_ps_file = strdup(TCSD_DEFAULT_SYSTEM_PS_FILE); conf->system_ps_dir = strdup(TCSD_DEFAULT_SYSTEM_PS_DIR); } if (conf->unset & TCSD_OPTION_FIRMWARE_LOGFILE) conf->firmware_log_file = strdup(TCSD_DEFAULT_FIRMWARE_LOG_FILE); if (conf->unset & TCSD_OPTION_KERNEL_LOGFILE) conf->kernel_log_file = strdup(TCSD_DEFAULT_KERNEL_LOG_FILE); if (conf->unset & TCSD_OPTION_HOST_PLATFORM_CLASS) platform_class_list_append(conf, "PC_12", TRUE); } int get_config_option(char *ptr, char **arg) { int i; for (i = 0; options_list[i].name; i++) { if (!strncasecmp(ptr, options_list[i].name, strlen(options_list[i].name))) { /* move ptr past our recognized token */ ptr += strlen(options_list[i].name); /* try to move ptr to the start of the option's argument */ while (*ptr == '=' || *ptr == ' ' || *ptr == '\t') ptr++; *arg = ptr; return options_list[i].option; } } /* on error we'll print the whole line to the log */ *arg = ptr; return 0; } /* copy a file path from a string into a newly malloc'd string */ int get_file_path(char *ptr, char **dest) { char tmp_buf[1024]; int i = 0; while (isalpha(*ptr) || isdigit(*ptr) || *ptr == '/' || *ptr == '.' || *ptr == '#' || *ptr == '_' || *ptr == '-') { tmp_buf[i] = *ptr; ptr++; i++; } /* move through whitespace after the path */ while (*ptr == ' ' || *ptr == '\t') ptr++; /* if we're not at a comment or EOL, there's junk */ if (*ptr != '#' && *ptr != '\n') { *dest = ptr; return 1; } /* too short a path */ if (i == 0) return -1; tmp_buf[i] = '\0'; *dest = strdup(tmp_buf); if (*dest == NULL) { LogError("malloc of %zd bytes failed", strlen(tmp_buf)); } return 0; } /* add an op ordinal, checking for duplicates along the way */ void tcsd_add_op(int *remote_ops, int *op) { int i = 0, j; while (op[i] != 0) { j = 0; while (remote_ops[j] != 0) { if (remote_ops[j] == op[i]) { break; } j++; } remote_ops[j] = op[i]; i++; } } int tcsd_set_remote_op(struct tcsd_config *conf, char *op_name) { int i = 0; while(tcsd_ops[i]) { if (!strcasecmp(tcsd_ops[i]->name, op_name)) { /* match found */ tcsd_add_op(conf->remote_ops, tcsd_ops[i]->op); return 0; } i++; } /* fail, op not found */ return 1; } TSS_RESULT read_conf_line(char *buf, int line_num, struct tcsd_config *conf) { char *ptr = buf, *tmp_ptr = NULL, *arg, *comma; int option, tmp_int; TSS_RESULT result; if (ptr == NULL || *ptr == '\0' || *ptr == '#' || *ptr == '\n') return TSS_SUCCESS; /* read through whitespace */ while (*ptr == ' ' || *ptr == '\t') ptr++; /* ignore comments */ if (*ptr == '#') return TSS_SUCCESS; option = get_config_option(ptr, &arg); switch (option) { case opt_port: tmp_int = atoi(arg); if (tmp_int < 0 || tmp_int > 65535) { LogError("Config option \"port\" out of range. %s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); return TCSERR(TSS_E_INTERNAL_ERROR); } else { conf->port = tmp_int; conf->unset &= ~TCSD_OPTION_PORT; } break; case opt_max_threads: tmp_int = atoi(arg); if (tmp_int <= 0) { LogError("Config option \"num_threads\" out of range. %s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); return TCSERR(TSS_E_INTERNAL_ERROR); } else { conf->num_threads = tmp_int; conf->unset &= ~TCSD_OPTION_MAX_THREADS; } break; case opt_firmware_pcrs: conf->unset &= ~TCSD_OPTION_FIRMWARE_PCRS; while (1) { comma = rindex(arg, ','); if (comma == NULL) { if (!isdigit(*arg)) break; comma = arg; tmp_int = atoi(comma); if (tmp_int >= 0 && tmp_int < TCSD_MAX_PCRS) conf->firmware_pcrs |= (1 << tmp_int); else LogError("Config option \"firmware_pcrs\" is out of range." "%s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); break; } *comma++ = '\0'; tmp_int = atoi(comma); if (tmp_int >= 0 && tmp_int < TCSD_MAX_PCRS) conf->firmware_pcrs |= (1 << tmp_int); else LogError("Config option \"firmware_pcrs\" is out of range. " "%s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); } break; case opt_kernel_pcrs: conf->unset &= ~TCSD_OPTION_KERNEL_PCRS; while (1) { comma = rindex(arg, ','); if (comma == NULL) { if (!isdigit(*arg)) break; comma = arg; tmp_int = atoi(comma); if (tmp_int >= 0 && tmp_int < TCSD_MAX_PCRS) conf->kernel_pcrs |= (1 << tmp_int); else LogError("Config option \"kernel_pcrs\" is out of range. " "%s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); break; } *comma++ = '\0'; tmp_int = atoi(comma); if (tmp_int >= 0 && tmp_int < TCSD_MAX_PCRS) conf->kernel_pcrs |= (1 << tmp_int); else LogError("Config option \"kernel_pcrs\" is out of range. " "%s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); } break; case opt_system_ps_file: if (*arg != '/') { LogError("Config option \"system_ps_dir\" must be an absolute path name. " "%s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { char *dir_ptr; int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"system_ps_file\" is invalid." " %s:%d: \"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"system_ps_file\" is invalid. %s:%d:" " \"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->system_ps_file) free(conf->system_ps_file); if (conf->system_ps_dir) free(conf->system_ps_dir); /* break out the system ps directory from the file path */ dir_ptr = rindex(tmp_ptr, '/'); *dir_ptr = '\0'; if (strlen(tmp_ptr) == 0) conf->system_ps_dir = strdup("/"); else conf->system_ps_dir = strdup(tmp_ptr); if (conf->system_ps_dir == NULL) { LogError("malloc failed."); free(tmp_ptr); return TCSERR(TSS_E_OUTOFMEMORY); } *dir_ptr = '/'; conf->system_ps_file = tmp_ptr; conf->unset &= ~TCSD_OPTION_SYSTEM_PSFILE; } break; case opt_kernel_log: if (*arg != '/') { LogError("Config option \"kernel_log\" must be an absolute path name." " %s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"kernel_log\" is invalid. %s:%d: \"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"kernel_log\" is invalid. %s:%d: \"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->kernel_log_file) free(conf->kernel_log_file); conf->kernel_log_file = tmp_ptr; conf->unset &= ~TCSD_OPTION_KERNEL_LOGFILE; } break; case opt_firmware_log: if (*arg != '/') { LogError("Config option \"firmware_log\" must be an absolute path name." " %s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"firmware_log\" is invalid. %s:%d: \"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"firmware_log\" is invalid. %s:%d: \"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->firmware_log_file) free(conf->firmware_log_file); conf->firmware_log_file = tmp_ptr; conf->unset &= ~TCSD_OPTION_FIRMWARE_LOGFILE; } break; case opt_platform_cred: if (*arg != '/') { LogError("Config option \"platform_cred\" must be an absolute path name. " "%s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"platform_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"platform_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->platform_cred) free(conf->platform_cred); conf->platform_cred = tmp_ptr; conf->unset &= ~TCSD_OPTION_PLATFORM_CRED; } break; case opt_conformance_cred: if (*arg != '/') { LogError("Config option \"conformance_cred\" must be an absolute path name." " %s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"conformance_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"conformance_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->conformance_cred) free(conf->conformance_cred); conf->conformance_cred = tmp_ptr; conf->unset &= ~TCSD_OPTION_CONFORMANCE_CRED; } break; case opt_endorsement_cred: if (*arg != '/') { LogError("Config option \"endorsement_cred\" must be an absolute path name." " %s:%d: \"%s\"", tcsd_config_file, line_num, arg); } else { int rc; if ((rc = get_file_path(arg, &tmp_ptr)) < 0) { LogError("Config option \"endorsement_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (rc > 0) { LogError("Config option \"endorsement_cred\" is invalid. %s:%d: " "\"%s\"", tcsd_config_file, line_num, tmp_ptr); return TCSERR(TSS_E_INTERNAL_ERROR); } if (tmp_ptr == NULL) return TCSERR(TSS_E_OUTOFMEMORY); if (conf->endorsement_cred) free(conf->endorsement_cred); conf->endorsement_cred = tmp_ptr; conf->unset &= ~TCSD_OPTION_ENDORSEMENT_CRED; } break; case opt_remote_ops: conf->unset &= ~TCSD_OPTION_REMOTE_OPS; comma = rindex(arg, '\n'); *comma = '\0'; while (1) { comma = rindex(arg, ','); if (comma == NULL) { comma = arg; if (comma != NULL) { if (tcsd_set_remote_op(conf, comma)) { LogError("Config option \"remote_ops\" is invalid. " "%s:%d: \"%s\"", tcsd_config_file, line_num, comma); } } break; } *comma++ = '\0'; if (tcsd_set_remote_op(conf, comma)) { LogError("Config option \"remote_ops\" is invalid. " "%s:%d: \"%s\"", tcsd_config_file, line_num, comma); } } break; case opt_exclusive_transport: tmp_int = atoi(arg); if (tmp_int < 0 || tmp_int > 1) { LogError("Config option \"enforce_exclusive_transport\" out of range." " %s:%d: \"%d\"", tcsd_config_file, line_num, tmp_int); return TCSERR(TSS_E_INTERNAL_ERROR); } else { conf->exclusive_transport = tmp_int; conf->unset &= ~TCSD_OPTION_EXCLUSIVE_TRANSPORT; } break; case opt_host_platform_class: /* append the host class on the list */ conf->unset &= ~TCSD_OPTION_HOST_PLATFORM_CLASS; comma = rindex(arg,'\n'); *comma = '\0'; comma = rindex(arg,','); /* At least one comma: error - more than one host class defined */ if (comma != NULL) { LogError("Config option \"host_platform_class\" error: more than one " "defined. %s:%d: \"%s\"", tcsd_config_file, line_num, comma); return TCSERR(TSS_E_INTERNAL_ERROR); } else { comma = arg; /* Add the platform class on the list */ if ((result = platform_class_list_append(conf, comma, TRUE))){ LogError("Config option \"host_platform_class\" invalid. " "%s:%d: \"%s\"", tcsd_config_file, line_num, comma); return result; } } break; case opt_all_platform_classes: /* append each of the comma separated values on the list */ comma = rindex(arg, '\n'); *comma = '\0'; while (1) { comma = rindex(arg, ','); if (comma == NULL) { comma = arg; if (comma != NULL) { /* Add the platform class on the list */ if ((result = platform_class_list_append(conf, comma, FALSE))) { LogError("Config option \"all_platform_class\" " "invalid. %s:%d: \"%s\"", tcsd_config_file, line_num, comma); return result; } } break; } *comma++ = '\0'; /* Add the platform class on the list */ if ((result = platform_class_list_append(conf, comma, FALSE))) { LogError("Config option \"all_platform_class\" invalid. " "%s:%d: \"%s\"", tcsd_config_file, line_num, comma); return result; } } break; default: /* bail out on any unknown option */ LogError("Unknown config option %s:%d \"%s\"!", tcsd_config_file, line_num, arg); return TCSERR(TSS_E_INTERNAL_ERROR); } return TSS_SUCCESS; } TSS_RESULT read_conf_file(FILE *f, struct tcsd_config *conf) { int line_num = 0; char buf[1024]; while (fgets(buf, 1024, f)) { line_num++; if (read_conf_line(buf, line_num, conf)) return TCSERR(TSS_E_INTERNAL_ERROR); } return TSS_SUCCESS; } void free_platform_lists(struct platform_class *list) { struct platform_class *tmp; while (list != NULL){ if (list->classURISize > 0) free(list->classURI); tmp = list->next; free(list); list = tmp; } } void conf_file_final(struct tcsd_config *conf) { free(conf->system_ps_file); free(conf->system_ps_dir); free(conf->kernel_log_file); free(conf->firmware_log_file); free(conf->platform_cred); free(conf->conformance_cred); free(conf->endorsement_cred); free_platform_lists(conf->host_platform_class); free_platform_lists(conf->all_platform_classes); } #ifdef SOLARIS static int get_smf_prop(const char *var, boolean_t def_val) { scf_simple_prop_t *prop; uint8_t *val; boolean_t res = def_val; prop = scf_simple_prop_get(NULL, "svc:/application/security/tcsd:default", "config", var); if (prop) { if ((val = scf_simple_prop_next_boolean(prop)) != NULL) res = (*val == 0) ? B_FALSE : B_TRUE; scf_simple_prop_free(prop); } if (prop == NULL || val == NULL) { syslog(LOG_ALERT, "no value for config/%s (%s). " "Using default \"%s\"", var, scf_strerror(scf_error()), def_val ? "true" : "false"); } return (res); } #endif TSS_RESULT conf_file_init(struct tcsd_config *conf) { FILE *f = NULL; struct stat stat_buf; #ifndef SOLARIS struct group *grp; struct passwd *pw; mode_t mode = (S_IRUSR|S_IWUSR); #endif /* SOLARIS */ TSS_RESULT result; init_tcsd_config(conf); #ifdef SOLARIS /* * Solaris runs as Rajiv Andrade pw_uid != stat_buf.st_uid || grp->gr_gid != stat_buf.st_gid) { LogError("TCSD config file (%s) must be user/group %s/%s", tcsd_config_file, TSS_USER_NAME, TSS_GROUP_NAME); return TCSERR(TSS_E_INTERNAL_ERROR); } /* make sure only the tss user can manipulate the config file */ if (((stat_buf.st_mode & 0777) ^ mode) != 0) { LogError("TCSD config file (%s) must be mode 0600", tcsd_config_file); return TCSERR(TSS_E_INTERNAL_ERROR); } #endif /* SOLARIS */ if ((f = fopen(tcsd_config_file, "r")) == NULL) { LogError("fopen(%s): %s", tcsd_config_file, strerror(errno)); return TCSERR(TSS_E_INTERNAL_ERROR); } result = read_conf_file(f, conf); fclose(f); /* fill out any uninitialized options */ config_set_defaults(conf); #ifdef SOLARIS /* * The SMF value for "local_only" overrides the config file and * disables all remote operations. */ if (get_smf_prop("local_only", B_TRUE)) { (void) memset(conf->remote_ops, 0, sizeof(conf->remote_ops)); conf->unset |= TCSD_OPTION_REMOTE_OPS; } #endif return result; } TSS_RESULT ps_dirs_init() { struct stat stat_buf; mode_t mode = S_IRWXU; /* 0700 */ /* query the key storage directory to make sure it exists and is of the right mode */ if (stat(tcsd_options.system_ps_dir, &stat_buf) == -1) { if (errno == ENOENT) { /* The dir DNE, create it with mode drwxrwxrwt */ if (mkdir(tcsd_options.system_ps_dir, mode) == -1) { LogError("mkdir(%s) failed: %s. If you'd like to use %s to " "store your system persistent data, please" " create it. Otherwise, change the location" " in your tcsd config file.", tcsd_options.system_ps_dir, strerror(errno), tcsd_options.system_ps_dir); return TCSERR(TSS_E_INTERNAL_ERROR); } } else { LogError("stat failed: %s", strerror(errno)); return TCSERR(TSS_E_INTERNAL_ERROR); } } /* stat should not fail now */ if (stat(tcsd_options.system_ps_dir, &stat_buf) == -1) { LogError("stat %s failed: %s", tcsd_options.system_ps_dir, strerror(errno)); return TCSERR(TSS_E_INTERNAL_ERROR); } /* tcsd_options.system_ps_dir should be a directory with mode equal to mode */ if (!S_ISDIR(stat_buf.st_mode)) { LogError("PS dir %s is not a directory! Exiting.", tcsd_options.system_ps_dir); return TCSERR(TSS_E_INTERNAL_ERROR); } else if (((stat_buf.st_mode & 0777) ^ mode) != 0) { /* This path is likely to be hit since open &'s mode with ~umask */ LogInfo("resetting mode of %s from %o to: %o", tcsd_options.system_ps_dir, (unsigned int) stat_buf.st_mode, (unsigned int) mode); if (chmod(tcsd_options.system_ps_dir, mode) == -1) { LogError("chmod(%s) failed: %s", tcsd_options.system_ps_dir, strerror(errno)); return TCSERR(TSS_E_INTERNAL_ERROR); } } return TSS_SUCCESS; }