Commit message: Fix regression in permissions of created files

Commit 16387744 changed temporary file creation to use mkstemp, resulting in
new files being created with 0600 permissions.  For brand new files created
through Augeas, their permissions stayed at 0600 rather than being set by the
umask as before.

  * src/transform.c (transform_save): chmod after creating new files to
    permissions implied by the umask
CVE: None
Vulnerability description: None

Project: augeas
File: transform.c

int text_store(struct augeas *aug, const char *lens_path,
               const char *path, const char *text) {
    struct info *info = NULL;
    struct lns_error *err = NULL;
    struct tree *tree = NULL;
    struct span *span = NULL;
    int result = -1;
    const char *err_status = NULL;
    struct lens *lens = NULL;

    lens = lens_from_name(aug, lens_path);
    if (lens == NULL) {
        goto done;
    }

    make_ref(info);
    info->first_line = 1;
    info->last_line = 1;
    info->first_column = 1;
    info->last_column = strlen(text);

    tree = lns_get(info, lens, text, &err);
    if (err != NULL) {
        err_status = "parse_failed";
        goto done;
    }

    unref(info, info);

    tree_replace(aug, path, tree);

    /* top level node span entire file length */
    if (span != NULL && tree != NULL) {
        tree->parent->span = span;
        tree->parent->span->span_start = 0;
        tree->parent->span->span_end = strlen(text);
    }

    tree = NULL;

    result = 0;
done:
    store_error(aug, NULL, path, err_status, errno, err, text);
    free_tree(tree);
    free_lns_error(err);
    return result;
}

Project: augeas
File: transform.c

int remove_file(struct augeas *aug, struct tree *tree) {
    char *path = NULL;
    const char *filename = NULL;
    const char *err_status = NULL;
    char *dyn_err_status = NULL;
    char *augsave = NULL, *augorig = NULL, *augorig_canon = NULL;
    int r;

    path = path_of_tree(tree);
    if (path == NULL) {
        err_status = "path_of_tree";
        goto error;
    }
    filename = path + strlen(AUGEAS_META_FILES);

    if ((augorig = strappend(aug->root, filename + 1)) == NULL) {
        err_status = "root_file";
        goto error;
    }

    augorig_canon = canonicalize_file_name(augorig);
    if (augorig_canon == NULL) {
        if (errno == ENOENT) {
            goto done;
        } else {
            err_status = "canon_augorig";
            goto error;
        }
    }

    r = file_saved_event(aug, path + strlen(AUGEAS_META_TREE));
    if (r < 0) {
        err_status = "saved_event";
        goto error;
    }

    if (aug->flags & AUG_SAVE_NOOP)
        goto done;

    if (aug->flags & AUG_SAVE_BACKUP) {
        /* Move file to one with extension .augsave */
        r = asprintf(&augsave, "%s" EXT_AUGSAVE, augorig_canon);
        if (r == -1) {
            augsave = NULL;
                goto error;
        }

        r = clone_file(augorig_canon, augsave, &err_status, 1, 1);
        if (r != 0) {
            dyn_err_status = strappend(err_status, "_augsave");
            goto error;
        }
    } else {
        /* Unlink file */
        r = unlink(augorig_canon);
        if (r < 0) {
            err_status = "unlink_orig";
            goto error;
        }
    }
    tree_unlink(tree);
 done:
    free(path);
    free(augorig);
    free(augorig_canon);
    free(augsave);
    return 0;
 error:
    {
        const char *emsg =
            dyn_err_status == NULL ? err_status : dyn_err_status;
        store_error(aug, filename, path, emsg, errno, NULL, NULL);
    }
    free(path);
    free(augorig);
    free(augorig_canon);
    free(augsave);
    free(dyn_err_status);
    return -1;
}

Project: augeas
File: transform.c

