static bool rejected_name(const char *name, char **v, int flags) { bool r = is_in_string_list(name, v); if (flags & MAKEDESC_WHITELIST) r = !r; return r; }
bool load_global_configuration_from_dirs(const char *dirs[], int dir_flags[]) { if (s_global_settings == NULL) { s_global_settings = new_map_string(); bool ret = load_conf_file_from_dirs_ext("libreport.conf", dirs, dir_flags, s_global_settings, /*don't skip without value*/ false); if (!ret) { error_msg("Failed to load libreport global configuration"); free_global_configuration(); return false; } map_string_iter_t iter; init_map_string_iter(&iter, s_global_settings); const char *key, *value; while(next_map_string_iter(&iter, &key, &value)) { /* Die to avoid security leaks in case where someone made a typo in a option name */ if (!is_in_string_list(key, s_recognized_options)) { error_msg("libreport global configuration contains unrecognized option : '%s'", key); free_global_configuration(); return false; } } } else log_notice("libreport global configuration already loaded"); return true; }
static msg_content_t * create_journal_message(problem_data_t *problem_data, problem_report_t *pr, unsigned dump_opts) { msg_content_t *msg_c = msg_content_new(); /* mandatory fields */ msg_content_add(msg_c, "MESSAGE", problem_report_get_summary(pr)); msg_content_add(msg_c, "PRIORITY", MESSAGE_PRIORITY); /* add problem report description into PROBLEM_REPORT field */ char *description = NULL; if (strcmp(problem_report_get_description(pr), "") != 0) description = xasprintf("\n%s", problem_report_get_description(pr)); msg_content_add(msg_c, "PROBLEM_REPORT", description ? description : ""); free(description); if (!(dump_opts & DUMP_FULL)) { msg_content_add_fields(msg_c, problem_data, fields_default_no_prefix); msg_content_add_fields_ext(msg_c, problem_data, fields_default, FIELD_PREFIX); /* add defined default fields */ if (dump_opts & DUMP_ESSENTIAL) msg_content_add_fields_ext(msg_c, problem_data, fields_essential, FIELD_PREFIX); } /* add all fields from problem directory */ else { /* iterate over all problem_data elements */ for (GList *elem = problem_data_get_all_elements(problem_data); elem != NULL; elem = elem->next) { const problem_item *item = problem_data_get_item_or_NULL(problem_data, elem->data); /* add only text elements */ if (item && (item->flags & CD_FLAG_TXT)) { /* elements listed in fields_default_no_prefix are added withou prefix */ if (is_in_string_list(elem->data, fields_default_no_prefix)) msg_content_add(msg_c, elem->data, item->content); else msg_content_add_ext(msg_c, elem->data, item->content, FIELD_PREFIX); } } } return msg_c; }
void problem_data_load_from_dump_dir(problem_data_t *problem_data, struct dump_dir *dd, char **excluding) { char *short_name; char *full_name; dd_init_next_file(dd); while (dd_get_next_file(dd, &short_name, &full_name)) { if (excluding && is_in_string_list(short_name, excluding)) { //log("Excluded:'%s'", short_name); goto next; } if (short_name[0] == '#' || (short_name[0] && short_name[strlen(short_name) - 1] == '~') ) { //log("Excluded (editor backup file):'%s'", short_name); goto next; } ssize_t sz = 4*1024; char *text = NULL; bool editable = is_editable_file(short_name); if (!editable) { text = is_text_file(full_name, &sz); if (!text) { problem_data_add(problem_data, short_name, full_name, CD_FLAG_BIN + CD_FLAG_ISNOTEDITABLE ); goto next; } } char *content; if (sz < 4*1024) /* did is_text_file read entire file? */ { /* yes */ content = text; } else { /* no, need to read it all */ free(text); content = dd_load_text(dd, short_name); } /* Strip '\n' from one-line elements: */ char *nl = strchr(content, '\n'); if (nl && nl[1] == '\0') *nl = '\0'; /* Sanitize possibly corrupted utf8. * Of control chars, allow only tab and newline. */ char *sanitized = sanitize_utf8(content, (SANITIZE_ALL & ~SANITIZE_LF & ~SANITIZE_TAB) ); if (sanitized) { free(content); content = sanitized; } int flags = 0; if (editable) flags |= CD_FLAG_TXT | CD_FLAG_ISEDITABLE; else flags |= CD_FLAG_TXT | CD_FLAG_ISNOTEDITABLE; static const char *const list_files[] = { FILENAME_UID , FILENAME_PACKAGE , FILENAME_EXECUTABLE, FILENAME_TIME , FILENAME_COUNT , NULL }; if (is_in_string_list(short_name, (char**)list_files)) flags |= CD_FLAG_LIST; if (strcmp(short_name, FILENAME_TIME) == 0) flags |= CD_FLAG_UNIXTIME; problem_data_add(problem_data, short_name, content, flags ); free(content); next: free(short_name); free(full_name); } }
static char* is_text_file(const char *name, ssize_t *sz) { /* We were using magic.h API to check for file being text, but it thinks * that file containing just "0" is not text (!!) * So, we do it ourself. */ int fd = open(name, O_RDONLY); if (fd < 0) return NULL; /* it's not text (because it does not exist! :) */ off_t size = lseek(fd, 0, SEEK_END); if (size < 0 || size > CD_MAX_TEXT_SIZE) { close(fd); return NULL; /* it's not a SMALL text */ } lseek(fd, 0, SEEK_SET); unsigned char *buf = xmalloc(*sz); ssize_t r = full_read(fd, buf, *sz); close(fd); if (r < 0) { free(buf); return NULL; /* it's not text (because we can't read it) */ } if (r < *sz) buf[r] = '\0'; *sz = r; /* Some files in our dump directories are known to always be textual */ const char *base = strrchr(name, '/'); if (base) { base++; if (is_in_string_list(base, (char**)always_text_files)) return (char*)buf; } /* Every once in a while, even a text file contains a few garbled * or unexpected non-ASCII chars. We should not declare it "binary". * * Used to have RATIO = 50 (2%), but then came Fedora 19 with * os_release = "Schrödinger's Cat". Bumped to 10%. * Alternatives: add os_release to always_text_files[] * or add "if it is valid Unicode, then it's text" check here. * * Replaced crude "buf[r] > 0x7e is bad" logic with * "if it is a broken Unicode, then it's bad". */ const unsigned RATIO = 10; unsigned total_chars = r + RATIO; unsigned bad_chars = 1; /* 1 prevents division by 0 later */ bool prev_was_unicode = 0; ssize_t i = -1; while (++i < r) { /* Among control chars, only '\t','\n' etc are allowed */ if (buf[i] < ' ' && !isspace(buf[i])) { /* We don't like NULs and other control chars very much. * Not text for sure! */ free(buf); return NULL; } if (buf[i] == 0x7f) bad_chars++; else if (buf[i] > 0x7f) { /* We test two possible bad cases with one comparison: * (1) prev byte was unicode AND cur byte is 11xxxxxx: * BAD - unicode start byte can't be in the middle of unicode char * (2) prev byte wasnt unicode AND cur byte is 10xxxxxx: * BAD - unicode continuation byte can't start unicode char */ if (prev_was_unicode == ((buf[i] & 0x40) == 0x40)) bad_chars++; } prev_was_unicode = (buf[i] > 0x7f); } if ((total_chars / bad_chars) >= RATIO) return (char*)buf; /* looks like text to me */ free(buf); return NULL; /* it's binary */ }
static bool is_editable_file(const char *file_name) { return is_in_string_list(file_name, (char**)editable_files); }
static int create_and_upload_archive( const char *dump_dir_name, map_string_t *settings) { int result = 1; /* error */ pid_t child; TAR* tar = NULL; const char* errmsg = NULL; char* tempfile = NULL; struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); /* error msg is already logged by dd_opendir */ /* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */ log(_("Compressing data")); //TODO: //Encrypt = yes //ArchiveType = .tar.bz2 //ExcludeFiles = foo,bar*,b*z const char* opt = getenv("Upload_URL"); if (!opt) opt = get_map_string_item_or_empty(settings, "URL"); char *url = opt[0] != '\0' ? xstrdup(opt) : ask_url(_("Please enter a URL (scp, ftp, etc.) where the problem data is to be exported:")); /* Create a child gzip which will compress the data */ /* SELinux guys are not happy with /tmp, using /var/run/abrt */ /* Reverted back to /tmp for ABRT2 */ /* Changed again to /var/tmp because of Fedora feature tmp-on-tmpfs */ tempfile = concat_path_basename(LARGE_DATA_TMP_DIR, dump_dir_name); tempfile = append_to_malloced_string(tempfile, ".tar.gz"); int pipe_from_parent_to_child[2]; xpipe(pipe_from_parent_to_child); child = vfork(); if (child == 0) { /* child */ close(pipe_from_parent_to_child[1]); xmove_fd(pipe_from_parent_to_child[0], 0); xmove_fd(xopen3(tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600), 1); execlp("gzip", "gzip", NULL); perror_msg_and_die("Can't execute '%s'", "gzip"); } close(pipe_from_parent_to_child[0]); /* If child died (say, in xopen), then parent might get SIGPIPE. * We want to properly unlock dd, therefore we must not die on SIGPIPE: */ signal(SIGPIPE, SIG_IGN); /* Create tar writer object */ if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile, /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } /* Write data to the tarball */ { string_vector_ptr_t exclude_from_report = get_global_always_excluded_elements(); dd_init_next_file(dd); char *short_name, *full_name; while (dd_get_next_file(dd, &short_name, &full_name)) { if (exclude_from_report && is_in_string_list(short_name, (const_string_vector_const_ptr_t)exclude_from_report)) goto next; // dd_get_next_file guarantees that it's a REG: //struct stat stbuf; //if (stat(full_name, &stbuf) != 0) // || !S_ISREG(stbuf.st_mode) //) { // goto next; //} if (tar_append_file(tar, full_name, short_name) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; free(short_name); free(full_name); goto ret; } next: free(short_name); free(full_name); } } dd_close(dd); dd = NULL; /* Close tar writer... */ if (tar_append_eof(tar) != 0 || tar_close(tar) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } tar = NULL; /* ...and check that gzip child finished successfully */ int status; safe_waitpid(child, &status, 0); child = -1; if (status != 0) { /* We assume the error was out-of-disk-space or out-of-quota */ errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } /* Upload the tarball */ /* Upload from /tmp to /tmp + deletion -> BAD, exclude this possibility */ if (url && url[0] && strcmp(url, "file://"LARGE_DATA_TMP_DIR"/") != 0) { post_state_t *state = new_post_state(POST_WANT_ERROR_MSG); state->username = getenv("Upload_Username"); char *password_inp = NULL; if (state->username != NULL && state->username[0] != '\0') { /* Load Password only if Username is configured, it doesn't make */ /* much sense to load Password without Username. */ state->password = getenv("Upload_Password"); if (state->password == NULL) { /* Be permissive and nice, ask only once and don't check */ /* the result. User can dismiss this prompt but the upload */ /* may work somehow??? */ char *msg = xasprintf(_("Please enter password for uploading:"), state->username); state->password = password_inp = ask_password(msg); free(msg); } } char *remote_name = upload_file_ext(state, url, tempfile, UPLOAD_FILE_HANDLE_ACCESS_DENIALS); result = (remote_name == NULL); /* error if NULL */ free(remote_name); free(password_inp); free_post_state(state); /* cleanup code will delete tempfile */ } else { result = 0; /* success */ log(_("Archive is created: '%s'"), tempfile); free(tempfile); tempfile = NULL; } ret: free(url); dd_close(dd); if (tar) tar_close(tar); /* close(pipe_from_parent_to_child[1]); - tar_close() does it itself */ if (child > 0) safe_waitpid(child, NULL, 0); if (tempfile) { unlink(tempfile); free(tempfile); } if (errmsg) error_msg_and_die("%s", errmsg); return result; }
char *make_description(problem_data_t *problem_data, char **names_to_skip, unsigned max_text_size, unsigned desc_flags) { struct strbuf *buf_dsc = strbuf_new(); GList *list = g_hash_table_get_keys(problem_data); list = g_list_sort(list, (GCompareFunc)strcmp); GList *l; /* Print one-liners. Format: * NAME1: <maybe more spaces>VALUE1 * NAME2: <maybe more spaces>VALUE2 */ bool empty = true; l = list; while (l) { const char *key = l->data; l = l->next; /* Skip items we are not interested in */ //TODO: optimize by doing this once, not 3 times: if (names_to_skip && is_in_string_list(key, names_to_skip)) continue; struct problem_item *item = g_hash_table_lookup(problem_data, key); if (!item) continue; if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST)) continue; if ((item->flags & CD_FLAG_TXT) && strlen(item->content) <= max_text_size ) { char *formatted = format_problem_item(item); char *output = formatted ? formatted : item->content; char *eol = strchr(output, '\n'); if (!eol) { int pad = 16 - (strlen(key) + 2); if (pad < 0) pad = 0; strbuf_append_strf(buf_dsc, "%s: %*s%s\n", key, pad, "", output); empty = false; } free(formatted); } } bool append_empty_line = !empty; if (desc_flags & MAKEDESC_SHOW_FILES) { /* Print file info. Format: * <empty line if needed> * NAME1: <maybe more spaces>Binary file, NNN bytes * NAME2: <maybe more spaces>Text file, NNN bytes * * In many cases, it is useful to know how big binary files are * (for example, helps with diagnosing bug upload problems) */ l = list; while (l) { const char *key = l->data; l = l->next; /* Skip items we are not interested in */ if (names_to_skip && is_in_string_list(key, names_to_skip)) continue; struct problem_item *item = g_hash_table_lookup(problem_data, key); if (!item) continue; if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST)) continue; if ((item->flags & CD_FLAG_BIN) || ((item->flags & CD_FLAG_TXT) && strlen(item->content) > max_text_size) ) { if (append_empty_line) strbuf_append_char(buf_dsc, '\n'); append_empty_line = false; struct stat statbuf; int stat_err = 0; if (item->flags & CD_FLAG_BIN) stat_err = stat(item->content, &statbuf); else statbuf.st_size = strlen(item->content); /* We don't print item->content for CD_FLAG_BIN, as it is * always "/path/to/dump/dir/KEY" - not informative. */ int pad = 16 - (strlen(key) + 2); if (pad < 0) pad = 0; strbuf_append_strf(buf_dsc, (!stat_err ? "%s: %*s%s file, %llu bytes\n" : "%s: %*s%s file\n"), key, pad, "", ((item->flags & CD_FLAG_BIN) ? "Binary" : "Text"), (long long)statbuf.st_size ); empty = false; } } } if (desc_flags & MAKEDESC_SHOW_MULTILINE) { /* Print multi-liners. Format: * <empty line if needed> * NAME: * :LINE1 * :LINE2 * :LINE3 */ l = list; while (l) { const char *key = l->data; l = l->next; /* Skip items we are not interested in */ if (names_to_skip && is_in_string_list(key, names_to_skip)) continue; struct problem_item *item = g_hash_table_lookup(problem_data, key); if (!item) continue; if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST)) continue; if ((item->flags & CD_FLAG_TXT) && strlen(item->content) <= max_text_size ) { char *formatted = format_problem_item(item); char *output = formatted ? formatted : item->content; char *eol = strchr(output, '\n'); if (eol) { if (!empty) strbuf_append_char(buf_dsc, '\n'); strbuf_append_str(buf_dsc, key); strbuf_append_str(buf_dsc, ":\n"); for (;;) { eol = strchrnul(output, '\n'); strbuf_append_strf(buf_dsc, ":%.*s\n", (int)(eol - output), output); if (*eol == '\0' || eol[1] == '\0') break; output = eol + 1; } empty = false; } free(formatted); } } } g_list_free(list); return strbuf_free_nobuf(buf_dsc); }
void load_problem_data_from_dump_dir(problem_data_t *problem_data, struct dump_dir *dd) { char *short_name; char *full_name; dd_init_next_file(dd); while (dd_get_next_file(dd, &short_name, &full_name)) { ssize_t sz = 4*1024; char *text = NULL; bool editable = is_editable_file(short_name); if (!editable) { text = is_text_file(full_name, &sz); if (!text) { add_to_problem_data_ext(problem_data, short_name, full_name, CD_FLAG_BIN + CD_FLAG_ISNOTEDITABLE ); free(short_name); free(full_name); continue; } } char *content; if (sz < 4*1024) /* did is_text_file read entire file? */ { content = text; /* Strip '\n' from one-line elements: */ char *nl = strchr(content, '\n'); if (nl && nl[1] == '\0') *nl = '\0'; } else { /* no, need to read it all */ free(text); content = dd_load_text(dd, short_name); } int flags = 0; if (editable) flags |= CD_FLAG_TXT | CD_FLAG_ISEDITABLE; else flags |= CD_FLAG_TXT | CD_FLAG_ISNOTEDITABLE; static const char *const list_files[] = { FILENAME_UID , FILENAME_PACKAGE , FILENAME_EXECUTABLE, FILENAME_TIME , FILENAME_COUNT , NULL }; if (is_in_string_list(short_name, (char**)list_files)) flags |= CD_FLAG_LIST; if (strcmp(short_name, FILENAME_TIME) == 0) flags |= CD_FLAG_UNIXTIME; add_to_problem_data_ext(problem_data, short_name, content, flags ); free(short_name); free(full_name); free(content); } }
static char* is_text_file(const char *name, ssize_t *sz) { /* We were using magic.h API to check for file being text, but it thinks * that file containing just "0" is not text (!!) * So, we do it ourself. */ int fd = open(name, O_RDONLY); if (fd < 0) return NULL; /* it's not text (because it does not exist! :) */ /* Maybe 64k limit is small. But _some_ limit is necessary: * fields declared "text" may end up in editing fields and such. * We don't want to accidentally end up with 100meg text in a textbox! * So, don't remove this. If you really need to, raise the limit. * * Bumped up to 200k: saw 124740 byte /proc/PID/smaps file */ off_t size = lseek(fd, 0, SEEK_END); if (size < 0 || size > 200*1024) { close(fd); return NULL; /* it's not a SMALL text */ } lseek(fd, 0, SEEK_SET); char *buf = (char*)xmalloc(*sz); ssize_t r = full_read(fd, buf, *sz); close(fd); if (r < 0) { free(buf); return NULL; /* it's not text (because we can't read it) */ } if (r < *sz) buf[r] = '\0'; *sz = r; /* Some files in our dump directories are known to always be textual */ const char *base = strrchr(name, '/'); if (base) { base++; if (is_in_string_list(base, (char**)always_text_files)) return buf; } /* Every once in a while, even a text file contains a few garbled * or unexpected non-ASCII chars. We should not declare it "binary". */ const unsigned RATIO = 50; unsigned total_chars = r + RATIO; unsigned bad_chars = 1; /* 1 prevents division by 0 later */ while (--r >= 0) { if (buf[r] >= 0x7f /* among control chars, only '\t','\n' etc are allowed */ || (buf[r] < ' ' && !isspace(buf[r])) ) { if (buf[r] == '\0') { /* We don't like NULs very much. Not text for sure! */ free(buf); return NULL; } bad_chars++; } } if ((total_chars / bad_chars) >= RATIO) return buf; /* looks like text to me */ free(buf); return NULL; /* it's binary */ }