bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen#include "lib.h"
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen#include "str.h"
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen#include "eacces-error.h"
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen#include "mkdir-parents.h"
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#include "ipwd.h"
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen#include <sys/stat.h>
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen#include <fcntl.h>
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen#include <unistd.h>
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic int ATTR_NULL(5)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenmkdir_chown_full(const char *path, mode_t mode, uid_t uid,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid_t gid, const char *gid_origin)
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen string_t *str;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen mode_t old_mask;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen unsigned int i;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen int ret, fd = -1, orig_errno;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen for (i = 0;; i++) {
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen old_mask = umask(0);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen ret = mkdir(path, mode);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen umask(old_mask);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (ret < 0)
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen break;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen fd = open(path, O_RDONLY);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (fd != -1)
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen break;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (errno != ENOENT || i == 3) {
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_error("open(%s) failed: %m", path);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen return -1;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen }
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen /* it was just rmdir()ed by someone else? retry */
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen }
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen if (ret < 0) {
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen if (errno == EISDIR || errno == ENOSYS) {
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen /* EISDIR check is for BSD/OS which returns it if path
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen contains '/' at the end and it exists.
a96307ca3944c434f2c50f47c12985bcd34445b5Timo Sirainen
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen ENOSYS check is for NFS mount points. */
a96307ca3944c434f2c50f47c12985bcd34445b5Timo Sirainen errno = EEXIST;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen }
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_assert(fd == -1);
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen return -1;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen }
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (fchown(fd, uid, gid) < 0) {
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_close_fd(&fd);
b710c5119d3b66572eb87a15ac6d8dcd192f7cc5Timo Sirainen orig_errno = errno;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (rmdir(path) < 0 && errno != ENOENT)
b710c5119d3b66572eb87a15ac6d8dcd192f7cc5Timo Sirainen i_error("rmdir(%s) failed: %m", path);
b710c5119d3b66572eb87a15ac6d8dcd192f7cc5Timo Sirainen errno = orig_errno;
b710c5119d3b66572eb87a15ac6d8dcd192f7cc5Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (errno == EPERM && uid == (uid_t)-1) {
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_error("%s", eperm_error_get_chgrp("fchown", path, gid,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid_origin));
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return -1;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen }
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen str = t_str_new(256);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen str_printfa(str, "fchown(%s, %ld", path,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen uid == (uid_t)-1 ? -1L : (long)uid);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (uid != (uid_t)-1) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen struct passwd pw;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (i_getpwuid(uid, &pw) > 0)
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen str_printfa(str, "(%s)", pw.pw_name);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen }
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen str_printfa(str, ", %ld",
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid == (gid_t)-1 ? -1L : (long)gid);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (gid != (gid_t)-1) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen struct group gr;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (i_getgrgid(uid, &gr) > 0)
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen str_printfa(str, "(%s)", gr.gr_name);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen }
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen errno = orig_errno;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen i_error("%s) failed: %m", str_c(str));
a96307ca3944c434f2c50f47c12985bcd34445b5Timo Sirainen return -1;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen }
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen if (gid != (gid_t)-1 && (mode & S_ISGID) == 0) {
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen /* make sure the directory doesn't have setgid bit enabled
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen (in case its parent had) */
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (fchmod(fd, mode) < 0) {
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen orig_errno = errno;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen if (rmdir(path) < 0 && errno != ENOENT)
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen i_error("rmdir(%s) failed: %m", path);
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen errno = orig_errno;
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_error("fchmod(%s) failed: %m", path);
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_close_fd(&fd);
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen return -1;
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen }
a3e3650e98d326e8cc89e2a58061714af3214953Timo Sirainen }
a6bb10d402b5f28af4c31a4daa07e47146a3f897Timo Sirainen i_close_fd(&fd);
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen return 0;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen}
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenint mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return mkdir_chown_full(path, mode, uid, gid, NULL);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenint mkdir_chgrp(const char *path, mode_t mode,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid_t gid, const char *gid_origin)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic int ATTR_NULL(5)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenmkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen const char *gid_origin)
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen{
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen const char *p;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen int ret;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen if (errno != ENOENT)
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen return -1;
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen /* doesn't exist, try recursively creating our parent dir */
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen p = strrchr(path, '/');
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen if (p == NULL || p == path)
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen return -1; /* shouldn't happen */
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen ret = mkdir_parents_chown_full(t_strdup_until(path, p),
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen mode, uid,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid, gid_origin);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
d8d50edd0fddcc98582edc0301eb01e90ff0b9b5Timo Sirainen if (ret < 0 && errno != EEXIST)
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen return -1;
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen /* should work now */
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen return -1;
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen }
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen return 0;
53b32ab10cd22d7e762db4d813298ff72bff4b3dTimo Sirainen}
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenint mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenint mkdir_parents_chgrp(const char *path, mode_t mode,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen gid_t gid, const char *gid_origin)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainenint mkdir_parents(const char *path, mode_t mode)
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen{
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
6f9560d31a6fffeed234dc3b841169c40db1c5e1Timo Sirainen}
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainenint stat_first_parent(const char *path, const char **root_dir_r,
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen struct stat *st_r)
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen{
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen const char *p;
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen while (stat(path, st_r) < 0) {
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen if (errno != ENOENT || strcmp(path, "/") == 0) {
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen *root_dir_r = path;
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen return -1;
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen }
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen p = strrchr(path, '/');
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen if (p == NULL)
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen path = "/";
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen else
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen path = t_strdup_until(path, p);
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen }
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen *root_dir_r = path;
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen return 0;
766814de3fbfeb8ca7c4d6b592f6ca09f14d5690Timo Sirainen}