static int filter_generate(struct tree *xfm, const char *root,
                           int *nmatches, char ***matches) {
    glob_t globbuf;
    int gl_flags = glob_flags;
    int r;
    int ret = 0;
    char **pathv = NULL;
    int pathc = 0;
    int root_prefix = strlen(root) - 1;

    *nmatches = 0;
    *matches = NULL;
    MEMZERO(&globbuf, 1);

    list_for_each(f, xfm->children) {
        char *globpat = NULL;
        if (! is_incl(f))
            continue;
        pathjoin(&globpat, 2, root, f->value);
        r = glob(globpat, gl_flags, NULL, &globbuf);
        free(globpat);

        if (r != 0 && r != GLOB_NOMATCH)
            goto error;
        gl_flags |= GLOB_APPEND;
    }

    pathc = globbuf.gl_pathc;
    int pathind = 0;

    if (ALLOC_N(pathv, pathc) < 0)
        goto error;

    for (int i=0; i < pathc; i++) {
        const char *path = globbuf.gl_pathv[i] + root_prefix;
        bool include = true;

        list_for_each(e, xfm->children) {
            if (! is_excl(e))
                continue;

            if (strchr(e->value, SEP) == NULL)
                path = pathbase(path);
            if ((r = fnmatch(e->value, path, fnm_flags)) == 0) {
                include = false;
            }
        }

        if (include)
            include = is_regular_file(globbuf.gl_pathv[i]);

        if (include) {
            pathv[pathind] = strdup(globbuf.gl_pathv[i]);
            if (pathv[pathind] == NULL)
                goto error;
            pathind += 1;
        }
    }
    pathc = pathind;

    if (REALLOC_N(pathv, pathc) == -1)
        goto error;

    *matches = pathv;
    *nmatches = pathc;
 done:
    globfree(&globbuf);
    return ret;
 error:
    if (pathv != NULL)
        for (int i=0; i < pathc; i++)
            free(pathv[i]);
    free(pathv);
    ret = -1;
    goto done;
}

Project: augeas
File: transform.c

static struct tree *file_info(struct augeas *aug, const char *fname) {
    char *path = NULL;
    struct tree *result = NULL;
    int r;

    r = pathjoin(&path, 2, AUGEAS_META_FILES, fname);
    ERR_NOMEM(r < 0, aug);

    result = tree_find(aug, path);
    ERR_BAIL(aug);
 error:
    free(path);
    return result;
}

Project: augeas
File: transform.c

void transform_file_error(struct augeas *aug, const char *status,
                          const char *filename, const char *format, ...) {
    char *ep = err_path(filename);
    struct tree *err;
    char *msg;
    va_list ap;
    int r;

    err = tree_find_cr(aug, ep);
    if (err == NULL)
        return;

    tree_unlink_children(aug, err);
    tree_set_value(err, status);

    err = tree_child_cr(err, s_message);
    if (err == NULL)
        return;

    va_start(ap, format);
    r = vasprintf(&msg, format, ap);
    va_end(ap);
    if (r < 0)
        return;
    tree_set_value(err, msg);
    free(msg);
}

Project: augeas
File: transform.c

static int add_file_info(struct augeas *aug, const char *node,
                         struct lens *lens, const char *lens_name,
                         const char *filename, bool force_reload) {
    struct tree *file, *tree;
    char *tmp = NULL;
    int r;
    char *path = NULL;
    int result = -1;

    if (lens == NULL)
        return -1;

    r = pathjoin(&path, 2, AUGEAS_META_TREE, node);
    ERR_NOMEM(r < 0, aug);

    file = tree_find_cr(aug, path);
    ERR_BAIL(aug);

    /* Set 'path' */
    tree = tree_child_cr(file, s_path);
    ERR_NOMEM(tree == NULL, aug);
    r = tree_set_value(tree, node);
    ERR_NOMEM(r < 0, aug);

    /* Set 'mtime' */
    if (force_reload) {
        tmp = strdup("0");
        ERR_NOMEM(tmp == NULL, aug);
    } else {
        tmp = mtime_as_string(aug, filename);
        ERR_BAIL(aug);
    }
    tree = tree_child_cr(file, s_mtime);
    ERR_NOMEM(tree == NULL, aug);
    tree_store_value(tree, &tmp);

    /* Set 'lens/info' */
    tmp = format_info(lens->info);
    ERR_NOMEM(tmp == NULL, aug);
    tree = tree_path_cr(file, 2, s_lens, s_info);
    ERR_NOMEM(tree == NULL, aug);
    r = tree_set_value(tree, tmp);
    ERR_NOMEM(r < 0, aug);
    FREE(tmp);

    /* Set 'lens' */
    tree = tree->parent;
    r = tree_set_value(tree, lens_name);
    ERR_NOMEM(r < 0, aug);

    tree_clean(file);

    result = 0;
 error:
    free(path);
    free(tmp);
    return result;
}

Project: augeas
File: transform.c

int transform_validate(struct augeas *aug, struct tree *xfm) {
    struct tree *l = NULL;

    for (struct tree *t = xfm->children; t != NULL; ) {
        if (streqv(t->label, "lens")) {
            l = t;
        } else if ((is_incl(t) || (is_excl(t) && strchr(t->value, SEP) != NULL))
                       && t->value[0] != SEP) {
            /* Normalize relative paths to absolute ones */
            int r;
            r = REALLOC_N(t->value, strlen(t->value) + 2);
            ERR_NOMEM(r < 0, aug);
            memmove(t->value + 1, t->value, strlen(t->value) + 1);
            t->value[0] = SEP;
        }

        if (streqv(t->label, "error")) {
            struct tree *del = t;
            t = del->next;
            tree_unlink(del);
        } else {
            t = t->next;
        }
    }

    if (l == NULL) {
        xfm_error(xfm, "missing a child with label 'lens'");
        return -1;
    }
    if (l->value == NULL) {
        xfm_error(xfm, "the 'lens' node does not contain a lens name");
        return -1;
    }
    lens_from_name(aug, l->value);
    ERR_BAIL(aug);

    return 0;
 error:
    xfm_error(xfm, aug->error->details);
    return -1;
}

Project: augeas
File: transform.c

static int load_file(struct augeas *aug, struct lens *lens,
                     const char *lens_name, char *filename) {
    char *text = NULL;
    const char *err_status = NULL;
    struct tree *tree = NULL;
    char *path = NULL;
    struct lns_error *err = NULL;
    struct span *span = NULL;
    int result = -1, r, text_len = 0;

    path = file_name_path(aug, filename);
    ERR_NOMEM(path == NULL, aug);

    r = add_file_info(aug, path, lens, lens_name, filename, false);
    if (r < 0)
        goto done;

    text = xread_file(filename);
    if (text == NULL) {
        err_status = "read_failed";
        goto done;
    }
    text_len = strlen(text);
    text = append_newline(text, text_len);

    struct info *info;
    make_ref(info);
    make_ref(info->filename);
    info->filename->str = strdup(filename);
    info->error = aug->error;
    info->flags = aug->flags;
    info->first_line = 1;

    if (aug->flags & AUG_ENABLE_SPAN) {
        span = make_span(info);
        ERR_NOMEM(span == NULL, info);
    }

    tree = lns_get(info, lens, text, &err);

    unref(info, info);

    if (err != NULL) {
        err_status = "parse_failed";
        goto done;
    }

    tree_replace(aug, path, tree);

    /* top level node span entire file length */
    if (span != NULL && tree != NULL) {
        tree->parent->span = span;
        tree->parent->span->span_start = 0;
        tree->parent->span->span_end = text_len;
    }

    tree = NULL;

    result = 0;
 done:
    store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
                errno, err, text);
 error:
    free_lns_error(err);
    free(path);
    free_tree(tree);
    free(text);
    return result;
}

