Fix potential TOCTOU when creating time stamp directory and file.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2013-2018 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
* Copyright (c) 2013-2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -247,6 +247,8 @@ sudo_dso_public const char *sudo_logpri2str_v1(int num);
|
||||
/* mkdir_parents.c */
|
||||
sudo_dso_public bool sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet);
|
||||
#define sudo_mkdir_parents(_a, _b, _c, _d, _e) sudo_mkdir_parents_v1((_a), (_b), (_c), (_d), (_e))
|
||||
sudo_dso_public int sudo_open_parent_dir_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet);
|
||||
#define sudo_open_parent_dir(_a, _b, _c, _d, _e) sudo_open_parent_dir_v1((_a), (_b), (_c), (_d), (_e))
|
||||
|
||||
/* mmap_alloc.c */
|
||||
sudo_dso_public void *sudo_mmap_alloc_v1(size_t size) sudo_malloclike;
|
||||
@@ -295,6 +297,10 @@ sudo_dso_public int sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, s
|
||||
#define sudo_secure_dir(_a, _b, _c, _d) sudo_secure_dir_v1((_a), (_b), (_c), (_d))
|
||||
sudo_dso_public int sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *sbp);
|
||||
#define sudo_secure_file(_a, _b, _c, _d) sudo_secure_file_v1((_a), (_b), (_c), (_d))
|
||||
sudo_dso_public int sudo_secure_open_file_v1(const char *path, uid_t uid, gid_t gid, int *error);
|
||||
#define sudo_secure_open_file(_a, _b, _c, _d) sudo_secure_open_file_v1((_a), (_b), (_c), (_d))
|
||||
sudo_dso_public int sudo_secure_open_dir_v1(const char *path, uid_t uid, gid_t gid, int *error);
|
||||
#define sudo_secure_open_dir(_a, _b, _c, _d) sudo_secure_open_dir_v1((_a), (_b), (_c), (_d))
|
||||
|
||||
/* setgroups.c */
|
||||
sudo_dso_public int sudo_setgroups_v1(int ngids, const GETGROUPS_T *gids);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
* Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -76,16 +76,17 @@ is_dir(int dfd, const char *name, int namelen, bool quiet)
|
||||
}
|
||||
|
||||
/*
|
||||
* Create any parent directories needed by path (but not path itself).
|
||||
* Create any parent directories needed by path (but not path itself)
|
||||
* and return an open fd for the parent directory or -1 on error.
|
||||
*/
|
||||
bool
|
||||
sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet)
|
||||
int
|
||||
sudo_open_parent_dir_v1(const char *path, uid_t uid, gid_t gid, mode_t mode,
|
||||
bool quiet)
|
||||
{
|
||||
const char *cp, *ep, *pathend;
|
||||
char name[PATH_MAX];
|
||||
bool ret = false;
|
||||
int parentfd;
|
||||
debug_decl(sudo_mkdir_parents, SUDO_DEBUG_UTIL);
|
||||
debug_decl(sudo_open_parent_dir, SUDO_DEBUG_UTIL);
|
||||
|
||||
/* Starting parent dir is either root or cwd. */
|
||||
cp = path;
|
||||
@@ -100,7 +101,7 @@ sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool
|
||||
if (parentfd == -1) {
|
||||
if (!quiet)
|
||||
sudo_warn(U_("unable to open %s"), *path == '/' ? "/" : ".");
|
||||
debug_return_bool(false);
|
||||
debug_return_int(-1);
|
||||
}
|
||||
|
||||
/* Iterate over path components, skipping the last one. */
|
||||
@@ -129,7 +130,7 @@ reopen:
|
||||
sudo_warn(U_("unable to open %.*s"),
|
||||
(int)(ep - path), path);
|
||||
}
|
||||
goto done;
|
||||
goto bad;
|
||||
}
|
||||
if (mkdirat(parentfd, name, mode) == 0) {
|
||||
dfd = openat(parentfd, name, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0);
|
||||
@@ -138,12 +139,12 @@ reopen:
|
||||
sudo_warn(U_("unable to open %.*s"),
|
||||
(int)(ep - path), path);
|
||||
}
|
||||
goto done;
|
||||
goto bad;
|
||||
}
|
||||
/* Make sure the path we created is still a directory. */
|
||||
if (!is_dir(dfd, path, ep - path, quiet)) {
|
||||
close(dfd);
|
||||
goto done;
|
||||
goto bad;
|
||||
}
|
||||
if (uid != (uid_t)-1 && gid != (gid_t)-1) {
|
||||
if (fchown(dfd, uid, gid) != 0) {
|
||||
@@ -159,22 +160,39 @@ reopen:
|
||||
sudo_warn(U_("unable to mkdir %.*s"),
|
||||
(int)(ep - path), path);
|
||||
}
|
||||
goto done;
|
||||
goto bad;
|
||||
}
|
||||
} else {
|
||||
/* Already exists, make sure it is a directory. */
|
||||
if (!is_dir(dfd, path, ep - path, quiet)) {
|
||||
close(dfd);
|
||||
goto done;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
close(parentfd);
|
||||
parentfd = dfd;
|
||||
}
|
||||
ret = true;
|
||||
|
||||
done:
|
||||
debug_return_int(parentfd);
|
||||
bad:
|
||||
if (parentfd != -1)
|
||||
close(parentfd);
|
||||
debug_return_bool(ret);
|
||||
debug_return_int(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create any parent directories needed by path (but not path itself).
|
||||
*/
|
||||
bool
|
||||
sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode,
|
||||
bool quiet)
|
||||
{
|
||||
int fd;
|
||||
debug_decl(sudo_mkdir_parents, SUDO_DEBUG_UTIL);
|
||||
|
||||
fd = sudo_open_parent_dir(path, uid, gid, mode, quiet);
|
||||
if (fd == -1)
|
||||
debug_return_bool(false);
|
||||
close(fd);
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2012, 2014-2016 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
* Copyright (c) 2012, 2014-2022 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -24,7 +24,9 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sudo_compat.h"
|
||||
#include "sudo_util.h"
|
||||
@@ -34,16 +36,11 @@
|
||||
* Verify that path is the right type and not writable by other users.
|
||||
*/
|
||||
static int
|
||||
sudo_secure_path(const char *path, unsigned int type, uid_t uid, gid_t gid, struct stat *sb)
|
||||
sudo_check_secure(struct stat *sb, unsigned int type, uid_t uid, gid_t gid)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
int ret = SUDO_PATH_MISSING;
|
||||
debug_decl(sudo_secure_path, SUDO_DEBUG_UTIL);
|
||||
int ret = SUDO_PATH_SECURE;
|
||||
debug_decl(sudo_check_secure, SUDO_DEBUG_UTIL);
|
||||
|
||||
if (sb == NULL)
|
||||
sb = &stat_buf;
|
||||
|
||||
if (path != NULL && stat(path, sb) == 0) {
|
||||
if ((sb->st_mode & S_IFMT) != type) {
|
||||
ret = SUDO_PATH_BAD_TYPE;
|
||||
} else if (uid != (uid_t)-1 && sb->st_uid != uid) {
|
||||
@@ -53,28 +50,89 @@ sudo_secure_path(const char *path, unsigned int type, uid_t uid, gid_t gid, stru
|
||||
} else if (ISSET(sb->st_mode, S_IWGRP) &&
|
||||
(gid == (gid_t)-1 || sb->st_gid != gid)) {
|
||||
ret = SUDO_PATH_GROUP_WRITABLE;
|
||||
} else {
|
||||
ret = SUDO_PATH_SECURE;
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_int(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that path is the right type and not writable by other users.
|
||||
*/
|
||||
static int
|
||||
sudo_secure_path(const char *path, unsigned int type, uid_t uid, gid_t gid,
|
||||
struct stat *sb)
|
||||
{
|
||||
int ret = SUDO_PATH_MISSING;
|
||||
struct stat stat_buf;
|
||||
debug_decl(sudo_secure_path, SUDO_DEBUG_UTIL);
|
||||
|
||||
if (sb == NULL)
|
||||
sb = &stat_buf;
|
||||
|
||||
if (path != NULL && stat(path, sb) == 0)
|
||||
ret = sudo_check_secure(sb, type, uid, gid);
|
||||
|
||||
debug_return_int(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that path is a regular file and not writable by other users.
|
||||
*/
|
||||
int
|
||||
sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *st)
|
||||
sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb)
|
||||
{
|
||||
return sudo_secure_path(path, S_IFREG, uid, gid, st);
|
||||
return sudo_secure_path(path, S_IFREG, uid, gid, sb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that path is a directory and not writable by other users.
|
||||
*/
|
||||
int
|
||||
sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *st)
|
||||
sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb)
|
||||
{
|
||||
return sudo_secure_path(path, S_IFDIR, uid, gid, st);
|
||||
return sudo_secure_path(path, S_IFDIR, uid, gid, sb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open path read-only as long as it is not writable by other users.
|
||||
* Returns an open file descriptor on success, else -1.
|
||||
* Sets error to SUDO_PATH_SECURE on success, and a value < 0 on failure.
|
||||
*/
|
||||
static int
|
||||
sudo_secure_open(const char *path, int type, uid_t uid, gid_t gid, int *error)
|
||||
{
|
||||
struct stat sb;
|
||||
int fd;
|
||||
debug_decl(sudo_secure_open, SUDO_DEBUG_UTIL);
|
||||
|
||||
fd = open(path, O_RDONLY|O_NONBLOCK);
|
||||
if (fd == -1 || fstat(fd, &sb) != 0) {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
*error = SUDO_PATH_MISSING;
|
||||
debug_return_int(-1);
|
||||
}
|
||||
|
||||
*error = sudo_check_secure(&sb, type, uid, gid);
|
||||
if (*error == SUDO_PATH_SECURE) {
|
||||
(void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
|
||||
} else {
|
||||
/* Not secure, caller can check error flag. */
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
debug_return_int(fd);
|
||||
}
|
||||
|
||||
int
|
||||
sudo_secure_open_file_v1(const char *path, uid_t uid, gid_t gid, int *error)
|
||||
{
|
||||
return sudo_secure_open(path, S_IFREG, uid, gid, error);
|
||||
}
|
||||
|
||||
int
|
||||
sudo_secure_open_dir_v1(const char *path, uid_t uid, gid_t gid, int *error)
|
||||
{
|
||||
return sudo_secure_open(path, S_IFDIR, uid, gid, error);
|
||||
}
|
||||
|
@@ -116,6 +116,7 @@ sudo_mmap_free_v1
|
||||
sudo_mmap_protect_v1
|
||||
sudo_mmap_strdup_v1
|
||||
sudo_new_key_val_v1
|
||||
sudo_open_parent_dir_v1
|
||||
sudo_parse_gids_v1
|
||||
sudo_parseln_v1
|
||||
sudo_parseln_v2
|
||||
@@ -127,6 +128,8 @@ sudo_rcstr_dup
|
||||
sudo_regex_compile_v1
|
||||
sudo_secure_dir_v1
|
||||
sudo_secure_file_v1
|
||||
sudo_secure_open_dir_v1
|
||||
sudo_secure_open_file_v1
|
||||
sudo_setgroups_v1
|
||||
sudo_str2logfac_v1
|
||||
sudo_str2logpri_v1
|
||||
|
@@ -181,66 +181,76 @@ ts_find_record(int fd, struct timestamp_entry *key, struct timestamp_entry *entr
|
||||
/*
|
||||
* Create a directory and any missing parent directories with the
|
||||
* specified mode.
|
||||
* Returns true on success.
|
||||
* Returns false on failure and displays a warning to stderr.
|
||||
* Returns an fd usable with the *at() functions on success.
|
||||
* Returns -1 on failure, setting errno.
|
||||
*/
|
||||
static bool
|
||||
static int
|
||||
ts_mkdirs(const char *path, uid_t owner, gid_t group, mode_t mode,
|
||||
mode_t parent_mode, bool quiet)
|
||||
{
|
||||
bool ret;
|
||||
int parentfd, fd = -1;
|
||||
const char *base;
|
||||
mode_t omask;
|
||||
debug_decl(ts_mkdirs, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
/* Child directory we will create. */
|
||||
base = sudo_basename(path);
|
||||
|
||||
/* umask must not be more restrictive than the file modes. */
|
||||
omask = umask(ACCESSPERMS & ~(mode|parent_mode));
|
||||
ret = sudo_mkdir_parents(path, owner, group, parent_mode, quiet);
|
||||
if (ret) {
|
||||
parentfd = sudo_open_parent_dir(path, owner, group, parent_mode, quiet);
|
||||
if (parentfd != -1) {
|
||||
/* Create final path component. */
|
||||
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
|
||||
"mkdir %s, mode 0%o, uid %d, gid %d", path, (unsigned int)mode,
|
||||
(int)owner, (int)group);
|
||||
if (mkdir(path, mode) != 0 && errno != EEXIST) {
|
||||
if (mkdirat(parentfd, base, mode) != 0 && errno != EEXIST) {
|
||||
if (!quiet)
|
||||
sudo_warn(U_("unable to mkdir %s"), path);
|
||||
ret = false;
|
||||
} else {
|
||||
if (chown(path, owner, group) != 0) {
|
||||
fd = openat(parentfd, base, O_RDONLY|O_NONBLOCK, 0);
|
||||
if (fd == -1) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
||||
"%s: unable to open %s", __func__, path);
|
||||
} else if (fchown(fd, owner, group) != 0) {
|
||||
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
|
||||
"%s: unable to chown %d:%d %s", __func__,
|
||||
(int)owner, (int)group, path);
|
||||
}
|
||||
}
|
||||
close(parentfd);
|
||||
}
|
||||
umask(omask);
|
||||
debug_return_bool(ret);
|
||||
debug_return_int(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that path is owned by timestamp_uid and not writable by
|
||||
* group or other. If path is missing and make_it is true, create
|
||||
* the directory and its parent dirs.
|
||||
* Returns true on success or false on failure, setting errno.
|
||||
*
|
||||
* Returns an fd usable with the *at() functions on success.
|
||||
* Returns -1 on failure, setting errno.
|
||||
*/
|
||||
static bool
|
||||
ts_secure_dir(char *path, bool make_it, bool quiet)
|
||||
static int
|
||||
ts_secure_opendir(const char *path, bool make_it, bool quiet)
|
||||
{
|
||||
struct stat sb;
|
||||
bool ret = false;
|
||||
debug_decl(ts_secure_dir, SUDOERS_DEBUG_AUTH);
|
||||
int error, fd = -1;
|
||||
debug_decl(ts_secure_opendir, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "checking %s", path);
|
||||
switch (sudo_secure_dir(path, timestamp_uid, -1, &sb)) {
|
||||
case SUDO_PATH_SECURE:
|
||||
ret = true;
|
||||
break;
|
||||
fd = sudo_secure_open_dir(path, timestamp_uid, timestamp_gid, &error);
|
||||
if (fd == -1) {
|
||||
switch (error) {
|
||||
case SUDO_PATH_MISSING:
|
||||
if (make_it && ts_mkdirs(path, timestamp_uid, timestamp_gid, S_IRWXU,
|
||||
S_IRWXU|S_IXGRP|S_IXOTH, quiet)) {
|
||||
ret = true;
|
||||
if (make_it) {
|
||||
fd = ts_mkdirs(path, timestamp_uid, timestamp_gid, S_IRWXU,
|
||||
S_IRWXU|S_IXGRP|S_IXOTH, quiet);
|
||||
if (fd != -1)
|
||||
break;
|
||||
}
|
||||
errno = ENOENT;
|
||||
if (!quiet)
|
||||
sudo_warn("%s", path);
|
||||
break;
|
||||
case SUDO_PATH_BAD_TYPE:
|
||||
errno = ENOTDIR;
|
||||
@@ -249,9 +259,8 @@ ts_secure_dir(char *path, bool make_it, bool quiet)
|
||||
break;
|
||||
case SUDO_PATH_WRONG_OWNER:
|
||||
if (!quiet) {
|
||||
sudo_warnx(U_("%s is owned by uid %u, should be %u"),
|
||||
path, (unsigned int) sb.st_uid,
|
||||
(unsigned int) timestamp_uid);
|
||||
sudo_warnx(U_("%s: wrong owner, should be uid %u\n"),
|
||||
path, (unsigned int)timestamp_uid);
|
||||
}
|
||||
errno = EACCES;
|
||||
break;
|
||||
@@ -260,8 +269,17 @@ ts_secure_dir(char *path, bool make_it, bool quiet)
|
||||
sudo_warnx(U_("%s is group writable"), path);
|
||||
errno = EACCES;
|
||||
break;
|
||||
default:
|
||||
if (!quiet) {
|
||||
sudo_warnx("%s: internal error, unexpected error %d",
|
||||
__func__, error);
|
||||
errno = EINVAL;
|
||||
}
|
||||
debug_return_bool(ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_int(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -271,15 +289,15 @@ ts_secure_dir(char *path, bool make_it, bool quiet)
|
||||
* Returns TIMESTAMP_OPEN_ERROR or TIMESTAMP_PERM_ERROR on error.
|
||||
*/
|
||||
static int
|
||||
ts_open(const char *path, int flags)
|
||||
ts_openat(int dfd, const char *path, int flags)
|
||||
{
|
||||
bool uid_changed = false;
|
||||
int fd;
|
||||
debug_decl(ts_open, SUDOERS_DEBUG_AUTH);
|
||||
debug_decl(ts_openat, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (timestamp_uid != 0)
|
||||
uid_changed = set_perms(PERM_TIMESTAMP);
|
||||
fd = open(path, flags, S_IRUSR|S_IWUSR);
|
||||
fd = openat(dfd, path, flags, S_IRUSR|S_IWUSR);
|
||||
if (uid_changed && !restore_perms()) {
|
||||
/* Unable to restore permissions, should not happen. */
|
||||
if (fd != -1) {
|
||||
@@ -406,7 +424,7 @@ timestamp_open(const char *user, pid_t sid)
|
||||
{
|
||||
struct ts_cookie *cookie;
|
||||
char *fname = NULL;
|
||||
int tries, fd = -1;
|
||||
int tries, dfd = -1, fd = -1;
|
||||
debug_decl(timestamp_open, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
/* Zero timeout means don't use the time stamp file. */
|
||||
@@ -416,7 +434,8 @@ timestamp_open(const char *user, pid_t sid)
|
||||
}
|
||||
|
||||
/* Check the validity of timestamp dir and create if missing. */
|
||||
if (!ts_secure_dir(def_timestampdir, true, false))
|
||||
dfd = ts_secure_opendir(def_timestampdir, true, false);
|
||||
if (dfd == -1)
|
||||
goto bad;
|
||||
|
||||
/* Open time stamp file. */
|
||||
@@ -427,7 +446,7 @@ timestamp_open(const char *user, pid_t sid)
|
||||
for (tries = 1; ; tries++) {
|
||||
struct stat sb;
|
||||
|
||||
fd = ts_open(fname, O_RDWR|O_CREAT);
|
||||
fd = ts_openat(dfd, user, O_RDWR|O_CREAT);
|
||||
switch (fd) {
|
||||
case TIMESTAMP_OPEN_ERROR:
|
||||
log_warning(SLOG_SEND_MAIL, N_("unable to open %s"), fname);
|
||||
@@ -453,7 +472,7 @@ timestamp_open(const char *user, pid_t sid)
|
||||
sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
|
||||
"removing time stamp file that predates boot time");
|
||||
close(fd);
|
||||
unlink(fname);
|
||||
unlinkat(dfd, user, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -473,9 +492,12 @@ timestamp_open(const char *user, pid_t sid)
|
||||
cookie->sid = sid;
|
||||
cookie->pos = -1;
|
||||
|
||||
close(dfd);
|
||||
debug_return_ptr(cookie);
|
||||
bad:
|
||||
if (fd != -1)
|
||||
if (dfd != -1)
|
||||
close(dfd);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
free(fname);
|
||||
debug_return_ptr(NULL);
|
||||
@@ -962,7 +984,7 @@ int
|
||||
timestamp_remove(bool unlink_it)
|
||||
{
|
||||
struct timestamp_entry key, entry;
|
||||
int fd = -1, ret = true;
|
||||
int dfd = -1, fd = -1, ret = true;
|
||||
char *fname = NULL;
|
||||
debug_decl(timestamp_remove, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
@@ -976,6 +998,13 @@ timestamp_remove(bool unlink_it)
|
||||
}
|
||||
#endif
|
||||
|
||||
dfd = open(def_timestampdir, O_RDONLY|O_NONBLOCK);
|
||||
if (dfd == -1) {
|
||||
if (errno != ENOENT)
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (asprintf(&fname, "%s/%s", def_timestampdir, user_name) == -1) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = -1;
|
||||
@@ -984,12 +1013,12 @@ timestamp_remove(bool unlink_it)
|
||||
|
||||
/* For "sudo -K" simply unlink the time stamp file. */
|
||||
if (unlink_it) {
|
||||
ret = unlink(fname) ? -1 : true;
|
||||
ret = unlinkat(dfd, user_name, 0) ? -1 : true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open time stamp file and lock it for exclusive access. */
|
||||
fd = ts_open(fname, O_RDWR);
|
||||
fd = ts_openat(dfd, user_name, O_RDWR);
|
||||
switch (fd) {
|
||||
case TIMESTAMP_OPEN_ERROR:
|
||||
if (errno != ENOENT)
|
||||
@@ -1023,7 +1052,9 @@ timestamp_remove(bool unlink_it)
|
||||
}
|
||||
|
||||
done:
|
||||
if (fd != -1)
|
||||
if (dfd != -1)
|
||||
close(dfd);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
free(fname);
|
||||
debug_return_int(ret);
|
||||
@@ -1035,21 +1066,17 @@ done:
|
||||
bool
|
||||
already_lectured(void)
|
||||
{
|
||||
char status_file[PATH_MAX];
|
||||
bool ret = false;
|
||||
struct stat sb;
|
||||
int len;
|
||||
int dfd;
|
||||
debug_decl(already_lectured, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
if (ts_secure_dir(def_lecture_status_dir, false, true)) {
|
||||
len = snprintf(status_file, sizeof(status_file), "%s/%s",
|
||||
def_lecture_status_dir, user_name);
|
||||
if (len > 0 && len < ssizeof(status_file)) {
|
||||
debug_return_bool(stat(status_file, &sb) == 0);
|
||||
dfd = ts_secure_opendir(def_lecture_status_dir, false, true);
|
||||
if (dfd != -1) {
|
||||
ret = fstatat(dfd, user_name, &sb, AT_SYMLINK_NOFOLLOW) == 0;
|
||||
close(dfd);
|
||||
}
|
||||
log_warningx(SLOG_SEND_MAIL, N_("lecture status path too long: %s/%s"),
|
||||
def_lecture_status_dir, user_name);
|
||||
}
|
||||
debug_return_bool(false);
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1059,24 +1086,16 @@ already_lectured(void)
|
||||
int
|
||||
set_lectured(void)
|
||||
{
|
||||
char lecture_status[PATH_MAX];
|
||||
int len, fd, ret = false;
|
||||
int dfd, fd, ret = false;
|
||||
debug_decl(set_lectured, SUDOERS_DEBUG_AUTH);
|
||||
|
||||
len = snprintf(lecture_status, sizeof(lecture_status), "%s/%s",
|
||||
def_lecture_status_dir, user_name);
|
||||
if (len < 0 || len >= ssizeof(lecture_status)) {
|
||||
log_warningx(SLOG_SEND_MAIL, N_("lecture status path too long: %s/%s"),
|
||||
def_lecture_status_dir, user_name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check the validity of lecture dir and create if missing. */
|
||||
if (!ts_secure_dir(def_lecture_status_dir, true, false))
|
||||
/* Check the validity of timestamp dir and create if missing. */
|
||||
dfd = ts_secure_opendir(def_lecture_status_dir, true, false);
|
||||
if (dfd == -1)
|
||||
goto done;
|
||||
|
||||
/* Create lecture file. */
|
||||
fd = ts_open(lecture_status, O_WRONLY|O_CREAT|O_EXCL);
|
||||
fd = ts_openat(dfd, user_name, O_WRONLY|O_CREAT|O_EXCL);
|
||||
switch (fd) {
|
||||
case TIMESTAMP_OPEN_ERROR:
|
||||
/* Failed to open, not a fatal error. */
|
||||
@@ -1091,6 +1110,7 @@ set_lectured(void)
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
close(dfd);
|
||||
|
||||
done:
|
||||
debug_return_int(ret);
|
||||
|
Reference in New Issue
Block a user