main: create new file when writing pidfile

When writing the pidfile, open the file with the O_CREAT|O_EXCL flags
to avoid following a symlink and writing the PID to an unexpected file,
when chronyd still has the root privileges.

The Linux open(2) man page warns about O_EXCL not working as expected on
NFS versions before 3 and Linux versions before 2.6. Saving pidfiles on
a distributed filesystem like NFS is not generally expected, but if
there is a reason to do that, these old kernel and NFS versions are not
considered to be supported for saving files by chronyd.

This is a minimal backport specific to this issue of the following
commits:
- commit 2fc8edacb8 ("use PATH_MAX")
- commit f4c6a00b2a ("logging: call exit() in LOG_Message()")
- commit 7a4c396bba ("util: add functions for common file operations")
- commit e18903a6b5 ("switch to new util file functions")

Reported-by: Matthias Gerstner <mgerstner@suse.de>
This commit is contained in:
Miroslav Lichvar 2020-08-06 09:31:11 +02:00
parent ffb9887cce
commit f00fed2009
5 changed files with 111 additions and 7 deletions

View file

@ -171,6 +171,7 @@ void LOG_Message(LOG_Severity severity,
system_log = 0;
log_message(1, severity, buf);
}
exit(1);
break;
default:
assert(0);

10
main.c
View file

@ -281,13 +281,9 @@ write_pidfile(void)
if (!pidfile[0])
return;
out = fopen(pidfile, "w");
if (!out) {
LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
} else {
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
/* ================================================== */

View file

@ -37,6 +37,7 @@
#include <glob.h>
#include <grp.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <netinet/in.h>
#include <pwd.h>

95
util.c
View file

@ -1179,6 +1179,101 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
/* ================================================== */
static int
join_path(const char *basedir, const char *name, const char *suffix,
char *buffer, size_t length, LOG_Severity severity)
{
const char *sep;
if (!basedir) {
basedir = "";
sep = "";
} else {
sep = "/";
}
if (!suffix)
suffix = "";
if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
return 0;
}
return 1;
}
/* ================================================== */
FILE *
UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
char mode, mode_t perm)
{
const char *file_mode;
char path[PATH_MAX];
LOG_Severity severity;
int fd, flags;
FILE *file;
severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
return NULL;
switch (mode) {
case 'r':
case 'R':
flags = O_RDONLY;
file_mode = "r";
if (severity != LOGS_FATAL)
severity = LOGS_DEBUG;
break;
case 'w':
case 'W':
flags = O_WRONLY | O_CREAT | O_EXCL;
file_mode = "w";
break;
case 'a':
case 'A':
flags = O_WRONLY | O_CREAT | O_APPEND;
file_mode = "a";
break;
default:
assert(0);
return NULL;
}
try_again:
fd = open(path, flags, perm);
if (fd < 0) {
if (errno == EEXIST) {
if (unlink(path) < 0) {
LOG(severity, "Could not remove %s : %s", path, strerror(errno));
return NULL;
}
DEBUG_LOG("Removed %s", path);
goto try_again;
}
LOG(severity, "Could not open %s : %s", path, strerror(errno));
return NULL;
}
UTI_FdSetCloexec(fd);
file = fdopen(fd, file_mode);
if (!file) {
LOG(severity, "Could not open %s : %s", path, strerror(errno));
close(fd);
return NULL;
}
DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
return file;
}
/* ================================================== */
void
UTI_DropRoot(uid_t uid, gid_t gid)
{

11
util.h
View file

@ -176,6 +176,17 @@ extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid
permissions and its uid/gid must match the specified values. */
extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
/* Open a file. The full path of the file is constructed from the basedir
(may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
Created files have specified permissions (umasked). Returns NULL on error.
The following modes are supported (if the mode is an uppercase character,
errors are fatal):
r/R - open an existing file for reading
w/W - open a new file for writing (remove existing file)
a/A - open an existing file for appending (create if does not exist) */
extern FILE *UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
char mode, mode_t perm);
/* Set process user/group IDs and drop supplementary groups */
extern void UTI_DropRoot(uid_t uid, gid_t gid);