Skip to content

Commit

Permalink
When opening files for write, ensure they aren't symbolic links
Browse files Browse the repository at this point in the history
Issue #1048 identified that if, for example, a non privileged user
created a symbolic link from /etc/keepalvied.data to /etc/passwd,
writing to /etc/keepalived.data (which could be invoked via DBus)
would cause /etc/passwd to be overwritten.

This commit stops keepalived writing to pathnames where the ultimate
component is a symbolic link, by setting O_NOFOLLOW whenever opening
a file for writing.

This might break some setups, where, for example, /etc/keepalived.data
was a symbolic link to /home/fred/keepalived.data. If this was the case,
instead create a symbolic link from /home/fred/keepalived.data to
/tmp/keepalived.data, so that the file is still accessible via
/home/fred/keepalived.data.

There doesn't appear to be a way around this backward incompatibility,
since even checking if the pathname is a symbolic link prior to opening
for writing would create a race condition.

Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
  • Loading branch information
pqarmitage committed Oct 31, 2018
1 parent 5241e4d commit 04f2d32
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 23 deletions.
2 changes: 1 addition & 1 deletion keepalived/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,7 @@ parse_cmdline(int argc, char **argv)
__set_bit(DONT_FORK_BIT, &debug);
__set_bit(NO_SYSLOG_BIT, &debug);
if (optarg && optarg[0]) {
int fd = open(optarg, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int fd = open(optarg, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd == -1) {
fprintf(stderr, "Unable to open config-test log file %s\n", optarg);
exit(EXIT_FAILURE);
Expand Down
2 changes: 1 addition & 1 deletion keepalived/core/pidfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ int
pidfile_write(const char *pid_file, int pid)
{
FILE *pidfile = NULL;
int pidfd = creat(pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int pidfd = open(pid_file, O_NOFOLLOW | O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

if (pidfd != -1) pidfile = fdopen(pidfd, "w");

Expand Down
2 changes: 1 addition & 1 deletion keepalived/core/smtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ smtp_connect(smtp_t * smtp)
static void
smtp_log_to_file(smtp_t *smtp)
{
FILE *fp = fopen("/tmp/smtp-alert.log", "a");
FILE *fp = fopen_safe("/tmp/smtp-alert.log", "a");
time_t now;
struct tm tm;
char time_buf[25];
Expand Down
2 changes: 1 addition & 1 deletion keepalived/vrrp/vrrp_dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ read_file(gchar* filepath)
size_t length;
gchar *ret = NULL;

f = fopen(filepath, "rb");
f = fopen(filepath, "r");
if (f) {
fseek(f, 0, SEEK_END);
length = (size_t)ftell(f);
Expand Down
3 changes: 2 additions & 1 deletion keepalived/vrrp/vrrp_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "vrrp_iprule.h"
#include "logger.h"
#include "timer.h"
#include "utils.h"

static inline double
timeval_to_double(const timeval_t *t)
Expand All @@ -55,7 +56,7 @@ vrrp_print_json(void)
if (LIST_ISEMPTY(vrrp_data->vrrp))
return;

file = fopen ("/tmp/keepalived.json","w");
file = fopen_safe("/tmp/keepalived.json", "w");
if (!file) {
log_message(LOG_INFO, "Can't open /tmp/keepalived.json (%d: %s)",
errno, strerror(errno));
Expand Down
2 changes: 1 addition & 1 deletion keepalived/vrrp/vrrp_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ vrrp_tfile_end_handler(void)

if (!__test_bit(CONFIG_TEST_BIT, &debug)) {
/* Write the value to the file */
if ((tf = fopen(tfile->file_path, "w"))) {
if ((tf = fopen_safe(tfile->file_path, "w"))) {
fprintf(tf, "%d\n", track_file_init_value);
fclose(tf);
}
Expand Down
17 changes: 7 additions & 10 deletions keepalived/vrrp/vrrp_print.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@
#include "vrrp.h"
#include "vrrp_data.h"
#include "vrrp_print.h"
#include "utils.h"

static const char *dump_file = "/tmp/keepalived.data";
static const char *stats_file = "/tmp/keepalived.stats";

void
vrrp_print_data(void)
{
FILE *file = fopen (dump_file, "w");
FILE *file = fopen_safe(dump_file, "w");

if (!file) {
log_message(LOG_INFO, "Can't open %s (%d: %s)",
Expand All @@ -55,28 +56,24 @@ vrrp_print_data(void)
void
vrrp_print_stats(void)
{
FILE *file;
file = fopen (stats_file, "w");
FILE *file = fopen_safe(stats_file, "w");
element e;
vrrp_t *vrrp;

if (!file) {
log_message(LOG_INFO, "Can't open %s (%d: %s)",
stats_file, errno, strerror(errno));
return;
}

list l = vrrp_data->vrrp;
element e;
vrrp_t *vrrp;

for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) {
vrrp = ELEMENT_DATA(e);
LIST_FOREACH(vrrp_data->vrrp, vrrp, e) {
fprintf(file, "VRRP Instance: %s\n", vrrp->iname);
fprintf(file, " Advertisements:\n");
fprintf(file, " Received: %" PRIu64 "\n", vrrp->stats->advert_rcvd);
fprintf(file, " Sent: %d\n", vrrp->stats->advert_sent);
fprintf(file, " Became master: %d\n", vrrp->stats->become_master);
fprintf(file, " Released master: %d\n",
vrrp->stats->release_master);
fprintf(file, " Released master: %d\n", vrrp->stats->release_master);
fprintf(file, " Packet Errors:\n");
fprintf(file, " Length: %" PRIu64 "\n", vrrp->stats->packet_len_err);
fprintf(file, " TTL: %" PRIu64 "\n", vrrp->stats->ip_ttl_err);
Expand Down
2 changes: 1 addition & 1 deletion keepalived/vrrp/vrrp_scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,7 @@ dump_threads(void)
NULL,
#endif
global_data->instance_name);
fp = fopen(file_name, "a");
fp = fopen_safe(file_name, "a");
FREE(file_name);

set_time_now();
Expand Down
2 changes: 1 addition & 1 deletion lib/logger.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ open_log_file(const char *name, const char *prog, const char *namespace, const c

file_name = make_file_name(name, prog, namespace, instance);

log_file = fopen(file_name, "a");
log_file = fopen_safe(file_name, "a");
if (log_file) {
int n = fileno(log_file);
fcntl(n, F_SETFD, FD_CLOEXEC | fcntl(n, F_GETFD));
Expand Down
2 changes: 1 addition & 1 deletion lib/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ mem_log_init(const char* prog_name, const char *banner)
}

snprintf(log_name, log_name_len, "/tmp/%s_mem.%d.log", prog_name, getpid());
log_op = fopen(log_name, "a");
log_op = fopen_safe(log_name, "a");
if (log_op == NULL) {
log_message(LOG_INFO, "Unable to open %s for appending", log_name);
log_op = stderr;
Expand Down
2 changes: 1 addition & 1 deletion lib/notify.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ fifo_open(notify_fifo_t* fifo, int (*script_exit)(thread_t *), const char *type)
notify_fifo_exec(master, script_exit, fifo, fifo->script);

/* Now open the fifo */
if ((fifo->fd = open(fifo->name, O_RDWR | O_CLOEXEC | O_NONBLOCK)) == -1) {
if ((fifo->fd = open(fifo->name, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOFOLLOW)) == -1) {
log_message(LOG_INFO, "Unable to open %snotify fifo %s - errno %d", type, fifo->name, errno);
if (fifo->created_fifo) {
unlink(fifo->name);
Expand Down
4 changes: 2 additions & 2 deletions lib/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,11 @@ dump_keywords(vector_t *keydump, int level, FILE *fp)
{
unsigned int i;
keyword_t *keyword_vec;
char file_name[21];
char file_name[22];

if (!level) {
snprintf(file_name, sizeof(file_name), "/tmp/keywords.%d", getpid());
fp = fopen(file_name, "w");
fp = fopen_safe(file_name, "w");
if (!fp)
return;
}
Expand Down
43 changes: 42 additions & 1 deletion lib/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ write_stacktrace(const char *file_name, const char *str)

nptrs = backtrace(buffer, 100);
if (file_name) {
fd = open(file_name, O_WRONLY | O_APPEND | O_CREAT, 0644);
fd = open(file_name, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (str)
dprintf(fd, "%s\n", str);
backtrace_symbols_fd(buffer, nptrs, fd);
Expand Down Expand Up @@ -788,6 +788,47 @@ string_equal(const char *str1, const char *str2)
return !strcmp(str1, str2);
}

/* We need to use O_NOFOLLOW if opening a file for write, so that a non privileged user can't
* create a symbolic link from the path to a system file and cause a system file to be overwritten. */
FILE *fopen_safe(const char *path, const char *mode)
{
int fd;
FILE *file;
int flags = O_NOFOLLOW | O_CREAT;

if (mode[0] == 'r')
return fopen(path, mode);

if (mode[0] != 'a' && mode[0] != 'w')
return NULL;

if (mode[1] &&
(mode[1] != '+' || mode[2]))
return NULL;

if (mode[0] == 'w')
flags |= O_TRUNC;
else
flags |= O_APPEND;

if (mode[1])
flags |= O_RDWR;
else
flags |= O_WRONLY;

fd = open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd == -1)
return NULL;

file = fdopen (fd, "w");
if (!file) {
close(fd);
return NULL;
}

return file;
}

void
set_std_fd(bool force)
{
Expand Down
1 change: 1 addition & 0 deletions lib/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ extern int inet_inaddrcmp(int, const void *, const void *);
extern int inet_sockaddrcmp(const struct sockaddr_storage *, const struct sockaddr_storage *);
extern char *get_local_name(void);
extern bool string_equal(const char *, const char *);
extern FILE *fopen_safe(const char *, const char *);
extern void set_std_fd(bool);
extern void close_std_fd(void);
#if !defined _HAVE_LIBIPTC_ || defined _LIBIPTC_DYNAMIC_
Expand Down

0 comments on commit 04f2d32

Please sign in to comment.