/* * Obtain Kerberos tickets for the principal specified in config/principal * using the keytab specified in config/keytab, both of which are presumed to * be in tests in either the build or the source tree. Also sets KRB5_KTNAME * and KRB5CCNAME. * * Returns the contents of config/principal in newly allocated memory or NULL * if Kerberos tests are apparently not configured. If Kerberos tests are * configured but something else fails, calls bail. */ struct kerberos_config * kerberos_setup(enum kerberos_needs needs) { char *path; char buffer[BUFSIZ]; FILE *file = NULL; /* If we were called before, clean up after the previous run. */ if (config != NULL) kerberos_cleanup(); config = bcalloc(1, sizeof(struct kerberos_config)); /* * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME * environment variables and obtain initial tickets. */ config->keytab = test_file_path("config/keytab"); if (config->keytab == NULL) { if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH) skip_all("Kerberos tests not configured"); } else { tmpdir_ticket = test_tmpdir(); basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket); basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket); basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab); putenv(krb5ccname); putenv(krb5_ktname); kerberos_kinit(); } /* * If we have a config/password file, read it and fill out the relevant * members of our config struct. */ path = test_file_path("config/password"); if (path != NULL) file = fopen(path, "r"); if (file == NULL) { if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH) skip_all("Kerberos tests not configured"); } else { if (fgets(buffer, sizeof(buffer), file) == NULL) bail("cannot read %s", path); if (buffer[strlen(buffer) - 1] != '\n') bail("no newline in %s", path); buffer[strlen(buffer) - 1] = '\0'; config->userprinc = bstrdup(buffer); if (fgets(buffer, sizeof(buffer), file) == NULL) bail("cannot read password from %s", path); fclose(file); if (buffer[strlen(buffer) - 1] != '\n') bail("password too long in %s", path); buffer[strlen(buffer) - 1] = '\0'; config->password = bstrdup(buffer); /* * Strip the realm from the principal and set realm and username. * This is not strictly correct; it doesn't cope with escaped @-signs * or enterprise names. */ config->username = bstrdup(config->userprinc); config->realm = strchr(config->username, '@'); if (config->realm == NULL) bail("test principal has no realm"); *config->realm = '\0'; config->realm++; } test_file_path_free(path); /* * If we have PKINIT configuration, read it and fill out the relevant * members of our config struct. */ path = test_file_path("config/pkinit-principal"); if (path != NULL) file = fopen(path, "r"); if (file != NULL) { if (fgets(buffer, sizeof(buffer), file) == NULL) bail("cannot read %s", path); if (buffer[strlen(buffer) - 1] != '\n') bail("no newline in %s", path); buffer[strlen(buffer) - 1] = '\0'; fclose(file); test_file_path_free(path); path = test_file_path("config/pkinit-cert"); if (path != NULL) { config->pkinit_principal = bstrdup(buffer); config->pkinit_cert = bstrdup(path); } } test_file_path_free(path); if (config->pkinit_cert == NULL && (needs & TAP_KRB_NEEDS_PKINIT) != 0) skip_all("PKINIT tests not configured"); /* * Register the cleanup function so that the caller doesn't have to do * explicit cleanup. */ test_cleanup_register(kerberos_cleanup_handler); /* Return the configuration. */ return config; }
/* * Start a process and return its status information. The status information * is also stored in the global processes linked list so that it can be * stopped automatically on program exit. * * The boolean argument says whether to start the process under fakeroot. If * true, PATH_FAKEROOT must be defined, generally by Autoconf. If it's not * found, call skip_all. * * This is a helper function for process_start and process_start_fakeroot. */ static struct process * process_start_internal(const char *const argv[], const char *pidfile, bool fakeroot) { size_t i; int log_fd; const char *name; struct timeval tv; struct process *process; const char **fakeroot_argv = NULL; const char *path_fakeroot = PATH_FAKEROOT; /* Check prerequisites. */ if (fakeroot && path_fakeroot[0] == '\0') skip_all("fakeroot not found"); /* Create the process struct and log file. */ process = bcalloc(1, sizeof(struct process)); process->pidfile = bstrdup(pidfile); process->tmpdir = test_tmpdir(); name = strrchr(argv[0], '/'); if (name != NULL) name++; else name = argv[0]; basprintf(&process->logfile, "%s/%s.log.XXXXXX", process->tmpdir, name); log_fd = mkstemp(process->logfile); if (log_fd < 0) sysbail("cannot create log file for %s", argv[0]); /* If using fakeroot, rewrite argv accordingly. */ if (fakeroot) { for (i = 0; argv[i] != NULL; i++) ; fakeroot_argv = bcalloc(2 + i + 1, sizeof(const char *)); fakeroot_argv[0] = path_fakeroot; fakeroot_argv[1] = "--"; for (i = 0; argv[i] != NULL; i++) fakeroot_argv[i + 2] = argv[i]; fakeroot_argv[i + 2] = NULL; argv = fakeroot_argv; } /* * Fork off the child process, redirect its standard output and standard * error to the log file, and then exec the program. */ process->pid = fork(); if (process->pid < 0) sysbail("fork failed"); else if (process->pid == 0) { if (dup2(log_fd, STDOUT_FILENO) < 0) sysbail("cannot redirect standard output"); if (dup2(log_fd, STDERR_FILENO) < 0) sysbail("cannot redirect standard error"); close(log_fd); if (execv(argv[0], (char *const *) argv) < 0) sysbail("exec of %s failed", argv[0]); } close(log_fd); free(fakeroot_argv); /* * In the parent. Wait for the child to start by watching for the PID * file to appear in 100ms intervals. */ for (i = 0; i < PROCESS_WAIT * 10 && access(pidfile, F_OK) != 0; i++) { tv.tv_sec = 0; tv.tv_usec = 100000; select(0, NULL, NULL, NULL, &tv); } /* * If the PID file still hasn't appeared after ten seconds, attempt to * kill the process and then bail. */ if (access(pidfile, F_OK) != 0) { kill(process->pid, SIGTERM); alarm(5); waitpid(process->pid, NULL, 0); alarm(0); bail("cannot start %s", argv[0]); } /* * Read the PID back from the PID file. This usually isn't necessary for * non-forking daemons, but always doing this makes this function general, * and it's required when running under fakeroot. */ if (fakeroot) process->pid = read_pidfile(pidfile); process->is_child = !fakeroot; /* Register the log file as a source of diag messages. */ diag_file_add(process->logfile); /* * Add the process to our global list and set our cleanup handler if this * is the first process we started. */ if (processes == NULL) test_cleanup_register(process_stop_all); process->next = processes; processes = process; /* All done. */ return process; }