Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:11.4:Update
logrotate
logrotate-3.7.9-su.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File logrotate-3.7.9-su.diff of Package logrotate
From 2ceb9af5b48bacd8c7327a8b9b1c42848c24cdd9 Mon Sep 17 00:00:00 2001 From: jkaluza <jkaluza@ec1272ba-9ed1-42ef-8245-99669996828e> Date: Wed, 9 Mar 2011 09:41:46 +0000 Subject: [PATCH logrotate] Backport svn rev 317, 358-360 (bnc#677335) - don't accept service owned log directories anymore - don't run external programs with uid != euid - switch euid back to root before running external scripts - run compressors and shred as unprivileged user - add O_NOFOLLOW when opening files as safeguard against symlink tricks - add "su" config option --- config.c | 45 ++++++++++++++++++ logrotate.8 | 5 ++ logrotate.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- logrotate.h | 3 + 4 files changed, 183 insertions(+), 13 deletions(-) diff --git a/config.c b/config.c index 652cbe9..f2ad04e 100644 --- a/config.c +++ b/config.c @@ -440,6 +440,8 @@ int readAllConfigPaths(const char **paths) .createMode = NO_MODE, .createUid = NO_UID, .createGid = NO_GID, + .suUid = 0, + .suGid = 0, .compress_options_list = NULL, .compress_options_count = 0 }; @@ -752,6 +754,49 @@ static int readConfigFile(const char *configFile, struct logInfo *defConfig) newlog->flags &= ~LOG_FLAG_MAILFIRST; *endtag = oldchar, start = endtag; + } else if (!strcmp(start, "su")) { + *endtag = oldchar, start = endtag; + + endtag = start; + while (*endtag != '\n') + endtag++; + while (isspace(*endtag)) + endtag--; + endtag++; + oldchar = *endtag, *endtag = '\0'; + + rc = sscanf(start, "%200s %200s%c", createOwner, + createGroup, &foo); + if (rc == 3) { + message(MESS_ERROR, "%s:%d extra arguments for " + "su\n", configFile, lineNum); + return 1; + } + + if (rc > 0) { + pw = getpwnam(createOwner); + if (!pw) { + message(MESS_ERROR, "%s:%d unknown user '%s'\n", + configFile, lineNum, createOwner); + return 1; + } + newlog->suUid = pw->pw_uid; + endpwent(); + } + if (rc > 1) { + group = getgrnam(createGroup); + if (!group) { + message(MESS_ERROR, "%s:%d unknown group '%s'\n", + configFile, lineNum, createGroup); + return 1; + } + newlog->suGid = group->gr_gid; + endgrent(); + } + + newlog->flags |= LOG_FLAG_SU; + + *endtag = oldchar, start = endtag; } else if (!strcmp(start, "create")) { *endtag = oldchar, start = endtag; diff --git a/logrotate.8 b/logrotate.8 index 93d5c6a..54a5460 100644 --- a/logrotate.8 +++ b/logrotate.8 @@ -460,6 +460,11 @@ be created with a .9, skipping 0-8. Files will still be rotated the number of times specified with the \fBcount\fR directive. .TP +\fBsu \fIuser\fR \fIgroup\fR +Rotate log files with the specified user and group instead of using +the default (usually root). + +.TP \fBtabooext\fR [+] \fIlist\fR The current taboo extension list is changed (see the \fBinclude\fR directive for information on the taboo extensions). If a + precedes the list of diff --git a/logrotate.c b/logrotate.c index aa405ee..5e4c27a 100644 --- a/logrotate.c +++ b/logrotate.c @@ -76,6 +76,8 @@ const char * compress_cmd_list[][2] = { }; time_t nowSecs = 0; +static uid_t save_euid; +static gid_t save_egid; static int shred_file(int fd, char *filename, struct logInfo *log); @@ -88,6 +90,48 @@ static int globerr(const char *pathname, int theerr) return 1; } +int switch_user(uid_t user, gid_t group) { + save_egid = getegid(); + save_euid = geteuid(); + if (save_euid == user && save_egid == group) + return 0; + message(MESS_DEBUG, "switching euid to %d and egid to %d\n", + user, group); + if (setegid(group) || seteuid(user)) { + message(MESS_ERROR, "error switching euid to %d and egid to %d: %s\n", + user, group, strerror(errno)); + return 1; + } + return 0; +} + +int switch_user_back() { + return switch_user(save_euid, save_egid); +} + +int switch_user_permanently(const struct logInfo *log) { + gid_t group = getegid(); + uid_t user = geteuid(); + if (!(log->flags & LOG_FLAG_SU)) { + return 0; + } + if (getuid() == user && getgid() == group) + return 0; + // switch to full root first + if (setgid(getgid()) || setuid(getuid())) { + message(MESS_ERROR, "error getting rid of euid != uid\n"); + return 1; + } + message(MESS_DEBUG, "switching uid to %d and gid to %d\n", + user, group); + if (setgid(group) || setuid(user)) { + message(MESS_ERROR, "error switching euid to %d and egid to %d: %s\n", + user, group, strerror(errno)); + return 1; + } + return 0; +} + static void unescape(char *arg) { char *p = arg; @@ -215,7 +259,7 @@ static struct logState *findState(const char *fn) return p; } -static int runScript(char *logfn, char *script) +static int runScript(struct logInfo *log, char *logfn, char *script) { int rc; @@ -226,6 +270,12 @@ static int runScript(char *logfn, char *script) } if (!fork()) { + if (log->flags & LOG_FLAG_SU) { + if (switch_user_back() != 0) { + exit(1); + } + } + execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL); exit(1); } @@ -304,6 +354,10 @@ static int shred_file(int fd, char *filename, struct logInfo *log) dup2(fd, 1); close(fd); + if (switch_user_permanently(log) != 0) { + exit(1); + } + execvp(fullCommand[0], (void *) fullCommand); exit(1); } @@ -325,7 +379,7 @@ static int removeLogFile(char *name, struct logInfo *log) int fd; message(MESS_DEBUG, "removing old log %s\n", name); - if ((fd = open(name, O_RDWR)) < 0) { + if ((fd = open(name, O_RDWR | O_NOFOLLOW)) < 0) { message(MESS_ERROR, "error opening %s: %s\n", name, strerror(errno)); return 1; @@ -366,7 +420,7 @@ static int compressLogFile(char *name, struct logInfo *log, struct stat *sb) compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2); sprintf(compressedName, "%s%s", name, log->compress_ext); - if ((inFile = open(name, O_RDWR)) < 0) { + if ((inFile = open(name, O_RDWR | O_NOFOLLOW)) < 0) { message(MESS_ERROR, "unable to open %s for compression\n", name); return 1; } @@ -384,6 +438,10 @@ static int compressLogFile(char *name, struct logInfo *log, struct stat *sb) dup2(outFile, 1); close(outFile); + if (switch_user_permanently(log) != 0) { + exit(1); + } + execvp(fullCommand[0], (void *) fullCommand); exit(1); } @@ -409,7 +467,7 @@ static int compressLogFile(char *name, struct logInfo *log, struct stat *sb) return 0; } -static int mailLog(char *logFile, char *mailCommand, +static int mailLog(const struct logInfo *log, char *logFile, char *mailCommand, char *uncompressCommand, char *address, char *subject) { int mailInput; @@ -419,7 +477,7 @@ static int mailLog(char *logFile, char *mailCommand, char *mailArgv[] = { mailCommand, "-s", subject, address, NULL }; int rc = 0; - if ((mailInput = open(logFile, O_RDONLY)) < 0) { + if ((mailInput = open(logFile, O_RDONLY | O_NOFOLLOW)) < 0) { message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile, strerror(errno)); return 1; @@ -439,6 +497,9 @@ static int mailLog(char *logFile, char *mailCommand, close(uncompressPipe[0]); close(uncompressPipe[1]); + if (switch_user_permanently(log) != 0) { + exit(1); + } execlp(uncompressCommand, uncompressCommand, NULL); exit(1); } @@ -453,6 +514,13 @@ static int mailLog(char *logFile, char *mailCommand, close(mailInput); close(1); + // mail command runs as root + if (log->flags & LOG_FLAG_SU) { + if (switch_user_back() != 0) { + exit(1); + } + } + execvp(mailArgv[0], mailArgv); exit(1); } @@ -488,12 +556,12 @@ static int mailLogWrapper(char *mailFilename, char *mailCommand, if ((log->flags & LOG_FLAG_COMPRESS) && !((log->flags & LOG_FLAG_DELAYCOMPRESS) && (log->flags & LOG_FLAG_MAILFIRST))) { - if (mailLog(mailFilename, mailCommand, + if (mailLog(log, mailFilename, mailCommand, log->uncompress_prog, log->logAddress, log->files[logNum])) return 1; } else { - if (mailLog(mailFilename, mailCommand, NULL, + if (mailLog(log, mailFilename, mailCommand, NULL, log->logAddress, mailFilename)) return 1; } @@ -510,7 +578,7 @@ static int copyTruncate(char *currLog, char *saveLog, struct stat *sb, message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog); if (!debug) { - if ((fdcurr = open(currLog, (flags & LOG_FLAG_COPY) ? O_RDONLY : O_RDWR)) < 0) { + if ((fdcurr = open(currLog, ((flags & LOG_FLAG_COPY) ? O_RDONLY : O_RDWR) | O_NOFOLLOW)) < 0) { message(MESS_ERROR, "error opening %s: %s\n", currLog, strerror(errno)); return 1; @@ -608,6 +676,36 @@ int findNeedRotating(struct logInfo *log, int logNum) message(MESS_DEBUG, "considering log %s\n", log->files[logNum]); + /* Check if parent directory of this log has safe permissions */ + if ((log->flags & LOG_FLAG_SU) == 0 && getuid() == 0) { + char *ld = ourDirName(log->files[logNum]); + if (stat(ld, &sb)) { + /* If parent directory doesn't exist, it's not real error + and rotation is not needed */ + if (errno != ENOENT) { + message(MESS_ERROR, "stat of %s failed: %s\n", ld, + strerror(errno)); + free(ld); + return 1; + } + free(ld); + return 0; + } + /* Don't rotate in directories writable by others or group which is not "root" */ + if (sb.st_uid != 0 || (sb.st_gid != 0 && sb.st_mode & S_IWGRP) || sb.st_mode & S_IWOTH) { + message(MESS_ERROR, "\"%s\" has insecure permissions." + " It must be owned and be writable by root only to avoid security problems." + " Set the \"su\" directive in the config file to tell logrotate which user/group" + " should be used for rotation.\n" + , ld); +#if 0 // accept nevertheless for now + free(ld); + return 0; +#endif + } + free(ld); + } + if (stat(log->files[logNum], &sb)) { if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) { message(MESS_DEBUG, " log %s does not exist -- skipping\n", @@ -1114,7 +1212,7 @@ int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state, security_context_t oldContext = NULL; int fdcurr = -1; - if ((fdcurr = open(log->files[logNum], O_RDWR)) < 0) { + if ((fdcurr = open(log->files[logNum], O_RDWR | O_NOFOLLOW)) < 0) { message(MESS_ERROR, "error opening %s: %s\n", log->files[logNum], strerror(errno)); @@ -1333,6 +1431,12 @@ int rotateLogSet(struct logInfo *log, int force) message(MESS_DEBUG, "old logs are removed\n"); } + if (log->flags & LOG_FLAG_SU) { + if (switch_user(log->suUid, log->suGid) != 0) { + return 1; + } + } + for (i = 0; i < log->numFiles; i++) { logHasErrors[i] = findNeedRotating(log, i); hasErrors |= logHasErrors[i]; @@ -1348,10 +1452,17 @@ int rotateLogSet(struct logInfo *log, int force) "since no logs will be rotated\n"); } else { message(MESS_DEBUG, "running first action script\n"); - if (runScript(log->pattern, log->first)) { + if (runScript(log, log->pattern, log->first)) { message(MESS_ERROR, "error running first action script " "for %s\n", log->pattern); hasErrors = 1; + + if (log->flags & LOG_FLAG_SU) { + if (switch_user_back() != 0) { + return 1; + } + } + /* finish early, firstaction failed, affects all logs in set */ return hasErrors; } @@ -1386,7 +1497,7 @@ int rotateLogSet(struct logInfo *log, int force) "since no logs will be rotated\n"); } else { message(MESS_DEBUG, "running prerotate script\n"); - if (runScript(log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->pre)) { + if (runScript(log, log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->pre)) { if (log->flags & LOG_FLAG_SHAREDSCRIPTS) message(MESS_ERROR, "error running shared prerotate script " @@ -1421,7 +1532,7 @@ int rotateLogSet(struct logInfo *log, int force) "since no logs were rotated\n"); } else { message(MESS_DEBUG, "running postrotate script\n"); - if (runScript(log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->post)) { + if (runScript(log, log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->post)) { if (log->flags & LOG_FLAG_SHAREDSCRIPTS) message(MESS_ERROR, "error running shared postrotate script " @@ -1467,7 +1578,7 @@ int rotateLogSet(struct logInfo *log, int force) "since no logs will be rotated\n"); } else { message(MESS_DEBUG, "running last action script\n"); - if (runScript(log->pattern, log->last)) { + if (runScript(log, log->pattern, log->last)) { message(MESS_ERROR, "error running last action script " "for %s\n", log->pattern); hasErrors = 1; @@ -1475,6 +1586,12 @@ int rotateLogSet(struct logInfo *log, int force) } } + if (log->flags & LOG_FLAG_SU) { + if (switch_user_back() != 0) { + return 1; + } + } + return hasErrors; } diff --git a/logrotate.h b/logrotate.h index 24e3ec0..1ac94b3 100644 --- a/logrotate.h +++ b/logrotate.h @@ -18,6 +18,7 @@ #define LOG_FLAG_COPY (1 << 8) #define LOG_FLAG_DATEEXT (1 << 9) #define LOG_FLAG_SHRED (1 << 10) +#define LOG_FLAG_SU (1 << 11) #define NO_MODE ((mode_t) -1) #define NO_UID ((uid_t) -1) @@ -51,6 +52,8 @@ struct logInfo { mode_t createMode; /* if any/all of these are -1, we use the */ uid_t createUid; /* attributes from the log file just rotated */ gid_t createGid; + uid_t suUid; /* switch user to this uid and group to this gid */ + gid_t suGid; /* these are at the end so they end up nil */ const char **compress_options_list; int compress_options_count; -- 1.7.7
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor