Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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);
    }
}
Beispiel #5
0
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 */
}
Beispiel #6
0
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;
}
Beispiel #8
0
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);
}
Beispiel #9
0
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);
    }
}
Beispiel #10
0
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 */
}