Sanitize leading dots from log path components

Prevent directory traversal by sanitizing leading dots as well as
slashes from log path components, which can be controlled by the
server. Side effect of preventing hidden dotfiles is a bonus, I
think.

Also check that the full path actually fits in the buffer.

Reported-by: Samanta Navarro <ferivoz@riseup.net>
weechat-hashes
June McEnroe 2022-04-20 18:29:28 -04:00
parent 70268b4cd3
commit 1b8be724bc
1 changed files with 19 additions and 11 deletions

30
log.c
View File

@ -73,6 +73,15 @@ static void logMkdir(const char *path) {
if (error && errno != EEXIST) err(EX_CANTCREAT, "log/%s", path); if (error && errno != EEXIST) err(EX_CANTCREAT, "log/%s", path);
} }
static void sanitize(char *ptr, char *end) {
for (char *ch = ptr; ch < end && *ch == '.'; ++ch) {
*ch = '_';
}
for (char *ch = ptr; ch < end; ++ch) {
if (*ch == '/') *ch = '_';
}
}
static struct { static struct {
int year; int year;
int month; int month;
@ -97,22 +106,21 @@ static FILE *logFile(uint id, const struct tm *tm) {
logs[id].month = tm->tm_mon; logs[id].month = tm->tm_mon;
logs[id].day = tm->tm_mday; logs[id].day = tm->tm_mday;
size_t len = 0;
char path[PATH_MAX]; char path[PATH_MAX];
for (const char *ch = network.name; *ch; ++ch) { char *ptr = path, *end = &path[sizeof(path)];
path[len++] = (*ch == '/' ? '_' : *ch);
} ptr = seprintf(ptr, end, "%s", network.name);
path[len] = '\0'; sanitize(path, ptr);
logMkdir(path); logMkdir(path);
path[len++] = '/'; char *name = ptr;
for (const char *ch = idNames[id]; *ch; ++ch) { ptr = seprintf(ptr, end, "/%s", idNames[id]);
path[len++] = (*ch == '/' ? '_' : *ch); sanitize(&name[1], ptr);
}
path[len] = '\0';
logMkdir(path); logMkdir(path);
strftime(&path[len], sizeof(path) - len, "/%F.log", tm); size_t len = strftime(ptr, end - ptr, "/%F.log", tm);
if (!len) errx(EX_CANTCREAT, "log path too long");
int fd = openat( int fd = openat(
logDir, path, logDir, path,
O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,