// Resolution order for ambiguous imports: // (1) filename as given // (2) underscore + given // (3) underscore + given + extension // (4) given + extension std::vector<Sass_Queued> resolve_includes(const std::string& root, const std::string& file) { std::string filename = join_paths(root, file); // supported extensions const std::vector<std::string> exts = { ".scss", ".sass", ".css" }; // split the filename std::string base(dir_name(file)); std::string name(base_name(file)); std::vector<Sass_Queued> includes; // create full path (maybe relative) std::string rel_path(join_paths(base, name)); std::string abs_path(join_paths(root, rel_path)); if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); // next test variation with underscore rel_path = join_paths(base, "_" + name); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); // next test exts plus underscore for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + ext); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); } // nothing found return includes; }
static Image *add_image( ImageManager *manager, char *name, MemoryManager memory) { char * const full_name = join_paths(manager->directory, name, memory); Image image; if (!full_name) { return NULL; } image.name = name; image.surface = load_texture(full_name, manager->format); Deallocator_free(memory.deallocator, full_name); if (!image.surface) { return NULL; } if (!Vector_push_back(&manager->images, &image, sizeof(image), memory.allocator)) { SDL_FreeSurface(image.surface); return NULL; } return ((Image *)Vector_end(&manager->images)) - 1; }
/* Build the upload_directories array by converting upload_subdirectories to * absolute path names. The generated paths don't have a trailing slash. */ static int initialize_upload_directories() { upload_directories = calloc(num_upload_subdirectories, sizeof(upload_directories[0])); if (upload_directories == NULL) { syslog(LOG_ERR, "initialize_upload_directories:calloc: %s", strerror(errno)); return -1; } int idx; for (idx = 0; idx < num_upload_subdirectories; ++idx) { char absolute_path[PATH_MAX + 1]; if (join_paths(UPLOADS_ROOT, upload_subdirectories[idx], absolute_path)) { return -1; } upload_directories[idx] = strdup(absolute_path); if (upload_directories[idx] == NULL) { syslog(LOG_ERR, "upload_directories:strdup(\"%s\"): %s", absolute_path, strerror(errno)); return -1; } } return 0; }
/* Transform all path components of the given entry. This includes hardlink and * symlink paths as well as the destination path. * * Returns 0 on success, 1 where the file does not need to be extracted and <0 * on error. */ static int transform_all_paths(struct archive_entry *entry, const char *dest) { char *path; const char *filename; int r; r = transform_dest_path(entry, dest); if (r) return r; /* Next transform hardlink and symlink destinations. */ filename = archive_entry_hardlink(entry); if (filename) { /* Apply the same transform to the hardlink path as was applied * to the destination path. */ path = join_paths(dest, filename); if (!path) { opkg_msg(ERROR, "Not extracting '%s': Hardlink to nowhere.\n", archive_entry_pathname(entry)); return 1; } archive_entry_set_hardlink(entry, path); free(path); } /* Currently no transform to perform for symlinks. */ return 0; }
void search_files(DIR *root, const char *rel_path, char *current_path, int level) { int index = 0; struct dirent *current; char full_path_name[MAXPATHLEN]; chdir(rel_path); strcpy(full_path_name, current_path); while ((current = readdir(root)) != NULL) { join_paths(full_path_name, full_path_name, current->d_name); if (current->d_type == DT_DIR && CHECK_IGNORED(current->d_name)) { search_files(opendir(current->d_name), current->d_name, full_path_name, ++level); chdir(".."); } else if (CHECK_IGNORED(current->d_name)) { if ((index = find_file(full_path_name)) >= 0) printf("%-20s %s\n", obvious_projects[index], full_path_name); } remove_component(full_path_name, current->d_name); } closedir(root); }
int chdir(const char *src) { int rv; struct file *file; char cwd_buf[CURRENTDIR_MAX]; if (this_fs->fs_ops->chdir) return this_fs->fs_ops->chdir(this_fs, src); /* Otherwise it is a "conventional filesystem" */ rv = searchdir(src); if (rv < 0) return rv; file = handle_to_file(rv); if (file->inode->mode != DT_DIR) { _close_file(file); return -1; } put_inode(this_fs->cwd); this_fs->cwd = get_inode(file->inode); _close_file(file); /* Save the current working directory */ realpath(cwd_buf, src, CURRENTDIR_MAX); /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */ join_paths(this_fs->cwd_name, CURRENTDIR_MAX, cwd_buf, "/"); return 0; }
void test_join_paths(void** state) { const char* dir = "/this/is/the/whole/"; const char* file = "path"; char path[FILENAME_MAX]; join_paths(dir, file, path); assert_true(!strcmp(path, "/this/is/the/whole/path")); }
static void run_boot_hooks(struct boot_task *task) { struct dirent **hooks; int i, n; n = scandir(boot_hook_dir, &hooks, hook_filter, hook_cmp); if (n < 1) return; update_status(task->status_fn, task->status_arg, BOOT_STATUS_INFO, _("running boot hooks")); boot_hook_setenv(task); for (i = 0; i < n; i++) { const char *argv[2] = { NULL, NULL }; struct process *process; char *path; int rc; path = join_paths(task, boot_hook_dir, hooks[i]->d_name); if (access(path, X_OK)) { talloc_free(path); continue; } process = process_create(task); argv[0] = path; process->path = path; process->argv = argv; process->keep_stdout = true; pb_log("running boot hook %s\n", hooks[i]->d_name); rc = process_run_sync(process); if (rc) { pb_log("boot hook exec failed!\n"); } else if (WIFEXITED(process->exit_status) && WEXITSTATUS(process->exit_status) == BOOT_HOOK_EXIT_UPDATE) { /* if the hook returned with BOOT_HOOK_EXIT_UPDATE, * then we process stdout to look for updated params */ boot_hook_update(task, hooks[i]->d_name, process->stdout_buf); boot_hook_setenv(task); } process_release(process); talloc_free(path); } free(hooks); }
void test_join_paths(void) { char result_path1[100]; char result_path2[100]; char result_path3[100]; const char *path1 = "."; const char *path2 = "src"; const char *path3 = "module"; join_paths(result_path1, path1, path2); join_paths(result_path2, result_path1, path3); join_paths(result_path3, path2, path3); assert(0 == strcmp(result_path1, "./src")); assert(0 == strcmp(result_path2, "./src/module")); assert(0 == strcmp(result_path3, "src/module")); }
size_t realpath(char *dst, const char *src, size_t bufsize) { if (this_fs->fs_ops->realpath) { return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize); } else { /* Filesystems with "common" pathname resolution */ return join_paths(dst, bufsize, src[0] == '/' ? "" : this_fs->cwd_name, src); } }
int linux_test_truncate(arg_temp *args_struct) { char path[200]; char message[200]; int output=0; join_paths(linux_struct.root_path,args_struct->string1, path ); sprintf(message,"file path: %s\n",path); print_message(3,message); output=truncate(path,args_struct->int1); return output; }
/* Transform the destination path of the given entry. * * Returns 0 on success, 1 where the file does not need to be extracted and <0 * on error. */ static int transform_dest_path(struct archive_entry *entry, const char *dest) { char *path; const char *filename; filename = archive_entry_pathname(entry); path = join_paths(dest, filename); if (!path) return 1; archive_entry_set_pathname(entry, path); free(path); return 0; }
Common::InSaveFile *DefaultSaveFileManager::openForLoading(const char *filename) { char buf[256]; join_paths(filename, getSavePath(), buf, sizeof(buf)); #ifdef USE_ZLIB GzipSaveFile *sf = new GzipSaveFile(buf, false); #else StdioSaveFile *sf = new StdioSaveFile(buf, false); #endif if (!sf->isOpen()) { delete sf; sf = 0; } return sf; }
/* Build the upload_subdirectories array * by scanning UPLOADS_ROOT for subdirectories. */ static int initialize_upload_subdirectories() { DIR* handle = opendir(UPLOADS_ROOT); if (handle == NULL) { syslog(LOG_ERR, "initialize_upload_subdirectories:opendir(\"%s\"): %s", UPLOADS_ROOT, strerror(errno)); return -1; } struct dirent* entry; while ((entry = readdir(handle))) { if (entry->d_name[0] == '.') { /* Skip hidden, ".", and ".." */ continue; } char absolute_filename[PATH_MAX + 1]; if (join_paths(UPLOADS_ROOT, entry->d_name, absolute_filename)) { return -1; } struct stat dir_info; if (stat(absolute_filename, &dir_info)) { syslog(LOG_ERR, "initialize_upload_subdirectories:stat(\"%s\"): %s", absolute_filename, strerror(errno)); return -1; } if (S_ISDIR(dir_info.st_mode)) { ++num_upload_subdirectories; upload_subdirectories = realloc( upload_subdirectories, num_upload_subdirectories * sizeof(upload_subdirectories[0])); if (upload_subdirectories == NULL) { syslog(LOG_ERR, "initialize_upload_subdirectories:realloc: %s", strerror(errno)); return -1; } upload_subdirectories[num_upload_subdirectories - 1] = strdup(entry->d_name); } } (void)closedir(handle); return 0; }
static bool open_wd(gitdb & db, git_wd & wd, string_view path) { std::string apath = absolute_path(clean_path(path)); path = apath; while (!path.empty()) { std::string nonbare_path = join_paths(path, ".git"); if (file::is_directory(nonbare_path)) { db.open(nonbare_path); wd.open(db, path); return true; } path = path_head(path); } return false; }
void hardcore_mapgen(int (*map)[mapX][mapY], int *endX, int *endY) { int x, y; // reset map for (y=0; y<mapY; y++) for (x=0; x<mapX; x++) (*map)[x][y] = 0; // generate path starting from every empty block for (y = 0; y<mapY/3; y++) for (x = 0; x<mapX/3; x++) if (is_taken(x, y, map) == 0) // check if block is empty mapgen(map,x,y,map, endX, endY); // generate path // generate final route int final_map[mapX][mapY] = {0}; mapgen(map, 0, 0, &final_map, endX, endY); // remove walls between paths to merge them join_paths(map); }
/** * Copy in @guest_path the canonicalization (see `man 3 realpath`) of * @user_path regarding to @tracee->root. The path to canonicalize * could be either absolute or relative to @guest_path. When the last * component of @user_path is a link, it is dereferenced only if * @deref_final is true -- it is useful for syscalls like lstat(2). * The parameter @recursion_level should be set to 0 unless you know * what you are doing. This function returns -errno if an error * occured, otherwise it returns 0. */ int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, char guest_path[PATH_MAX], unsigned int recursion_level) { char scratch_path[PATH_MAX]; Finality finality; const char *cursor; int status; /* Avoid infinite loop on circular links. */ if (recursion_level > MAXSYMLINKS) return -ELOOP; /* Sanity checks. */ assert(user_path != NULL); assert(guest_path != NULL); assert(user_path != guest_path); if (strnlen(guest_path, PATH_MAX) >= PATH_MAX) return -ENAMETOOLONG; if (user_path[0] != '/') { /* Ensure 'guest_path' contains an absolute base of * the relative `user_path`. */ if (guest_path[0] != '/') return -EINVAL; } else strcpy(guest_path, "/"); /* Canonicalize recursely 'user_path' into 'guest_path'. */ cursor = user_path; finality = NOT_FINAL; while (!IS_FINAL(finality)) { Comparison comparison; char component[NAME_MAX]; char host_path[PATH_MAX]; finality = next_component(component, &cursor); status = (int) finality; if (status < 0) return status; if (strcmp(component, ".") == 0) { if (IS_FINAL(finality)) finality = FINAL_DOT; continue; } if (strcmp(component, "..") == 0) { pop_component(guest_path); if (IS_FINAL(finality)) finality = FINAL_SLASH; continue; } status = join_paths(2, scratch_path, guest_path, component); if (status < 0) return status; /* Resolve bindings and check that a non-final * component exists and either is a directory or is a * symlink. For this latter case, we check that the * symlink points to a directory once it is * canonicalized, at the end of this loop. */ status = substitute_binding_stat(tracee, finality, recursion_level, scratch_path, host_path); if (status < 0) return status; /* Nothing special to do if it's not a link or if we * explicitly ask to not dereference 'user_path', as * required by syscalls like lstat(2). Obviously, this * later condition does not apply to intermediate path * components. Errors are explicitly ignored since * they should be handled by the caller. */ if (status <= 0 || (finality == FINAL_NORMAL && !deref_final)) { strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, component); if (status < 0) return status; continue; } /* It's a link, so we have to dereference *and* * canonicalize to ensure we are not going outside the * new root. */ comparison = compare_paths("/proc", guest_path); switch (comparison) { case PATHS_ARE_EQUAL: case PATH1_IS_PREFIX: /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ status = readlink_proc(tracee, scratch_path, guest_path, component, comparison); switch (status) { case CANONICALIZE: /* The symlink is already dereferenced, * now canonicalize it. */ goto canon; case DONT_CANONICALIZE: /* If and only very final, this symlink * shouldn't be dereferenced nor canonicalized. */ if (finality == FINAL_NORMAL) { strcpy(guest_path, scratch_path); return 0; } break; default: if (status < 0) return status; } default: break; } status = readlink(host_path, scratch_path, sizeof(scratch_path)); if (status < 0) return status; else if (status == sizeof(scratch_path)) return -ENAMETOOLONG; scratch_path[status] = '\0'; /* Remove the leading "root" part if needed, it's * useful for "/proc/self/cwd/" for instance. */ status = detranslate_path(tracee, scratch_path, host_path); if (status < 0) return status; canon: /* Canonicalize recursively the referee in case it * is/contains a link, moreover if it is not an * absolute link then it is relative to * 'guest_path'. */ status = canonicalize(tracee, scratch_path, true, guest_path, recursion_level + 1); if (status < 0) return status; /* Check that a non-final canonicalized/dereferenced * symlink exists and is a directory. */ status = substitute_binding_stat(tracee, finality, recursion_level, guest_path, host_path); if (status < 0) return status; /* Here, 'guest_path' shouldn't be a symlink anymore, * unless it is a named file descriptor. */ assert(status != 1 || sscanf(guest_path, "/proc/%*d/fd/%d", &status) == 1); } /* At the exit stage of the first level of recursion, * `guest_path` is fully canonicalized but a terminating '/' * or a terminating '.' may be required to keep the initial * semantic of `user_path`. */ if (recursion_level == 0) { switch (finality) { case FINAL_NORMAL: break; case FINAL_SLASH: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, ""); if (status < 0) return status; break; case FINAL_DOT: strcpy(scratch_path, guest_path); status = join_paths(2, guest_path, scratch_path, "."); if (status < 0) return status; break; default: assert(0); } } return 0; }
/** * Copy in @result the equivalent of "@tracee->root + canon(@dir_fd + * @user_path)". If @user_path is not absolute then it is relative to * the directory referred by the descriptor @dir_fd (AT_FDCWD is for * the current working directory). See the documentation of * canonicalize() for the meaning of @deref_final. This function * returns -errno if an error occured, otherwise 0. */ int translate_path(Tracee *tracee, char result[PATH_MAX], int dir_fd, const char *user_path, bool deref_final) { char guest_path[PATH_MAX]; int status; /* Use "/" as the base if it is an absolute guest path. */ if (user_path[0] == '/') { strcpy(result, "/"); } /* It is relative to a directory referred by a descriptor, see * openat(2) for details. */ else if (dir_fd != AT_FDCWD) { /* /proc/@tracee->pid/fd/@dir_fd -> result. */ status = readlink_proc_pid_fd(tracee->pid, dir_fd, result); if (status < 0) return status; /* Named file descriptors may reference special * objects like pipes, sockets, inodes, ... Such * objects do not belong to the file-system. */ if (result[0] != '/') return -ENOTDIR; /* Remove the leading "root" part of the base * (required!). */ status = detranslate_path(tracee, result, NULL); if (status < 0) return status; } /* It is relative to the current working directory. */ else { status = getcwd2(tracee, result); if (status < 0) return status; } VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")", tracee != NULL ? tracee->pid : 0, result, user_path); status = notify_extensions(tracee, GUEST_PATH, (intptr_t) result, (intptr_t) user_path); if (status < 0) return status; if (status > 0) goto skip; /* So far "result" was used as a base path, it's time to join * it to the user path. */ assert(result[0] == '/'); status = join_paths(2, guest_path, result, user_path); if (status < 0) return status; strcpy(result, "/"); /* Canonicalize regarding the new root. */ status = canonicalize(tracee, guest_path, deref_final, result, 0); if (status < 0) return status; /* Final binding substitution to convert "result" into a host * path, since canonicalize() works from the guest * point-of-view. */ status = substitute_binding(tracee, GUEST, result); if (status < 0) return status; skip: VERBOSE(tracee, 2, "pid %d: -> \"%s\"", tracee != NULL ? tracee->pid : 0, result); return 0; }
int main(int argc, char** argv) { if (argc != 2) { strncpy(uploads_url, DEFAULT_UPLOADS_URL, MAX_URL_LENGTH); } else { strncpy(uploads_url, argv[1], MAX_URL_LENGTH); } openlog("bismark-data-transmit", LOG_PERROR, LOG_USER); if (read_bismark_id()) { return 1; } if (initialize_upload_subdirectories() || initialize_upload_directories()) { return 1; } failure_counters = calloc(num_upload_subdirectories, sizeof(failure_counters[0])); if (failure_counters == NULL) { syslog(LOG_ERR, "main:calloc: %s", strerror(errno)); return 1; } if (write_upload_failures_log()) { return 1; } if (initialize_curl()) { return 1; } /* Initialize inotify */ int inotify_handle = inotify_init(); if (inotify_handle < 0) { syslog(LOG_ERR, "main:inotify_init: %s", strerror(errno)); return 1; } int idx; watch_descriptors = calloc(num_upload_subdirectories, sizeof(watch_descriptors[0])); if (watch_descriptors == NULL) { syslog(LOG_ERR, "main:calloc: %s", strerror(errno)); return 1; } for (idx = 0; idx < num_upload_subdirectories; ++idx) { watch_descriptors[idx] = inotify_add_watch(inotify_handle, upload_directories[idx], IN_MOVED_TO); if (watch_descriptors[idx] < 0) { syslog(LOG_ERR, "main:inotify_add_watch(\"%s\"): %s", upload_directories[idx], strerror(errno)); return 1; } syslog(LOG_INFO, "Watching %s", upload_directories[idx]); } time_t current_time = time(NULL); if (current_time < 0) { syslog(LOG_ERR, "main:time: %s", strerror(errno)); return 1; } time_t last_retry_time = current_time - RETRY_INTERVAL_SECONDS; while (1) { current_time = time(NULL); if (current_time < 0) { syslog(LOG_ERR, "main:time: %s", strerror(errno)); return 1; } fd_set select_set; FD_ZERO(&select_set); FD_SET(inotify_handle, &select_set); struct timeval select_timeout; select_timeout.tv_sec = RETRY_INTERVAL_SECONDS - (current_time - last_retry_time); if (select_timeout.tv_sec < 0) { select_timeout.tv_sec = 0; } select_timeout.tv_usec = 0; int select_result = select( inotify_handle + 1, &select_set, NULL, NULL, &select_timeout); if (select_result < 0) { syslog(LOG_ERR, "main:select: %s", strerror(errno)); curl_easy_cleanup(curl_handle); return 1; } else if (select_result > 0) { if (FD_ISSET(inotify_handle, &select_set)) { char events_buffer[BUF_LEN]; int length = read(inotify_handle, events_buffer, BUF_LEN); if (length < 0) { syslog(LOG_ERR, "main:read: %s", strerror(errno)); curl_easy_cleanup(curl_handle); return 1; } int offset = 0; while (offset < length) { struct inotify_event* event \ = (struct inotify_event*)(events_buffer + offset); if (event->len && (event->mask & IN_MOVED_TO)) { int idx; for (idx = 0; idx < num_upload_subdirectories; ++idx) { if (event->wd == watch_descriptors[idx]) { char absolute_path[PATH_MAX + 1]; if (join_paths( upload_directories[idx], event->name, absolute_path)) { break; } syslog(LOG_INFO, "File move detected: %s", absolute_path); if (!curl_send(absolute_path, upload_subdirectories[idx])) { if (unlink(absolute_path)) { syslog(LOG_ERR, "main:unlink(\"%s\"): %s", absolute_path, strerror(errno)); } } break; } } } offset += sizeof(*event) + event->len; } } } else if (select_result == 0) { current_time = time(NULL); if (current_time < 0) { syslog(LOG_ERR, "main:time: %s", strerror(errno)); return 1; } retry_uploads(current_time); last_retry_time = time(NULL); if (last_retry_time < 0) { syslog(LOG_ERR, "main:time: %s", strerror(errno)); return 1; } } } return 0; }
static void retry_uploads(time_t current_time) { upload_list_t files_to_sort; upload_list_init(&files_to_sort); int new_upload_failure = 0; syslog(LOG_INFO, "Checking for uploads to retry"); int idx; for (idx = 0; idx < num_upload_subdirectories; ++idx) { DIR* handle = opendir(upload_directories[idx]); if (handle == NULL) { syslog(LOG_ERR, "retry_uploads:opendir(\"%s\"): %s", upload_directories[idx], strerror(errno)); continue; } struct dirent* entry; while ((entry = readdir(handle))) { char absolute_path[PATH_MAX + 1]; if (join_paths(upload_directories[idx], entry->d_name, absolute_path)) { continue; } struct stat file_info; if (stat(absolute_path, &file_info)) { syslog(LOG_ERR, "retry_uploads:stat(\"%s\"): %s", absolute_path, strerror(errno)); continue; } if (S_ISREG(file_info.st_mode) || S_ISLNK(file_info.st_mode)) { if (current_time - file_info.st_ctime > RETRY_INTERVAL_SECONDS) { syslog(LOG_INFO, "Retrying file: %s", absolute_path); if (curl_send(absolute_path, upload_subdirectories[idx]) == 0) { if (unlink(absolute_path)) { syslog(LOG_ERR, "retry_uploads:unlink(\"%s\"): %s", absolute_path, strerror(errno)); } else { continue; } } } upload_list_append(&files_to_sort, absolute_path, file_info.st_ctime, file_info.st_blocks, idx); } } if (closedir(handle)) { syslog(LOG_ERR, "retry_uploads:closedir: %s", strerror(errno)); } } if (files_to_sort.entries != NULL) { upload_list_sort(&files_to_sort); int total_blocks = 0; for (idx = 0; idx < files_to_sort.length; ++idx) { upload_entry_t* entry = &files_to_sort.entries[idx]; if (total_blocks + entry->size > MAX_UPLOADS_BLOCKS) { syslog(LOG_INFO, "Removing old upload: %s", files_to_sort.entries[idx].filename); if (unlink(files_to_sort.entries[idx].filename)) { syslog(LOG_ERR, "retry_uploads:unlink(\"%s\"): %s", files_to_sort.entries[idx].filename, strerror(errno)); } else { log_upload_failure(files_to_sort.entries[idx].index); new_upload_failure = 1; } } else { total_blocks += entry->size; } } } upload_list_destroy(&files_to_sort); if (new_upload_failure) { (void)write_upload_failures_log(); } }
/* A generic subtree traversing. Returns zero on success, otherwise non-zero is * returned. */ static int traverse_subtree(const char path[], subtree_visitor visitor, void *param) { DIR *dir; struct dirent *d; int result; VisitResult enter_result; dir = os_opendir(path); if(dir == NULL) { return 1; } enter_result = visitor(path, VA_DIR_ENTER, param); if(enter_result == VR_ERROR) { (void)os_closedir(dir); return 1; } result = 0; while((d = os_readdir(dir)) != NULL) { char *full_path; if(is_builtin_dir(d->d_name)) { continue; } full_path = join_paths(path, d->d_name); if(entry_is_link(full_path, d)) { /* Treat symbolic links to directories as files as well. */ result = visitor(full_path, VA_FILE, param); } else if(entry_is_dir(full_path, d)) { result = traverse_subtree(full_path, visitor, param); } else { result = visitor(full_path, VA_FILE, param); } free(full_path); if(result != 0) { break; } } (void)os_closedir(dir); if(result == 0 && enter_result != VR_SKIP_DIR_LEAVE && enter_result != VR_CANCELLED) { result = visitor(path, VA_DIR_LEAVE, param); } return result; }
int main(int argc, char* argv[]) { char* collection = NULL; char* platform = NULL; char* prefix = NULL; char* generation_string = strdup(DEFAULT_GENERATION); const char* argv0 = argv[0]; for ( int i = 0; i < argc; i++ ) { const char* arg = argv[i]; if ( arg[0] != '-' || !arg[1] ) continue; argv[i] = NULL; if ( !strcmp(arg, "--") ) break; if ( arg[1] != '-' ) { char c; while ( (c = *++arg) ) switch ( c ) { default: fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c); help(stderr, argv0); exit(1); } } else if ( !strcmp(arg, "--help") ) help(stdout, argv0), exit(0); else if ( !strcmp(arg, "--version") ) version(stdout, argv0), exit(0); else if ( GET_OPTION_VARIABLE("--collection", &collection) ) { } else if ( GET_OPTION_VARIABLE("--platform", &platform) ) { } else if ( GET_OPTION_VARIABLE("--prefix", &prefix) ) { } else if ( GET_OPTION_VARIABLE("--generation", &generation_string) ) { } else if ( !strcmp(arg, "--disable-multiarch") ) { // TODO: After releasing Sortix 1.1, delete this compatibility that // lets Sortix 1.0 build. This option used to disable // compatibility with Sortix 0.9. } else { fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); help(stderr, argv0); exit(1); } } if ( argc == 1 ) { help(stdout, argv0); exit(0); } compact_arguments(&argc, &argv); ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv); VerifyCommandLineCollection(&collection); int generation = atoi(generation_string); free(generation_string); if ( !prefix ) prefix = strdup(collection); if ( argc == 1 ) { error(0, 0, "error: no command specified."); exit(1); } const char* cmd = argv[1]; if ( !strcmp(cmd, "create") ) { if ( !platform && !(platform = GetBuildTriplet()) ) error(1, errno, "unable to determine platform, use --platform"); char* tix_path = join_paths(collection, "tix"); if ( mkdir_p(tix_path, 0755) != 0 ) error(1, errno, "mkdir: `%s'", tix_path); char* tixdb_path = strdup(tix_path); char* tixinfo_path = join_paths(tixdb_path, "tixinfo"); if ( mkdir_p(tixinfo_path, 0755) != 0 ) error(1, errno, "mkdir: `%s'", tixinfo_path); free(tixinfo_path); char* manifest_path = join_paths(tixdb_path, "manifest"); if ( mkdir_p(manifest_path, 0755) != 0 ) error(1, errno, "mkdir: `%s'", manifest_path); free(manifest_path); char* collection_conf_path = join_paths(tixdb_path, "collection.conf"); FILE* conf_fp = fopen(collection_conf_path, "wx"); if ( !conf_fp && errno == EEXIST ) error(1, 0, "error: `%s' already exists, a tix collection is " "already installed at `%s'.", collection_conf_path, collection); fprintf(conf_fp, "tix.version=1\n"); fprintf(conf_fp, "tix.class=collection\n"); fprintf(conf_fp, "collection.generation=%i\n", generation); fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" : prefix); fprintf(conf_fp, "collection.platform=%s\n", platform); fclose(conf_fp); free(collection_conf_path); const char* repo_list_path = join_paths(tixdb_path, "repository.list"); FILE* repo_list_fp = fopen(repo_list_path, "w"); if ( !repo_list_fp ) error(1, errno, "`%s'", repo_list_path); fclose(repo_list_fp); const char* inst_list_path = join_paths(tixdb_path, "installed.list"); FILE* inst_list_fp = fopen(inst_list_path, "w"); if ( !inst_list_fp ) error(1, errno, "`%s'", inst_list_path); fclose(inst_list_fp); return 0; } else { fprintf(stderr, "%s: unknown command: `%s'\n", argv0, cmd); exit(1); } return 0; }
void execdiff(int tree_a, const char* tree_a_path, int tree_b, const char* tree_b_path, const char* relpath) { DIR* dir_b = fdopendupdir(tree_b); if ( !dir_b ) error(1, errno, "fdopendupdir(`%s`)", tree_b_path); struct dirent* entry; while ( (entry = readdir(dir_b)) ) { if ( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") ) continue; char* subrelpath = join_paths(relpath, entry->d_name); int diropenflags = O_RDONLY | O_DIRECTORY | O_NOFOLLOW; int subtree_b = openat(tree_b, entry->d_name, diropenflags); if ( 0 <= subtree_b ) { char* subtree_b_path = join_paths(tree_b_path, entry->d_name); int subtree_a = openat(tree_a, entry->d_name, diropenflags); if ( subtree_a < 0 ) { if ( !(errno == ENOTDIR || errno == ELOOP || errno == ENOENT) ) error(1, errno, "`%s/%s`", tree_b_path, entry->d_name); execdiff(-1, NULL, subtree_b, subtree_b_path, subrelpath); free(subtree_b_path); close(subtree_b); free(subrelpath); continue; } char* subtree_a_path = join_paths(tree_a_path, entry->d_name); execdiff(subtree_a, subtree_a_path, subtree_b, subtree_b_path, subrelpath); free(subtree_a_path); close(subtree_a); free(subtree_b_path); close(subtree_b); free(subrelpath); continue; } else if ( !(errno == ENOTDIR || errno == ELOOP) ) error(1, errno, "`%s/%s`", tree_b_path, entry->d_name); int a_executableness = ftestexecutableat(tree_a, entry->d_name); int b_executableness = ftestexecutableat(tree_b, entry->d_name); if ( (a_executableness == 1) && (b_executableness == 0) ) { printf("chmod -x -- '"); for ( size_t i = 0; subrelpath[i]; i++ ) if ( subrelpath[i] == '\'' ) printf("'\\''"); else putchar(subrelpath[i]); printf("'\n"); } if ( (a_executableness != 1) && (b_executableness == 1) ) { printf("chmod +x -- '"); for ( size_t i = 0; subrelpath[i]; i++ ) if ( subrelpath[i] == '\'' ) printf("'\\''"); else putchar(subrelpath[i]); printf("'\n"); } free(subrelpath); continue; } closedir(dir_b); }
/** * Translate the input arguments of the current @tracee's syscall in the * @tracee->pid process area. This function sets @tracee->status to * -errno if an error occured from the tracee's point-of-view (EFAULT * for instance), otherwise 0. */ int translate_syscall_enter(Tracee *tracee) { int flags; int dirfd; int olddirfd; int newdirfd; int status; int status2; char path[PATH_MAX]; char oldpath[PATH_MAX]; char newpath[PATH_MAX]; word_t syscall_number; bool special = false; status = notify_extensions(tracee, SYSCALL_ENTER_START, 0, 0); if (status < 0) goto end; if (status > 0) return 0; /* Translate input arguments. */ syscall_number = get_sysnum(tracee, ORIGINAL); switch (syscall_number) { default: /* Nothing to do. */ status = 0; break; case PR_execve: status = translate_execve_enter(tracee); break; case PR_ptrace: status = translate_ptrace_enter(tracee); break; case PR_wait4: case PR_waitpid: status = translate_wait_enter(tracee); break; case PR_brk: translate_brk_enter(tracee); status = 0; break; case PR_getcwd: set_sysnum(tracee, PR_void); status = 0; break; case PR_fchdir: case PR_chdir: { struct stat statl; char *tmp; /* The ending "." ensures an error will be reported if * path does not exist or if it is not a directory. */ if (syscall_number == PR_chdir) { status = get_sysarg_path(tracee, path, SYSARG_1); if (status < 0) break; status = join_paths(2, oldpath, path, "."); if (status < 0) break; dirfd = AT_FDCWD; } else { strcpy(oldpath, "."); dirfd = peek_reg(tracee, CURRENT, SYSARG_1); } status = translate_path(tracee, path, dirfd, oldpath, true); if (status < 0) break; status = lstat(path, &statl); if (status < 0) break; /* Check this directory is accessible. */ if ((statl.st_mode & S_IXUSR) == 0) return -EACCES; /* Sadly this method doesn't detranslate statefully, * this means that there's an ambiguity when several * bindings are from the same host path: * * $ proot -m /tmp:/a -m /tmp:/b fchdir_getcwd /a * /b * * $ proot -m /tmp:/b -m /tmp:/a fchdir_getcwd /a * /a * * A solution would be to follow each file descriptor * just like it is done for cwd. */ status = detranslate_path(tracee, path, NULL); if (status < 0) break; /* Remove the trailing "/" or "/.". */ chop_finality(path); tmp = talloc_strdup(tracee->fs, path); if (tmp == NULL) { status = -ENOMEM; break; } TALLOC_FREE(tracee->fs->cwd); tracee->fs->cwd = tmp; talloc_set_name_const(tracee->fs->cwd, "$cwd"); set_sysnum(tracee, PR_void); status = 0; break; } case PR_bind: case PR_connect: { word_t address; word_t size; address = peek_reg(tracee, CURRENT, SYSARG_2); size = peek_reg(tracee, CURRENT, SYSARG_3); status = translate_socketcall_enter(tracee, &address, size); if (status <= 0) break; poke_reg(tracee, SYSARG_2, address); poke_reg(tracee, SYSARG_3, sizeof(struct sockaddr_un)); status = 0; break; } #define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) #define PEEK_WORD(addr, forced_errno) \ peek_word(tracee, addr); \ if (errno != 0) { \ status = forced_errno ?: -errno; \ break; \ } #define POKE_WORD(addr, value) \ poke_word(tracee, addr, value); \ if (errno != 0) { \ status = -errno; \ break; \ } case PR_accept: case PR_accept4: /* Nothing special to do if no sockaddr was specified. */ if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) { status = 0; break; } special = true; /* Fall through. */ case PR_getsockname: case PR_getpeername:{ int size; /* Remember: PEEK_WORD puts -errno in status and breaks if an * error occured. */ size = (int) PEEK_WORD(peek_reg(tracee, ORIGINAL, SYSARG_3), special ? -EINVAL : 0); /* The "size" argument is both used as an input parameter * (max. size) and as an output parameter (actual size). The * exit stage needs to know the max. size to not overwrite * anything, that's why it is copied in the 6th argument * (unused) before the kernel updates it. */ poke_reg(tracee, SYSARG_6, size); status = 0; break; } case PR_socketcall: { word_t args_addr; word_t sock_addr_saved; word_t sock_addr; word_t size_addr; word_t size; args_addr = peek_reg(tracee, CURRENT, SYSARG_2); switch (peek_reg(tracee, CURRENT, SYSARG_1)) { case SYS_BIND: case SYS_CONNECT: /* Handle these cases below. */ status = 1; break; case SYS_ACCEPT: case SYS_ACCEPT4: /* Nothing special to do if no sockaddr was specified. */ sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); if (sock_addr == 0) { status = 0; break; } special = true; /* Fall through. */ case SYS_GETSOCKNAME: case SYS_GETPEERNAME: /* Remember: PEEK_WORD puts -errno in status and breaks * if an error occured. */ size_addr = PEEK_WORD(SYSARG_ADDR(3), 0); size = (int) PEEK_WORD(size_addr, special ? -EINVAL : 0); /* See case PR_accept for explanation. */ poke_reg(tracee, SYSARG_6, size); status = 0; break; default: status = 0; break; } /* An error occured or there's nothing else to do. */ if (status <= 0) break; /* Remember: PEEK_WORD puts -errno in status and breaks if an * error occured. */ sock_addr = PEEK_WORD(SYSARG_ADDR(2), 0); size = PEEK_WORD(SYSARG_ADDR(3), 0); sock_addr_saved = sock_addr; status = translate_socketcall_enter(tracee, &sock_addr, size); if (status <= 0) break; /* These parameters are used/restored at the exit stage. */ poke_reg(tracee, SYSARG_5, sock_addr_saved); poke_reg(tracee, SYSARG_6, size); /* Remember: POKE_WORD puts -errno in status and breaks if an * error occured. */ POKE_WORD(SYSARG_ADDR(2), sock_addr); POKE_WORD(SYSARG_ADDR(3), sizeof(struct sockaddr_un)); status = 0; break; } #undef SYSARG_ADDR #undef PEEK_WORD #undef POKE_WORD case PR_access: case PR_acct: case PR_chmod: case PR_chown: case PR_chown32: case PR_chroot: case PR_getxattr: case PR_listxattr: case PR_mknod: case PR_oldstat: case PR_creat: case PR_removexattr: case PR_setxattr: case PR_stat: case PR_stat64: case PR_statfs: case PR_statfs64: case PR_swapoff: case PR_swapon: case PR_truncate: case PR_truncate64: case PR_umount: case PR_umount2: case PR_uselib: case PR_utime: case PR_utimes: status = translate_sysarg(tracee, SYSARG_1, REGULAR); break; case PR_open: flags = peek_reg(tracee, CURRENT, SYSARG_2); if ( ((flags & O_NOFOLLOW) != 0) || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) status = translate_sysarg(tracee, SYSARG_1, SYMLINK); else status = translate_sysarg(tracee, SYSARG_1, REGULAR); break; case PR_fchownat: case PR_fstatat64: case PR_newfstatat: case PR_utimensat: case PR_name_to_handle_at: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; flags = ( syscall_number == PR_fchownat || syscall_number == PR_name_to_handle_at) ? peek_reg(tracee, CURRENT, SYSARG_5) : peek_reg(tracee, CURRENT, SYSARG_4); if ((flags & AT_SYMLINK_NOFOLLOW) != 0) status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); else status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_fchmodat: case PR_faccessat: case PR_futimesat: case PR_mknodat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_inotify_add_watch: flags = peek_reg(tracee, CURRENT, SYSARG_3); if ((flags & IN_DONT_FOLLOW) != 0) status = translate_sysarg(tracee, SYSARG_2, SYMLINK); else status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_readlink: case PR_lchown: case PR_lchown32: case PR_lgetxattr: case PR_llistxattr: case PR_lremovexattr: case PR_lsetxattr: case PR_lstat: case PR_lstat64: case PR_oldlstat: case PR_unlink: case PR_rmdir: case PR_mkdir: status = translate_sysarg(tracee, SYSARG_1, SYMLINK); break; case PR_pivot_root: status = translate_sysarg(tracee, SYSARG_1, REGULAR); if (status < 0) break; status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_linkat: olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); flags = peek_reg(tracee, CURRENT, SYSARG_5); status = get_sysarg_path(tracee, oldpath, SYSARG_2); if (status < 0) break; status = get_sysarg_path(tracee, newpath, SYSARG_4); if (status < 0) break; if ((flags & AT_SYMLINK_FOLLOW) != 0) status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, REGULAR); else status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); break; case PR_mount: status = get_sysarg_path(tracee, path, SYSARG_1); if (status < 0) break; /* The following check covers only 90% of the cases. */ if (path[0] == '/' || path[0] == '.') { status = translate_path2(tracee, AT_FDCWD, path, SYSARG_1, REGULAR); if (status < 0) break; } status = translate_sysarg(tracee, SYSARG_2, REGULAR); break; case PR_openat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); flags = peek_reg(tracee, CURRENT, SYSARG_3); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; if ( ((flags & O_NOFOLLOW) != 0) || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); else status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); break; case PR_readlinkat: case PR_unlinkat: case PR_mkdirat: dirfd = peek_reg(tracee, CURRENT, SYSARG_1); status = get_sysarg_path(tracee, path, SYSARG_2); if (status < 0) break; status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); break; case PR_link: case PR_rename: status = translate_sysarg(tracee, SYSARG_1, SYMLINK); if (status < 0) break; status = translate_sysarg(tracee, SYSARG_2, SYMLINK); break; case PR_renameat: olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); status = get_sysarg_path(tracee, oldpath, SYSARG_2); if (status < 0) break; status = get_sysarg_path(tracee, newpath, SYSARG_4); if (status < 0) break; status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); break; case PR_symlink: status = translate_sysarg(tracee, SYSARG_2, SYMLINK); break; case PR_symlinkat: newdirfd = peek_reg(tracee, CURRENT, SYSARG_2); status = get_sysarg_path(tracee, newpath, SYSARG_3); if (status < 0) break; status = translate_path2(tracee, newdirfd, newpath, SYSARG_3, SYMLINK); break; } end: status2 = notify_extensions(tracee, SYSCALL_ENTER_END, status, 0); if (status2 < 0) status = status2; return status; }
static int init_chain(const char* target) { init_early(); prepare_block_devices(); load_fstab(); if ( atexit(init_chain_atexit) != 0 ) fatal("atexit: %m"); if ( !mkdtemp(chain_location) ) fatal("mkdtemp: /tmp/fs.XXXXXX: %m"); chain_location_made = true; bool found_root = false; for ( size_t i = 0; i < mountpoints_used; i++ ) { struct mountpoint* mountpoint = &mountpoints[i]; if ( !strcmp(mountpoint->entry.fs_file, "/") ) found_root = true; char* absolute = join_paths(chain_location, mountpoint->absolute); free(mountpoint->absolute); mountpoint->absolute = absolute; } if ( !found_root ) fatal("/etc/fstab: Root filesystem not found in filesystem table"); if ( atexit(mountpoints_unmount) != 0 ) fatal("atexit: %m"); mountpoints_mount(true); snprintf(chain_location_dev, sizeof(chain_location_dev), "%s/dev", chain_location); if ( mkdir(chain_location_dev, 755) < 0 && errno != EEXIST ) fatal("mkdir: %s: %m", chain_location_dev); int old_dev_fd = open("/dev", O_DIRECTORY | O_RDONLY); if ( old_dev_fd < 0 ) fatal("%s: %m", "/dev"); int new_dev_fd = open(chain_location_dev, O_DIRECTORY | O_RDONLY); if ( new_dev_fd < 0 ) fatal("%s: %m", chain_location_dev); if ( fsm_fsbind(old_dev_fd, new_dev_fd, 0) < 0 ) fatal("mount: `%s' onto `%s': %m", "/dev", chain_location_dev); close(new_dev_fd); close(old_dev_fd); int result; while ( true ) { pid_t child_pid = fork(); if ( child_pid < 0 ) fatal("fork: %m"); if ( !child_pid ) { if ( chroot(chain_location) < 0 ) fatal("chroot: %s: %m", chain_location); if ( chdir("/") < 0 ) fatal("chdir: %s: %m", chain_location); if ( !strcmp(target, "chain-merge") ) { const char* argv[] = { "sysmerge", "--booting", NULL }; execv("/sysmerge/sbin/sysmerge", (char* const*) argv); fatal("Failed to run automatic update: %s: %m", argv[0]); } else { unsetenv("INIT_PID"); const char* argv[] = { "init", NULL }; execv("/sbin/init", (char* const*) argv); fatal("Failed to load chain init: %s: %m", argv[0]); } } int status; if ( waitpid(child_pid, &status, 0) < 0 ) fatal("waitpid"); const char* back = ": Trying to bring it back up again"; if ( !strcmp(target, "chain-merge") ) { if ( WIFEXITED(status) && WEXITSTATUS(status) == 0 ) { target = "chain"; continue; } if ( WIFEXITED(status) ) fatal("Automatic upgrade failed: Exit status %i", WEXITSTATUS(status)); else if ( WIFSIGNALED(status) ) fatal("Automatic upgrade failed: %s", strsignal(WTERMSIG(status))); else fatal("Automatic upgrade failed: Unexpected unusual termination"); } if ( WIFEXITED(status) ) { result = WEXITSTATUS(status); break; } else if ( WIFSIGNALED(status) ) note("chain init: %s%s", strsignal(WTERMSIG(status)), back); else note("chain init: Unexpected unusual termination%s", back); } mountpoints_unmount(); init_chain_atexit(); return result; }
// create an absolute path by resolving relative paths with cwd std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd) { return make_canonical_path(join_paths(join_paths(cwd, base), path)); }
int main(int argc, char **argv) { int RC; pthread_attr_t attr; default_pthead_attr(&attr); pthread_mutex_init(&keep_alive_mutex, NULL); /* Lock the keep-alive mutex */ pthread_mutex_lock(&keep_alive_mutex); /* Allocate a new parallel_wrapper structure */ parallel_wrapper *par_wrapper = (parallel_wrapper *)calloc(1, sizeof(struct parallel_wrapper)); if (par_wrapper == (parallel_wrapper *)NULL) { print(PRNT_ERR, "Unable to allocate space for parallel wrapper\n"); return 1; } /* Install command signal handlers */ signal(SIGINT, handle_exit_signal); signal(SIGTERM, handle_exit_signal); signal(SIGHUP, handle_exit_signal); /* Create structures for this machine */ par_wrapper -> this_machine = calloc(1, sizeof(struct machine)); if (par_wrapper -> this_machine == (machine *)NULL) { print(PRNT_ERR, "Unable to allocate space for this machine\n"); return 1; } /* Fill in the default values for port ranges */ par_wrapper -> low_port = LOW_PORT; par_wrapper -> high_port = HIGH_PORT; /* Fill in other default values */ par_wrapper -> this_machine -> rank = -1; par_wrapper -> num_procs = -1; par_wrapper -> command_socket = -1; par_wrapper -> pgid = -1; par_wrapper -> ka_interval = KA_INTERVAL; par_wrapper -> timeout = TIMEOUT; /* Default mutex state */ pthread_mutex_init(&par_wrapper -> mutex, NULL); /* Allocate a list of symlinks */ par_wrapper -> symlinks = sll_get_list(); /* Get the initial working directory */ par_wrapper -> this_machine -> iwd = getcwd(NULL, 0); /* Allocates space */ /* Determine our name */ uid_t uid = getuid(); struct passwd *user_stats = getpwuid(uid); if (user_stats == (struct passwd *)NULL) { print(PRNT_WARN, "Unable to determine the name of the current user - assuming 'nobody'\n"); par_wrapper -> this_machine -> user = strdup("nobody"); } else { par_wrapper -> this_machine -> user = strdup(user_stats -> pw_name); } /* Parse environment variables and command line arguments */ parse_environment_vars(par_wrapper); parse_args(argc, argv, par_wrapper); /* Set the environment variables */ set_environment_vars(par_wrapper); /* Check that required values are filled in */ if (par_wrapper -> this_machine -> rank < 0) { print(PRNT_ERR, "Invalid rank (%d). Environment variable or command option not set.\n", par_wrapper -> this_machine -> rank); return 2; } if (par_wrapper -> num_procs < 0) { print(PRNT_ERR, "Invalid number of processors (%d). Environment variable or command option not set.\n", par_wrapper -> num_procs); return 2; } if (par_wrapper -> timeout <= (2*par_wrapper -> ka_interval)) { print(PRNT_WARN, "Keep-alive interval and timeout too close. Using default values.\n"); par_wrapper -> timeout = TIMEOUT; par_wrapper -> ka_interval = KA_INTERVAL; } /* Get the IP address for this machine */ par_wrapper -> this_machine -> ip_addr = get_ip_addr(); debug(PRNT_INFO, "IP Addr: %s\n", par_wrapper -> this_machine -> ip_addr); if (par_wrapper -> this_machine -> ip_addr == (char *)NULL) { print(PRNT_ERR, "Unable to get the IP address for this machine\n"); return 2; } /* Get a command port for this machine */ RC = get_bound_dgram_socket_by_range(par_wrapper -> low_port, par_wrapper -> high_port, &par_wrapper -> this_machine -> port, &par_wrapper -> command_socket); if (RC != 0) { print(PRNT_ERR, "Unable to bind to command socket\n"); return 2; } else { debug(PRNT_INFO, "Bound to command port: %d\n", par_wrapper -> this_machine -> port); } /** * If this is rank 0, point rank 0 to this_machine, otherwise allocate * a new structure for the master */ if (par_wrapper -> this_machine -> rank == MASTER) { par_wrapper -> master = par_wrapper -> this_machine; par_wrapper -> machines = (machine **) calloc(par_wrapper -> num_procs, sizeof(machine *)); if (par_wrapper -> machines == (machine **)NULL) { print(PRNT_ERR, "Unable to allocate space for machines array\n"); return 3; } par_wrapper -> machines[0] = par_wrapper -> master; } else { par_wrapper -> master = (machine *)calloc(1, sizeof(struct machine)); if (par_wrapper -> master == (machine *)NULL) { print(PRNT_ERR, "Unable to allocate space for master\n"); return 2; } par_wrapper -> master -> rank = MASTER; } /* Create the scratch directory */ create_scratch(par_wrapper); /* Gather the necessary chirp information */ RC = chirp_info(par_wrapper); if (RC != 0) { print(PRNT_ERR, "Failure sending/recieving chirp information\n"); return 2; } /* Create the listener */ pthread_create(&par_wrapper -> listener, &attr, &udp_server, (void *)par_wrapper); /* If I am the MASTER, wait for all ranks to register */ if (par_wrapper -> this_machine -> rank == MASTER) { int i, j; j = 0; /* Timeout = 0 */ while ( 1 ) { int finished = 1; for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == NULL) { finished = 0; } if ((j % par_wrapper -> ka_interval) == 0 && par_wrapper -> machines[i] == NULL) { debug(PRNT_INFO, "Waiting for registration from rank %d\n", i); } } j++; /* Waiting for timeout */ if (j >= par_wrapper -> timeout) { finished = 1; for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == NULL) { print(PRNT_WARN, "Rank %d not registered - giving up on it\n", i); } } } if (finished == 1) { break; } sleep(1); } debug(PRNT_INFO, "Finished machine registration.\n", par_wrapper -> num_procs); /* Create the machines file */ RC = create_machine_file(par_wrapper); if (RC != 0) { print(PRNT_ERR, "Unable to create the machines files"); cleanup(par_wrapper, 5); } RC = create_ssh_config(par_wrapper); if (RC != 0) { print(PRNT_ERR, "Unable to create the machines files"); cleanup(par_wrapper, 5); } } else /* Register with the master */ { struct timeval old_time = par_wrapper -> master -> last_alive; while ( 1 ) { RC = register_cmd(par_wrapper -> command_socket, par_wrapper -> this_machine -> rank, par_wrapper -> this_machine -> cpus, par_wrapper -> this_machine -> iwd, par_wrapper -> this_machine -> user, par_wrapper -> master -> ip_addr, par_wrapper -> master -> port); sleep(1); if (RC == 0 && ((old_time.tv_sec != par_wrapper -> master -> last_alive.tv_sec) || (old_time.tv_usec != par_wrapper -> master -> last_alive.tv_usec))) { break; } debug(PRNT_INFO, "Waiting for ACK from mater\n"); /* Retrieve chirp information (in case it changed since the last time) */ chirp_info(par_wrapper); } } /* MASTER - Identify unique hosts */ if (par_wrapper -> this_machine -> rank == MASTER) { int i, j; for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == (machine *)NULL) { continue; } /* All machines are initially unique */ par_wrapper -> machines[i] -> unique = 1; } for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == (machine *)NULL) { continue; } if (par_wrapper -> machines[i] -> unique == 0) { continue; /* This host is already not unique */ } for (j = i + 1; j < par_wrapper -> num_procs; j++) { if (par_wrapper -> machines[j] == (machine *)NULL) { continue; } if (par_wrapper -> machines[j] -> unique == 0) { continue; /* This host is already not unique */ } if (strcmp(par_wrapper -> machines[i] -> ip_addr, par_wrapper -> machines[j] -> ip_addr) == 0) { par_wrapper -> machines[j] -> unique = 0; debug(PRNT_INFO, "Rank %d (%s:%d) is not unique (some host as %d).\n", j, par_wrapper -> machines[j] -> ip_addr, par_wrapper -> machines[j] -> port, i); } } } } int shared_fs = 1; /* Flag which denotes a shared fs */ /* If I am rank 0 - determine if we need to create a fake file system */ if (par_wrapper -> this_machine -> rank == MASTER) { int i; for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == (machine *)NULL) { continue; } if (strcmp(par_wrapper -> machines[i] -> iwd, par_wrapper -> master -> iwd) != 0) { shared_fs = 0; } } if (shared_fs == 0) { char fake_fs[1024]; time_t curr_time = time(NULL); snprintf(fake_fs, 1024, "/tmp/condor_hydra_%d_%ld", par_wrapper -> cluster_id, (long)curr_time); debug(PRNT_INFO, "Using fake file system (%s). IWD's across ranks differ\n", fake_fs); for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == (machine *)NULL) { continue; } if (! par_wrapper -> machines[i] -> unique) { continue; } /* Send the command to create softlinks */ struct timeval old_time = par_wrapper -> machines[i] -> last_alive; while ( 1 ) { RC = create_link(par_wrapper -> command_socket, par_wrapper -> machines[i] -> iwd, fake_fs, par_wrapper -> machines[i] -> ip_addr, par_wrapper -> machines[i] -> port); usleep(100000); /* Sleep for 1/10th of a second */ if (RC == 0 && ((old_time.tv_sec != par_wrapper -> machines[i] -> last_alive.tv_sec) || (old_time.tv_usec != par_wrapper -> machines[i] -> last_alive.tv_usec))) { break; } debug(PRNT_INFO, "Waiting for ACK from rank %d\n", i); } } par_wrapper -> shared_fs = strdup(fake_fs); } else { par_wrapper -> shared_fs = strdup(par_wrapper -> master -> iwd); debug(PRNT_INFO, "Using a shared file system, IWD = %s\n", par_wrapper -> master -> iwd); } } /* Unlock the keepalive mutex */ pthread_mutex_unlock(&keep_alive_mutex); /* Start up the MPI executable */ if (par_wrapper -> this_machine -> rank == MASTER) { par_wrapper -> child_pid = fork(); if (par_wrapper -> child_pid == (pid_t) -1) { print(PRNT_ERR, "Fork failed\n"); cleanup(par_wrapper, 10); } else if (par_wrapper -> child_pid == (pid_t) 0) { /* I am the child */ prctl(PR_SET_PDEATHSIG, SIGTERM); /* Set environment variables */ char *machine_file = join_paths(par_wrapper -> scratch_dir, MACHINE_FILE); char *ssh_config = join_paths(par_wrapper -> scratch_dir, SSH_CONFIG); setenv("MACHINE_FILE", machine_file, 1); setenv("SSH_CONFIG", ssh_config, 1); free(machine_file); free(ssh_config); char *ssh_wrapper = join_paths(par_wrapper -> scratch_dir, SSH_WRAPPER); setenv("SSH_WRAPPER", ssh_wrapper, 1); free(ssh_wrapper); char temp_str[1024]; snprintf(temp_str, 1024, "%d", par_wrapper -> num_procs); setenv("NUM_MACHINES", temp_str, 1); /* Determine the total number of cpus allocated to this task */ int total_cpus = 0; int i; for (i = 0; i < par_wrapper -> num_procs; i++) { if (par_wrapper -> machines[i] == (machine *)NULL) { continue; } total_cpus += par_wrapper -> machines[i] -> cpus; } snprintf(temp_str, 1024, "%d", total_cpus); setenv("CPUS", temp_str, 1); setenv("NUM_PROCS", temp_str, 1); snprintf(temp_str, 1024, "%d", par_wrapper -> this_machine -> rank); setenv("RANK", temp_str, 1); snprintf(temp_str, 1024, "%d", par_wrapper -> cluster_id); setenv("CLUSTER_ID", temp_str, 1); snprintf(temp_str, 1024, "%d", par_wrapper -> this_machine -> port); setenv("CMD_PORT", temp_str, 1); snprintf(temp_str, 1024, "%d", par_wrapper -> this_machine -> cpus); setenv("REQUEST_CPUS", temp_str, 1); setenv("SCRATCH_DIR", par_wrapper -> scratch_dir, 1); setenv("IWD", par_wrapper -> this_machine -> iwd, 1); setenv("IP_ADDR", par_wrapper -> this_machine -> ip_addr, 1); setenv("TRANSFER_FILES", shared_fs != 0 ? "TRUE" : "FALSE", 1); setenv("SHARED_FS", shared_fs != 0 ? par_wrapper -> this_machine -> iwd : par_wrapper -> shared_fs, 1); setenv("SHARED_DIR", shared_fs != 0 ? par_wrapper -> this_machine -> iwd : par_wrapper -> shared_fs, 1); setenv("SCHEDD_IWD", par_wrapper -> this_machine -> schedd_iwd, 1); /* TODO: SSH ENVS */ /* Search in path */ int process_RC = execvp(par_wrapper -> executable[0], &par_wrapper -> executable[0]); if (process_RC != 0) { print(PRNT_ERR, "%s\n", get_exec_error_msg(errno, par_wrapper -> executable[0])); } exit(process_RC); } else { /* I am the parent */ int child_status = 0; /* Create a new group for the child processes */ par_wrapper -> pgid = setpgid(par_wrapper -> child_pid, par_wrapper -> child_pid); if (par_wrapper -> pgid < 0) { print(PRNT_WARN, "Unable to set process group for children\n"); } waitpid(par_wrapper -> child_pid, &child_status, WUNTRACED); /* Wait for the child to finish */ cleanup(par_wrapper, child_status); } } /* Always wait for the listener */ pthread_join(par_wrapper -> listener, NULL); return 0; }
/** * @brief main of scepclient * * @param argc number of arguments * @param argv pointer to the argument values */ int main(int argc, char **argv) { /* external values */ extern char * optarg; extern int optind; /* type of input and output files */ typedef enum { PKCS1 = 0x01, PKCS10 = 0x02, PKCS7 = 0x04, CERT_SELF = 0x08, CERT = 0x10, CACERT_ENC = 0x20, CACERT_SIG = 0x40, } scep_filetype_t; /* filetype to read from, defaults to "generate a key" */ scep_filetype_t filetype_in = 0; /* filetype to write to, no default here */ scep_filetype_t filetype_out = 0; /* input files */ char *file_in_pkcs1 = DEFAULT_FILENAME_PKCS1; char *file_in_pkcs10 = DEFAULT_FILENAME_PKCS10; char *file_in_cert_self = DEFAULT_FILENAME_CERT_SELF; char *file_in_cacert_enc = DEFAULT_FILENAME_CACERT_ENC; char *file_in_cacert_sig = DEFAULT_FILENAME_CACERT_SIG; /* output files */ char *file_out_pkcs1 = DEFAULT_FILENAME_PKCS1; char *file_out_pkcs10 = DEFAULT_FILENAME_PKCS10; char *file_out_pkcs7 = DEFAULT_FILENAME_PKCS7; char *file_out_cert_self = DEFAULT_FILENAME_CERT_SELF; char *file_out_cert = DEFAULT_FILENAME_CERT; char *file_out_ca_cert = DEFAULT_FILENAME_CACERT_ENC; /* by default user certificate is requested */ bool request_ca_certificate = FALSE; /* by default existing files are not overwritten */ bool force = FALSE; /* length of RSA key in bits */ u_int rsa_keylength = DEFAULT_RSA_KEY_LENGTH; /* validity of self-signed certificate */ time_t validity = DEFAULT_CERT_VALIDITY; time_t notBefore = 0; time_t notAfter = 0; /* distinguished name for requested certificate, ASCII format */ char *distinguishedName = NULL; /* challenge password */ char challenge_password_buffer[MAX_PASSWORD_LENGTH]; /* symmetric encryption algorithm used by pkcs7, default is DES */ encryption_algorithm_t pkcs7_symmetric_cipher = ENCR_DES; size_t pkcs7_key_size = 0; /* digest algorithm used by pkcs7, default is MD5 */ hash_algorithm_t pkcs7_digest_alg = HASH_MD5; /* signature algorithm used by pkcs10, default is MD5 */ hash_algorithm_t pkcs10_signature_alg = HASH_MD5; /* URL of the SCEP-Server */ char *scep_url = NULL; /* Name of CA to fetch CA certs for */ char *ca_name = "CAIdentifier"; /* http request method, default is GET */ bool http_get_request = TRUE; /* poll interval time in manual mode in seconds */ u_int poll_interval = DEFAULT_POLL_INTERVAL; /* maximum poll time */ u_int max_poll_time = 0; err_t ugh = NULL; /* initialize library */ if (!library_init(NULL)) { library_deinit(); exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); } if (lib->integrity && !lib->integrity->check_file(lib->integrity, "scepclient", argv[0])) { fprintf(stderr, "integrity check of scepclient failed\n"); library_deinit(); exit(SS_RC_DAEMON_INTEGRITY); } /* initialize global variables */ pkcs1 = chunk_empty; pkcs7 = chunk_empty; serialNumber = chunk_empty; transID = chunk_empty; fingerprint = chunk_empty; encoding = chunk_empty; pkcs10_encoding = chunk_empty; issuerAndSubject = chunk_empty; challengePassword = chunk_empty; getCertInitial = chunk_empty; scep_response = chunk_empty; subjectAltNames = linked_list_create(); options = options_create(); for (;;) { static const struct option long_opts[] = { /* name, has_arg, flag, val */ { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "optionsfrom", required_argument, NULL, '+' }, { "quiet", no_argument, NULL, 'q' }, { "debug", required_argument, NULL, 'l' }, { "in", required_argument, NULL, 'i' }, { "out", required_argument, NULL, 'o' }, { "force", no_argument, NULL, 'f' }, { "httptimeout", required_argument, NULL, 'T' }, { "keylength", required_argument, NULL, 'k' }, { "dn", required_argument, NULL, 'd' }, { "days", required_argument, NULL, 'D' }, { "startdate", required_argument, NULL, 'S' }, { "enddate", required_argument, NULL, 'E' }, { "subjectAltName", required_argument, NULL, 's' }, { "password", required_argument, NULL, 'p' }, { "algorithm", required_argument, NULL, 'a' }, { "url", required_argument, NULL, 'u' }, { "caname", required_argument, NULL, 'c'}, { "method", required_argument, NULL, 'm' }, { "interval", required_argument, NULL, 't' }, { "maxpolltime", required_argument, NULL, 'x' }, { 0,0,0,0 } }; /* parse next option */ int c = getopt_long(argc, argv, "hv+:qi:o:fk:d:s:p:a:u:c:m:t:x:APRCMS", long_opts, NULL); switch (c) { case EOF: /* end of flags */ break; case 'h': /* --help */ usage(NULL); case 'v': /* --version */ version(); case 'q': /* --quiet */ log_to_stderr = FALSE; continue; case 'l': /* --debug <level> */ default_loglevel = atoi(optarg); continue; case 'i': /* --in <type> [= <filename>] */ { char *filename = strstr(optarg, "="); if (filename) { /* replace '=' by '\0' */ *filename = '\0'; /* set pointer to start of filename */ filename++; } if (strcaseeq("pkcs1", optarg)) { filetype_in |= PKCS1; if (filename) file_in_pkcs1 = filename; } else if (strcaseeq("pkcs10", optarg)) { filetype_in |= PKCS10; if (filename) file_in_pkcs10 = filename; } else if (strcaseeq("cacert-enc", optarg)) { filetype_in |= CACERT_ENC; if (filename) file_in_cacert_enc = filename; } else if (strcaseeq("cacert-sig", optarg)) { filetype_in |= CACERT_SIG; if (filename) file_in_cacert_sig = filename; } else if (strcaseeq("cert-self", optarg)) { filetype_in |= CERT_SELF; if (filename) file_in_cert_self = filename; } else { usage("invalid --in file type"); } continue; } case 'o': /* --out <type> [= <filename>] */ { char *filename = strstr(optarg, "="); if (filename) { /* replace '=' by '\0' */ *filename = '\0'; /* set pointer to start of filename */ filename++; } if (strcaseeq("pkcs1", optarg)) { filetype_out |= PKCS1; if (filename) file_out_pkcs1 = filename; } else if (strcaseeq("pkcs10", optarg)) { filetype_out |= PKCS10; if (filename) file_out_pkcs10 = filename; } else if (strcaseeq("pkcs7", optarg)) { filetype_out |= PKCS7; if (filename) file_out_pkcs7 = filename; } else if (strcaseeq("cert-self", optarg)) { filetype_out |= CERT_SELF; if (filename) file_out_cert_self = filename; } else if (strcaseeq("cert", optarg)) { filetype_out |= CERT; if (filename) file_out_cert = filename; } else if (strcaseeq("cacert", optarg)) { request_ca_certificate = TRUE; if (filename) file_out_ca_cert = filename; } else { usage("invalid --out file type"); } continue; } case 'f': /* --force */ force = TRUE; continue; case 'T': /* --httptimeout */ http_timeout = atoi(optarg); if (http_timeout <= 0) { usage("invalid httptimeout specified"); } continue; case '+': /* --optionsfrom <filename> */ if (!options->from(options, optarg, &argc, &argv, optind)) { exit_scepclient("optionsfrom failed"); } continue; case 'k': /* --keylength <length> */ { div_t q; rsa_keylength = atoi(optarg); if (rsa_keylength == 0) usage("invalid keylength"); /* check if key length is a multiple of 8 bits */ q = div(rsa_keylength, 2*BITS_PER_BYTE); if (q.rem != 0) { exit_scepclient("keylength is not a multiple of %d bits!" , 2*BITS_PER_BYTE); } continue; } case 'D': /* --days */ if (optarg == NULL || !isdigit(optarg[0])) { usage("missing number of days"); } else { char *endptr; long days = strtol(optarg, &endptr, 0); if (*endptr != '\0' || endptr == optarg || days <= 0) usage("<days> must be a positive number"); validity = 24*3600*days; } continue; case 'S': /* --startdate */ if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') { usage("date format must be YYMMDDHHMMSSZ"); } else { chunk_t date = { optarg, 13 }; notBefore = asn1_to_time(&date, ASN1_UTCTIME); } continue; case 'E': /* --enddate */ if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') { usage("date format must be YYMMDDHHMMSSZ"); } else { chunk_t date = { optarg, 13 }; notAfter = asn1_to_time(&date, ASN1_UTCTIME); } continue; case 'd': /* --dn */ if (distinguishedName) { usage("only one distinguished name allowed"); } distinguishedName = optarg; continue; case 's': /* --subjectAltName */ { char *value = strstr(optarg, "="); if (value) { /* replace '=' by '\0' */ *value = '\0'; /* set pointer to start of value */ value++; } if (strcaseeq("email", optarg) || strcaseeq("dns", optarg) || strcaseeq("ip", optarg)) { subjectAltNames->insert_last(subjectAltNames, identification_create_from_string(value)); continue; } else { usage("invalid --subjectAltName type"); continue; } } case 'p': /* --password */ if (challengePassword.len > 0) { usage("only one challenge password allowed"); } if (strcaseeq("%prompt", optarg)) { printf("Challenge password: "******"challenge password could not be read"); } } else { challengePassword.ptr = optarg; challengePassword.len = strlen(optarg); } continue; case 'u': /* -- url */ if (scep_url) { usage("only one URL argument allowed"); } scep_url = optarg; continue; case 'c': /* -- caname */ ca_name = optarg; continue; case 'm': /* --method */ if (strcaseeq("get", optarg)) { http_get_request = TRUE; } else if (strcaseeq("post", optarg)) { http_get_request = FALSE; } else { usage("invalid http request method specified"); } continue; case 't': /* --interval */ poll_interval = atoi(optarg); if (poll_interval <= 0) { usage("invalid interval specified"); } continue; case 'x': /* --maxpolltime */ max_poll_time = atoi(optarg); continue; case 'a': /*--algorithm [<type>=]algo */ { const proposal_token_t *token; char *type = optarg; char *algo = strstr(optarg, "="); if (algo) { *algo = '\0'; algo++; } else { type = "enc"; algo = optarg; } if (strcaseeq("enc", type)) { token = lib->proposal->get_token(lib->proposal, algo); if (token == NULL || token->type != ENCRYPTION_ALGORITHM) { usage("invalid algorithm specified"); } pkcs7_symmetric_cipher = token->algorithm; pkcs7_key_size = token->keysize; if (encryption_algorithm_to_oid(token->algorithm, token->keysize) == OID_UNKNOWN) { usage("unsupported encryption algorithm specified"); } } else if (strcaseeq("dgst", type) || strcaseeq("sig", type)) { hash_algorithm_t hash; token = lib->proposal->get_token(lib->proposal, algo); if (token == NULL || token->type != INTEGRITY_ALGORITHM) { usage("invalid algorithm specified"); } hash = hasher_algorithm_from_integrity(token->algorithm, NULL); if (hash == OID_UNKNOWN) { usage("invalid algorithm specified"); } if (strcaseeq("dgst", type)) { pkcs7_digest_alg = hash; } else { pkcs10_signature_alg = hash; } } else { usage("invalid --algorithm type"); } continue; } default: usage("unknown option"); } /* break from loop */ break; } init_log("scepclient"); /* load plugins, further infrastructure may need it */ if (!lib->plugins->load(lib->plugins, NULL, lib->settings->get_str(lib->settings, "scepclient.load", PLUGINS))) { exit_scepclient("plugin loading failed"); } DBG1(DBG_APP, " loaded plugins: %s", lib->plugins->loaded_plugins(lib->plugins)); if ((filetype_out == 0) && (!request_ca_certificate)) { usage("--out filetype required"); } if (request_ca_certificate && (filetype_out > 0 || filetype_in > 0)) { usage("in CA certificate request, no other --in or --out option allowed"); } /* check if url is given, if cert output defined */ if (((filetype_out & CERT) || request_ca_certificate) && !scep_url) { usage("URL of SCEP server required"); } /* check for sanity of --in/--out */ if (!filetype_in && (filetype_in > filetype_out)) { usage("cannot generate --out of given --in!"); } /* get CA cert */ if (request_ca_certificate) { char ca_path[PATH_MAX]; container_t *container; pkcs7_t *pkcs7; if (!scep_http_request(scep_url, chunk_create(ca_name, strlen(ca_name)), SCEP_GET_CA_CERT, http_get_request, http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } join_paths(ca_path, sizeof(ca_path), CA_CERT_PATH, file_out_ca_cert); pkcs7 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER, scep_response, BUILD_END); if (!pkcs7) { /* no PKCS#7 encoded CA+RA certificates, assume simple CA cert */ DBG1(DBG_APP, "unable to parse PKCS#7, assuming plain CA cert"); if (!chunk_write(scep_response, ca_path, "ca cert", 0022, force)) { exit_scepclient("could not write ca cert file '%s'", ca_path); } } else { enumerator_t *enumerator; certificate_t *cert; int ra_certs = 0, ca_certs = 0; int ra_index = 1, ca_index = 1; enumerator = pkcs7->create_cert_enumerator(pkcs7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; if (x509->get_flags(x509) & X509_CA) { ca_certs++; } else { ra_certs++; } } enumerator->destroy(enumerator); enumerator = pkcs7->create_cert_enumerator(pkcs7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; bool ca_cert = x509->get_flags(x509) & X509_CA; char cert_path[PATH_MAX], *path = ca_path; if (ca_cert && ca_certs > 1) { add_path_suffix(cert_path, sizeof(cert_path), ca_path, "-%.1d", ca_index++); path = cert_path; } else if (!ca_cert) { /* use CA name as base for RA certs */ if (ra_certs > 1) { add_path_suffix(cert_path, sizeof(cert_path), ca_path, "-ra-%.1d", ra_index++); } else { add_path_suffix(cert_path, sizeof(cert_path), ca_path, "-ra"); } path = cert_path; } if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding) || !chunk_write(encoding, path, ca_cert ? "ca cert" : "ra cert", 0022, force)) { exit_scepclient("could not write cert file '%s'", path); } chunk_free(&encoding); } enumerator->destroy(enumerator); container = &pkcs7->container; container->destroy(container); } exit_scepclient(NULL); /* no further output required */ } creds = mem_cred_create(); lib->credmgr->add_set(lib->credmgr, &creds->set); /* * input of PKCS#1 file */ if (filetype_in & PKCS1) /* load an RSA key pair from file */ { char path[PATH_MAX]; join_paths(path, sizeof(path), PRIVATE_KEY_PATH, file_in_pkcs1); private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_FROM_FILE, path, BUILD_END); } else /* generate an RSA key pair */ { private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_KEY_SIZE, rsa_keylength, BUILD_END); } if (private_key == NULL) { exit_scepclient("no RSA private key available"); } creds->add_key(creds, private_key->get_ref(private_key)); public_key = private_key->get_public_key(private_key); /* check for minimum key length */ if (private_key->get_keysize(private_key) < RSA_MIN_OCTETS / BITS_PER_BYTE) { exit_scepclient("length of RSA key has to be at least %d bits", RSA_MIN_OCTETS * BITS_PER_BYTE); } /* * input of PKCS#10 file */ if (filetype_in & PKCS10) { char path[PATH_MAX]; join_paths(path, sizeof(path), REQ_PATH, file_in_pkcs10); pkcs10_req = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST, BUILD_FROM_FILE, path, BUILD_END); if (!pkcs10_req) { exit_scepclient("could not read certificate request '%s'", path); } subject = pkcs10_req->get_subject(pkcs10_req); subject = subject->clone(subject); } else { if (distinguishedName == NULL) { char buf[BUF_LEN]; int n = sprintf(buf, DEFAULT_DN); /* set the common name to the hostname */ if (gethostname(buf + n, BUF_LEN - n) || strlen(buf) == n) { exit_scepclient("no hostname defined, use " "--dn <distinguished name> option"); } distinguishedName = buf; } DBG2(DBG_APP, "dn: '%s'", distinguishedName); subject = identification_create_from_string(distinguishedName); if (subject->get_type(subject) != ID_DER_ASN1_DN) { exit_scepclient("parsing of distinguished name failed"); } DBG2(DBG_APP, "building pkcs10 object:"); pkcs10_req = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST, BUILD_SIGNING_KEY, private_key, BUILD_SUBJECT, subject, BUILD_SUBJECT_ALTNAMES, subjectAltNames, BUILD_CHALLENGE_PWD, challengePassword, BUILD_DIGEST_ALG, pkcs10_signature_alg, BUILD_END); if (!pkcs10_req) { exit_scepclient("generating pkcs10 request failed"); } } pkcs10_req->get_encoding(pkcs10_req, CERT_ASN1_DER, &pkcs10_encoding); fingerprint = scep_generate_pkcs10_fingerprint(pkcs10_encoding); DBG1(DBG_APP, " fingerprint: %s", fingerprint.ptr); /* * output of PKCS#10 file */ if (filetype_out & PKCS10) { char path[PATH_MAX]; join_paths(path, sizeof(path), REQ_PATH, file_out_pkcs10); if (!chunk_write(pkcs10_encoding, path, "pkcs10", 0022, force)) { exit_scepclient("could not write pkcs10 file '%s'", path); } filetype_out &= ~PKCS10; /* delete PKCS10 flag */ } if (!filetype_out) { exit_scepclient(NULL); /* no further output required */ } /* * output of PKCS#1 file */ if (filetype_out & PKCS1) { char path[PATH_MAX]; join_paths(path, sizeof(path), PRIVATE_KEY_PATH, file_out_pkcs1); DBG2(DBG_APP, "building pkcs1 object:"); if (!private_key->get_encoding(private_key, PRIVKEY_ASN1_DER, &pkcs1) || !chunk_write(pkcs1, path, "pkcs1", 0066, force)) { exit_scepclient("could not write pkcs1 file '%s'", path); } filetype_out &= ~PKCS1; /* delete PKCS1 flag */ } if (!filetype_out) { exit_scepclient(NULL); /* no further output required */ } scep_generate_transaction_id(public_key, &transID, &serialNumber); DBG1(DBG_APP, " transaction ID: %.*s", (int)transID.len, transID.ptr); /* * read or generate self-signed X.509 certificate */ if (filetype_in & CERT_SELF) { char path[PATH_MAX]; join_paths(path, sizeof(path), HOST_CERT_PATH, file_in_cert_self); x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); if (!x509_signer) { exit_scepclient("could not read certificate file '%s'", path); } } else { notBefore = notBefore ? notBefore : time(NULL); notAfter = notAfter ? notAfter : (notBefore + validity); x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_SIGNING_KEY, private_key, BUILD_PUBLIC_KEY, public_key, BUILD_SUBJECT, subject, BUILD_NOT_BEFORE_TIME, notBefore, BUILD_NOT_AFTER_TIME, notAfter, BUILD_SERIAL, serialNumber, BUILD_SUBJECT_ALTNAMES, subjectAltNames, BUILD_END); if (!x509_signer) { exit_scepclient("generating certificate failed"); } } creds->add_cert(creds, TRUE, x509_signer->get_ref(x509_signer)); /* * output of self-signed X.509 certificate file */ if (filetype_out & CERT_SELF) { char path[PATH_MAX]; join_paths(path, sizeof(path), HOST_CERT_PATH, file_out_cert_self); if (!x509_signer->get_encoding(x509_signer, CERT_ASN1_DER, &encoding)) { exit_scepclient("encoding certificate failed"); } if (!chunk_write(encoding, path, "self-signed cert", 0022, force)) { exit_scepclient("could not write self-signed cert file '%s'", path); } chunk_free(&encoding); filetype_out &= ~CERT_SELF; /* delete CERT_SELF flag */ } if (!filetype_out) { exit_scepclient(NULL); /* no further output required */ } /* * load ca encryption certificate */ { char path[PATH_MAX]; join_paths(path, sizeof(path), CA_CERT_PATH, file_in_cacert_enc); x509_ca_enc = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); if (!x509_ca_enc) { exit_scepclient("could not load encryption cacert file '%s'", path); } } /* * input of PKCS#7 file */ if (filetype_in & PKCS7) { /* user wants to load a pkcs7 encrypted request * operation is not yet supported! * would require additional parsing of transaction-id pkcs7 = pkcs7_read_from_file(file_in_pkcs7); */ } else { DBG2(DBG_APP, "building pkcs7 request"); pkcs7 = scep_build_request(pkcs10_encoding, transID, SCEP_PKCSReq_MSG, x509_ca_enc, pkcs7_symmetric_cipher, pkcs7_key_size, x509_signer, pkcs7_digest_alg, private_key); if (!pkcs7.ptr) { exit_scepclient("failed to build pkcs7 request"); } } /* * output pkcs7 encrypted and signed certificate request */ if (filetype_out & PKCS7) { char path[PATH_MAX]; join_paths(path, sizeof(path), REQ_PATH, file_out_pkcs7); if (!chunk_write(pkcs7, path, "pkcs7 encrypted request", 0022, force)) { exit_scepclient("could not write pkcs7 file '%s'", path); } filetype_out &= ~PKCS7; /* delete PKCS7 flag */ } if (!filetype_out) { exit_scepclient(NULL); /* no further output required */ } /* * output certificate fetch from SCEP server */ if (filetype_out & CERT) { bool stored = FALSE; certificate_t *cert; enumerator_t *enumerator; char path[PATH_MAX]; time_t poll_start = 0; pkcs7_t *p7; container_t *container = NULL; chunk_t chunk; scep_attributes_t attrs = empty_scep_attributes; join_paths(path, sizeof(path), CA_CERT_PATH, file_in_cacert_sig); x509_ca_sig = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); if (!x509_ca_sig) { exit_scepclient("could not load signature cacert file '%s'", path); } creds->add_cert(creds, TRUE, x509_ca_sig->get_ref(x509_ca_sig)); if (!scep_http_request(scep_url, pkcs7, SCEP_PKI_OPERATION, http_get_request, http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } ugh = scep_parse_response(scep_response, transID, &container, &attrs); if (ugh != NULL) { exit_scepclient(ugh); } /* in case of manual mode, we are going into a polling loop */ if (attrs.pkiStatus == SCEP_PENDING) { identification_t *issuer = x509_ca_sig->get_subject(x509_ca_sig); DBG1(DBG_APP, " scep request pending, polling every %d seconds", poll_interval); poll_start = time_monotonic(NULL); issuerAndSubject = asn1_wrap(ASN1_SEQUENCE, "cc", issuer->get_encoding(issuer), subject); } while (attrs.pkiStatus == SCEP_PENDING) { if (max_poll_time > 0 && (time_monotonic(NULL) - poll_start >= max_poll_time)) { exit_scepclient("maximum poll time reached: %d seconds" , max_poll_time); } DBG2(DBG_APP, "going to sleep for %d seconds", poll_interval); sleep(poll_interval); free(scep_response.ptr); container->destroy(container); DBG2(DBG_APP, "fingerprint: %.*s", (int)fingerprint.len, fingerprint.ptr); DBG2(DBG_APP, "transaction ID: %.*s", (int)transID.len, transID.ptr); chunk_free(&getCertInitial); getCertInitial = scep_build_request(issuerAndSubject, transID, SCEP_GetCertInitial_MSG, x509_ca_enc, pkcs7_symmetric_cipher, pkcs7_key_size, x509_signer, pkcs7_digest_alg, private_key); if (!getCertInitial.ptr) { exit_scepclient("failed to build scep request"); } if (!scep_http_request(scep_url, getCertInitial, SCEP_PKI_OPERATION, http_get_request, http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } ugh = scep_parse_response(scep_response, transID, &container, &attrs); if (ugh != NULL) { exit_scepclient(ugh); } } if (attrs.pkiStatus != SCEP_SUCCESS) { container->destroy(container); exit_scepclient("reply status is not 'SUCCESS'"); } if (!container->get_data(container, &chunk)) { container->destroy(container); exit_scepclient("extracting signed-data failed"); } container->destroy(container); /* decrypt enveloped-data container */ container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER, chunk, BUILD_END); free(chunk.ptr); if (!container) { exit_scepclient("could not decrypt envelopedData"); } if (!container->get_data(container, &chunk)) { container->destroy(container); exit_scepclient("extracting encrypted-data failed"); } container->destroy(container); /* parse signed-data container */ container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER, chunk, BUILD_END); free(chunk.ptr); if (!container) { exit_scepclient("could not parse singed-data"); } /* no need to verify the signed-data container, the signature does NOT * cover the contained certificates */ /* store the end entity certificate */ join_paths(path, sizeof(path), HOST_CERT_PATH, file_out_cert); p7 = (pkcs7_t*)container; enumerator = p7->create_cert_enumerator(p7); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; if (!(x509->get_flags(x509) & X509_CA)) { if (stored) { exit_scepclient("multiple certs received, only first stored"); } if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding) || !chunk_write(encoding, path, "requested cert", 0022, force)) { exit_scepclient("could not write cert file '%s'", path); } chunk_free(&encoding); stored = TRUE; } } enumerator->destroy(enumerator); container->destroy(container); chunk_free(&attrs.transID); chunk_free(&attrs.senderNonce); chunk_free(&attrs.recipientNonce); filetype_out &= ~CERT; /* delete CERT flag */ } exit_scepclient(NULL); return -1; /* should never be reached */ }