Project: augeas
File: transform.c

static int filter_matches(struct tree *xfm, const char *path) {
    int found = 0;
    list_for_each(f, xfm->children) {
        if (is_incl(f) && fnmatch(f->value, path, fnm_flags) == 0) {
            found = 1;
            break;
        }
    }
    if (! found)
        return 0;
    list_for_each(f, xfm->children) {
        if (is_excl(f) && (fnmatch(f->value, path, fnm_flags) == 0))
            return 0;
    }
    return 1;
}

Project: augeas
File: transform.c

static void xfm_error(struct tree *xfm, const char *msg) {
    char *v = strdup(msg);
    char *l = strdup("error");

    if (l == NULL || v == NULL)
        return;
    tree_append(xfm, l, v);
}

Project: augeas
File: transform.c

static bool file_current(struct augeas *aug, const char *fname,
                         struct tree *finfo) {
    struct tree *mtime = tree_child(finfo, s_mtime);
    struct tree *file = NULL, *path = NULL;
    int r;
    struct stat st;
    int64_t mtime_i;

    if (mtime == NULL || mtime->value == NULL)
        return false;

    r = xstrtoint64(mtime->value, 10, &mtime_i);
    if (r < 0) {
        /* Ignore silently and err on the side of caution */
        return false;
    }

    r = stat(fname, &st);
    if (r < 0)
        return false;

    if (mtime_i != (int64_t) st.st_mtime)
        return false;

    path = tree_child(finfo, s_path);
    if (path == NULL)
        return false;

    file = tree_find(aug, path->value);
    return (file != NULL && ! file->dirty);
}

Project: augeas
File: transform.c

static int store_error(struct augeas *aug,
                       const char *filename, const char *path,
                       const char *status, int errnum,
                       const struct lns_error *err, const char *text) {
    struct tree *err_info = NULL, *finfo = NULL;
    char *fip = NULL;
    int r;
    int result = -1;

    if (filename != NULL) {
        r = pathjoin(&fip, 2, AUGEAS_META_FILES, filename);
    } else {
        r = pathjoin(&fip, 2, AUGEAS_META_TEXT, path);
    }
    ERR_NOMEM(r < 0, aug);

    finfo = tree_find_cr(aug, fip);
    ERR_BAIL(aug);

    if (status != NULL) {
        err_info = tree_child_cr(finfo, s_error);
        ERR_NOMEM(err_info == NULL, aug);

        r = tree_set_value(err_info, status);
        ERR_NOMEM(r < 0, aug);

        /* Errors from err_set are ignored on purpose. We try
         * to report as much as we can */
        if (err != NULL) {
            if (err->pos >= 0) {
                size_t line, ofs;
                err_set(aug, err_info, s_pos, "%d", err->pos);
                if (text != NULL) {
                    calc_line_ofs(text, err->pos, &line, &ofs);
                    err_set(aug, err_info, s_line, "%zd", line);
                    err_set(aug, err_info, s_char, "%zd", ofs);
                }
            }
            if (err->path != NULL) {
                err_set(aug, err_info, s_path, "%s%s", path, err->path);
            }
            if (err->lens != NULL) {
                char *fi = format_info(err->lens->info);
                if (fi != NULL) {
                    err_set(aug, err_info, s_lens, "%s", fi);
                    free(fi);
                }
            }
            err_set(aug, err_info, s_message, "%s", err->message);
        } else if (errnum != 0) {
            const char *msg = strerror(errnum);
            err_set(aug, err_info, s_message, "%s", msg);
        }
    } else {
        /* No error, nuke the error node if it exists */
        err_info = tree_child(finfo, s_error);
        if (err_info != NULL) {
            tree_unlink_children(aug, err_info);
            pathx_symtab_remove_descendants(aug->symtab, err_info);
            tree_unlink(err_info);
        }
    }

    tree_clean(finfo);
    result = 0;
 error:
    free(fip);
    return result;
}