You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

780 lines
24 KiB

/*
* Soapbox - A way to deny processes to write files outside some directories
*
* Copyright (C) 2003 by Dag Wieers <dag@wieers.com>
* Copyright (C) 2012 by Ralf S. Engelschall <rse@engelschall.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utime.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#ifdef __FreeBSD__
#include <libgen.h>
#endif
/*
* ==== GLOBAL VARIABLES ====
*/
static int (*sb_real_chmod) (const char *, mode_t);
static int (*sb_real_chown) (const char *, uid_t, gid_t);
static int (*sb_real_lchown) (const char *, uid_t, gid_t);
static int (*sb_real_link) (const char *, const char *);
static int (*sb_real_mkdir) (const char *, mode_t);
static int (*sb_real_mkfifo) (const char *, mode_t);
static int (*sb_real_mknod) (const char *, mode_t, dev_t);
static int (*sb_real___xmknod)(int, const char *, mode_t, dev_t *);
static int (*sb_real_open) (const char *, int, ...);
static int (*sb_real_open64) (const char *, int, ...);
static int (*sb_real_creat) (const char *, mode_t);
static int (*sb_real_creat64) (const char *, mode_t);
static int (*sb_real_remove) (const char *);
static int (*sb_real_rename) (const char *, const char *);
static int (*sb_real_rmdir) (const char *);
static int (*sb_real_symlink) (const char *, const char *);
static int (*sb_real_unlink) (const char *);
static int (*sb_real_utime) (const char *, const struct utimbuf *);
static int (*sb_real_utimes) (const char *, const struct timeval *);
#define SB_PATHSEP ":,;|"
enum { SB_R_FILE, SB_R_LINK };
enum { SB_A_UNKN, SB_A_WARN, SB_A_ERR, SB_A_HALT };
#define SB_A_DEFAULT SB_A_WARN
#define SB_A_DEFAULT_NAME "warn"
static char *sb_path;
static int sb_devnull;
static int sb_action = SB_A_UNKN;
static int sb_debug = 0;
static FILE *sb_logfp;
/*
* ==== UTILITY FUNCTIONS ====
*/
static void sb_die(FILE *out, const char *str, ...) {
va_list argptr;
fprintf(out, "soapbox: ");
va_start(argptr, str);
vfprintf(out, str, argptr);
va_end(argptr);
fprintf(out, "\n");
exit(-1);
}
static void sb_log(int level, const char *str, ...) {
va_list argptr;
if (sb_debug & level || level == 0) {
if (level != 0)
fprintf(sb_logfp, "soapbox: debug%i: ", level);
else
fprintf(sb_logfp, "soapbox: ");
va_start(argptr, str);
vfprintf(sb_logfp, str, argptr);
va_end(argptr);
fprintf(sb_logfp, "\n");
}
}
static void sb_dlcheck(const char *err) {
if (err != NULL)
sb_die(sb_logfp, "%s", err);
}
/*
* ==== DSO MANAGEMENT ====
*/
/* DSO initialization */
void _init(int argc, char *argv[]) {
char *soapboxaction;
char *soapboxdbg;
char *soapboxlog;
/* make output unbuffered */
setvbuf(stdout, (char *)NULL, _IONBF, 0);
setvbuf(stderr, (char *)NULL, _IONBF, 0);
/* handle and unset logging environment variable */
soapboxlog = getenv("SOAPBOXLOG");
unsetenv("SOAPBOXLOG");
if (soapboxlog == NULL || *soapboxlog == '\0') {
sb_logfp = stderr;
}
else {
sb_logfp = fopen(soapboxlog, "a");
if (sb_logfp == NULL) {
sb_logfp = stderr;
sb_die(sb_logfp, "%s: %s (%i)", soapboxlog, strerror(errno), errno);
}
setvbuf(sb_logfp, (char *)NULL, _IONBF, 0);
}
/* handle and unset debugging environment variable */
soapboxdbg = getenv("SOAPBOXDEBUG");
unsetenv("SOAPBOXDEBUG");
if (soapboxdbg != NULL)
sb_debug = atoi(soapboxdbg);
sb_log(8, "Variable SOAPBOXDEBUG is set to %i.", sb_debug);
/* handle and unset path environment variable */
sb_path = getenv("SOAPBOXPATH");
unsetenv("SOAPBOXPATH");
if (sb_path == NULL)
sb_path = "";
if (sb_path == NULL || *sb_path=='\0')
sb_log(8, "Variable SOAPBOXPATH is not set. Not allowed to write anywhere.");
else
sb_log(8, "Variable SOAPBOXPATH is set to \"%s\".", sb_path);
/* handle and unset action environment variable */
soapboxaction = getenv("SOAPBOXACTION");
unsetenv("SOAPBOXACTION");
if (soapboxaction == NULL || *soapboxaction == '\0') {
soapboxaction = SB_A_DEFAULT_NAME;
sb_log(8, "Variable SOAPBOXACTION is not set. Using \"%s\" by default.", SB_A_DEFAULT_NAME);
}
else
sb_log(8, "Variable SOAPBOXACTION is set to \"%s\".", soapboxaction);
if (strcmp(soapboxaction, "warn") == 0)
sb_action = SB_A_WARN;
else if (strcmp(soapboxaction, "err") == 0)
sb_action = SB_A_ERR;
else if (strcmp(soapboxaction, "halt") == 0)
sb_action = SB_A_HALT;
if (sb_action == SB_A_UNKN) {
sb_log(8, "Variable SOAPBOXACTION=\"%s\" is unknown. Using \"%s\" by default.", soapboxaction, SB_A_DEFAULT_NAME);
sb_action = SB_A_DEFAULT;
}
/* resolve real functions from libc */
sb_real_chmod = dlsym(RTLD_NEXT, "chmod"); sb_dlcheck(dlerror());
sb_real_chown = dlsym(RTLD_NEXT, "chown"); sb_dlcheck(dlerror());
sb_real_lchown = dlsym(RTLD_NEXT, "lchown"); sb_dlcheck(dlerror());
sb_real_link = dlsym(RTLD_NEXT, "link"); sb_dlcheck(dlerror());
sb_real_mkdir = dlsym(RTLD_NEXT, "mkdir"); sb_dlcheck(dlerror());
sb_real_mkfifo = dlsym(RTLD_NEXT, "mkfifo"); sb_dlcheck(dlerror());
sb_real_mknod = dlsym(RTLD_NEXT, "mknod"); sb_dlcheck(dlerror());
#ifdef __linux__
sb_real___xmknod = dlsym(RTLD_NEXT, "__xmknod"); sb_dlcheck(dlerror());
#endif
sb_real_open = dlsym(RTLD_NEXT, "open"); sb_dlcheck(dlerror());
#ifdef __linux__
sb_real_open64 = dlsym(RTLD_NEXT, "open64"); sb_dlcheck(dlerror());
#endif
sb_real_creat = dlsym(RTLD_NEXT, "creat"); sb_dlcheck(dlerror());
#ifdef __linux__
sb_real_creat64 = dlsym(RTLD_NEXT, "creat64"); sb_dlcheck(dlerror());
#endif
sb_real_remove = dlsym(RTLD_NEXT, "remove"); sb_dlcheck(dlerror());
sb_real_rename = dlsym(RTLD_NEXT, "rename"); sb_dlcheck(dlerror());
sb_real_rmdir = dlsym(RTLD_NEXT, "rmdir"); sb_dlcheck(dlerror());
sb_real_symlink = dlsym(RTLD_NEXT, "symlink"); sb_dlcheck(dlerror());
sb_real_unlink = dlsym(RTLD_NEXT, "unlink"); sb_dlcheck(dlerror());
sb_real_utime = dlsym(RTLD_NEXT, "utime"); sb_dlcheck(dlerror());
sb_real_utimes = dlsym(RTLD_NEXT, "utimes"); sb_dlcheck(dlerror());
/* open /dev/null for reuse */
sb_devnull = sb_real_open("/dev/null", O_RDWR);
if (sb_devnull == -1)
sb_die(sb_logfp, "/dev/null: %s", strerror(errno));
}
/* DSO finish */
void _fini(void) {
close(sb_devnull);
if (sb_logfp != stderr)
fclose(sb_logfp);
}
/*
* ==== HELPER FUNCTIONS ====
*/
static char *sb_dirname(const char *path) {
char *ptr;
char safe[PATH_MAX+1];
safe[0]='\0'; safe[PATH_MAX]='\0';
if (strrchr(path, '/') == NULL)
getcwd(safe, PATH_MAX);
else {
snprintf(safe, PATH_MAX, "%s", path);
ptr = strrchr(safe, '/');
*ptr = '\0';
}
return strndup(safe, strlen(safe));
}
/* sb_rewrite relative path to absolute. */
static char *sb_rewrite(const char *path, const int flag) {
char *linkdir, *out;
struct stat *buf;
char safe[PATH_MAX+1];
/* terminate string for safety */
safe[0] = '\0';
safe[PATH_MAX] = '\0';
/* to make sure path is not empty and defined. Return empty string */
if (path == NULL || *path == '\0')
return strndup(safe, 0);
/* check if file exists */
buf = malloc(sizeof(struct stat));
if (lstat(path, buf) == 0) {
/* case 1: file exists */
int type = (buf->st_mode & 0170000);
int mode = (buf->st_mode & 07777);
if (sb_debug & 8) {
switch (type) {
case S_IFLNK: sb_log(8, "File \"%s\" is a symlink. (%04o)", path, mode); break;;
case S_IFREG: sb_log(8, "File \"%s\" is a regular file. (%04o)", path, mode); break;;
case S_IFDIR: sb_log(8, "File \"%s\" is a directory. (%04o)", path, mode); break;;
case S_IFCHR: sb_log(8, "File \"%s\" is a character device. (%04o)", path, mode); break;;
case S_IFBLK: sb_log(8, "File \"%s\" is a block device. (%04o)", path, mode); break;;
default: sb_log(8, "File \"%s\" is an unknown file type. (%04o)", path, mode); break;;
}
}
/* resolve path */
if ((type == S_IFLNK) && (flag == SB_R_LINK)) {
/* if it is a symlink and if asked (SB_R_LINK), we should make its
dirname absolute (not the symlink) and add its basename */
linkdir = sb_rewrite(sb_dirname(path), SB_R_LINK);
snprintf(safe, PATH_MAX, "%s/%s", linkdir, basename(path));
free(linkdir);
}
else
realpath(path, safe);
}
else {
/* case 2: file not existing */
sb_log(8, "File \"%s\" does not exist.", path);
realpath(path, safe); /* TODO: realpath(3) resolves not correctly for not existing path?! */
}
free(buf);
out = strndup(safe, strlen(safe));
if (strcmp(path, out) != 0)
sb_log(4, "File \"%s\" is actually \"%s\".", path, out);
return out;
}
/* verify if program has access to a path. */
static int sb_has_access(char *path) {
int found = 0;
char *pathlist;
char *curpath;
pathlist = strndup(sb_path, strlen(sb_path));
curpath = strtok(pathlist, SB_PATHSEP);
while (curpath != NULL && !found) {
if (curpath != '\0' && strstr(path, curpath) == path) {
found = 1;
sb_log(4, "Allow access to \"%s\" (in \"%s\").", path, curpath);
}
curpath = strtok(NULL, SB_PATHSEP);
}
free(pathlist);
return found;
}
static void sb_str_cmode(char *str, const char *cmode) {
char *temp;
temp = strndup(str, strlen(str));
if (strlen(str) != 0)
snprintf(str, PATH_MAX, "%s|%s", temp, cmode);
else
snprintf(str, PATH_MAX, "%s%s", temp, cmode);
free(temp);
}
static char *sb_str_flags(const int flags) {
char str[PATH_MAX+1];
/* terminate string for safety */
str[0] = '\0';
str[PATH_MAX] = '\0';
if (flags & O_WRONLY) sb_str_cmode(str, "O_WRONLY");
else if (flags & O_RDWR) sb_str_cmode(str, "O_RDWR");
else sb_str_cmode(str, "O_RDONLY");
if (flags & O_EXCL) sb_str_cmode(str, "O_EXCL");
if (flags & O_TRUNC) sb_str_cmode(str, "O_TRUNC");
if (flags & O_APPEND) sb_str_cmode(str, "O_APPEND");
if (flags & O_NONBLOCK) sb_str_cmode(str, "O_NONBLOCK");
if (flags & O_CREAT) sb_str_cmode(str, "O_CREAT");
if (flags & O_NOCTTY) sb_str_cmode(str, "O_NOCTTY");
if (flags & O_SYNC) sb_str_cmode(str, "O_SYNC");
if (flags & O_NOFOLLOW) sb_str_cmode(str, "O_NOFOLLOW");
if (flags & O_DIRECT) sb_str_cmode(str, "O_DIRECT");
if (flags & O_ASYNC) sb_str_cmode(str, "O_ASYNC");
#ifdef O_LARGEFILE
if (flags & O_LARGEFILE) sb_str_cmode(str, "O_LARGEFILE");
#endif
if (flags & O_DIRECTORY) sb_str_cmode(str, "O_DIRECTORY");
return strndup(str, strlen(str));
}
static char *str_timet(time_t time) {
struct tm *t;
char str[PATH_MAX+1];
t = localtime(&time);
str[0] = '\0';
str[PATH_MAX] = '\0';
sprintf(str, "%04i/%02i/%02i-%02i:%02i:%02i",
t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
free(t);
return strndup(str, strlen(str));
}
static char *str_utimbuf(const struct utimbuf *buf) {
char str[PATH_MAX+1];
str[0] = '\0';
str[PATH_MAX] = '\0';
if (buf == NULL)
sprintf(str, "NULL");
else
sprintf(str, "[%s, %s]", str_timet(buf->actime), str_timet(buf->modtime));
return strndup(str, strlen(str));
}
/*
* ==== OVERLOADED LIBC FUNCTIONS ====
*/
int chmod(const char *path, mode_t mode) {
char *rpath;
sb_log(2, "Start chmod(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do chmod(\"%s\", %04o).", path, mode);
return sb_real_chmod(path, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to chmod(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int chown(const char *path, uid_t owner, gid_t group) {
char *rpath;
sb_log(2, "Start chown(\"%s\", %i, %i).", path, owner, group);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do chown(\"%s\", %i, %i).", path, owner, group);
return sb_real_chown(path, owner, group);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to chown(\"%s\", %i, %i).", rpath, owner, group);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int lchown(const char *path, uid_t owner, gid_t group) {
char *rpath;
sb_log(2, "Start lchown(\"%s\", %i, %i).", path, owner, group);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do lchown(\"%s\", %i, %i).", path, owner, group);
return sb_real_lchown(path, owner, group);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to lchown(\"%s\", %i, %i).", rpath, owner, group);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int link(const char *oldpath, const char *newpath) {
char *oldrpath, *newrpath;
sb_log(2, "Start link(\"%s\", \"%s\").", oldpath, newpath);
oldrpath = sb_rewrite(oldpath, SB_R_FILE);
newrpath = sb_rewrite(newpath, SB_R_FILE);
if (sb_has_access(oldrpath) && sb_has_access(newrpath)) {
sb_log(4, "Do link(\"%s\", \"%s\").", oldpath, newpath);
return sb_real_link(oldpath, newpath);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to link(\"%s\", \"%s\").", oldrpath, newrpath);
free(oldrpath);
free(newrpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int mkdir(const char *path, mode_t mode) {
char *rpath;
sb_log(2, "Start mkdir(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do mkdir(\"%s\", %04o).", path, mode);
return sb_real_mkdir(path, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to mkdir(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int mkfifo(const char *path, mode_t mode) {
char *rpath;
sb_log(2, "Start mkfifo(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do mkfifo(\"%s\", %04o).", path, mode);
return sb_real_mkfifo(path, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to mkfifo(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int mknod(const char *path, mode_t mode, dev_t dev) {
char *rpath;
sb_log(2, "Start mknod(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do mknod(\"%s\", %04o).", path, mode);
return sb_real_mknod(path, mode, dev);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to mknod(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev) {
char *rpath;
sb_log(2, "Start __xmknod(%i, \"%s\", %04o).", ver, path, mode);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do __xmknod(%i, \"%s\", %04o).", ver, path, mode);
return sb_real___xmknod(ver, path, mode, dev);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to __xmknod(%i, \"%s\", %04o).", ver, rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int open(const char *path, int flags, ...) {
char *rpath;
char *strflags;
va_list argptr;
mode_t mode;
int found;
strflags = sb_str_flags(flags);
if (flags & O_CREAT) {
va_start(argptr, flags);
mode = (mode_t)va_arg(argptr, int);
va_end(argptr);
}
else
mode = 0;
sb_log(2, "Start open(\"%s\", %s, %04o).", path, strflags, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if ((found = sb_has_access(rpath)) || !(flags & (O_WRONLY|O_RDWR))) {
/* disable O_CREAT when O_RDONLY */
if (!found) {
flags &= ~O_CREAT;
strflags = sb_str_flags(flags);
}
sb_log(4, "Do open(\"%s\", %s, %04o).", path, strflags, mode);
return sb_real_open(path, flags, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to open(\"%s\", %s, %04o).", rpath, strflags, mode);
free(rpath);
free(strflags);
if (sb_action == SB_A_WARN)
return sb_devnull;
errno = EACCES;
return -1;
}
int open64(const char *path, int flags, ...) {
char *rpath;
char *strflags;
va_list argptr;
mode_t mode;
int found;
strflags=sb_str_flags(flags);
if (flags & O_CREAT) {
va_start(argptr, flags);
mode = (mode_t)va_arg(argptr, int);
va_end(argptr);
}
else
mode = 0;
sb_log(2, "Start open64(\"%s\", %s, %04o).", path, strflags, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if ((found = sb_has_access(rpath)) || !(flags & (O_WRONLY|O_RDWR))) {
/* disable O_CREAT when O_RDONLY */
if (!found) {
flags &= ~O_CREAT;
strflags = sb_str_flags(flags);
}
sb_log(4, "Do open64(\"%s\", %s, %04o).", path, strflags, mode);
return sb_real_open64(path, flags, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to open64(\"%s\", %s, %04o).", rpath, strflags, mode);
free(rpath);
free(strflags);
if (sb_action == SB_A_WARN)
return sb_devnull;
errno = EACCES;
return -1;
}
int creat(const char *path, mode_t mode) {
char *rpath;
sb_log(2, "Start creat(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do creat(\"%s\", %04o).", path, mode);
return sb_real_creat(path, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to creat(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return sb_devnull;
errno = EACCES;
return -1;
}
int creat64(const char *path, mode_t mode) {
char *rpath;
sb_log(2, "Start creat64(\"%s\", %04o).", path, mode);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do creat64(\"%s\", %04o).", path, mode);
return sb_real_creat64(path, mode);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to creat64(\"%s\", %04o).", rpath, mode);
free(rpath);
if (sb_action == SB_A_WARN)
return sb_devnull;
errno = EACCES;
return -1;
}
int remove(const char *path) {
char *rpath;
sb_log(2, "Start remove(\"%s\").", path);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do remove(\"%s\").", path);
return sb_real_remove(path);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to remove(\"%s\").", rpath);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int rename(const char *oldpath, const char *newpath) {
char *oldrpath, *newrpath;
sb_log(2, "Start rename(\"%s\", \"%s\").", oldpath, newpath);
oldrpath = sb_rewrite(oldpath, SB_R_LINK);
newrpath = sb_rewrite(newpath, SB_R_FILE);
if (sb_has_access(oldrpath) && sb_has_access(newrpath)) {
sb_log(4, "Do rename(\"%s\", \"%s\").", oldpath, newpath);
return sb_real_rename(oldpath, newpath);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to rename(\"%s\", \"%s\").", oldrpath, newrpath);
free(oldrpath);
free(newrpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int rmdir(const char *path) {
char *rpath;
sb_log(2, "Start rmdir(\"%s\").", path);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do rmdir(\"%s\").", path);
return sb_real_rmdir(path);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to rmdir(\"%s\").", rpath);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int symlink(const char *oldpath, const char *newpath) {
char *newrpath;
sb_log(2, "Start symlink(\"%s\", \"%s\").", oldpath, newpath);
newrpath = sb_rewrite(newpath, SB_R_LINK);
if (sb_has_access(newrpath)) {
sb_log(4, "Do symlink(\"%s\", \"%s\").", oldpath, newpath);
return sb_real_symlink(oldpath, newpath);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to symlink(\"%s\", \"%s\").", oldpath, newrpath);
free(newrpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int unlink(const char *path) {
char *rpath;
sb_log(2, "Start unlink(\"%s\").", path);
rpath = sb_rewrite(path, SB_R_LINK);
if (sb_has_access(rpath)) {
sb_log(4, "Do unlink(\"%s\").", path);
return sb_real_unlink(path);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to unlink(\"%s\").", rpath);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int utime(const char *path, const struct utimbuf *buf) {
char *rpath;
sb_log(2, "Start utime(\"%s\", NULL).", path);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do utime(\"%s\", NULL).", path);
return sb_real_utime(path, buf);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to utime(\"%s\", %s).", rpath, str_utimbuf(buf));
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}
int utimes(const char *path, const struct timeval *tvp) {
char *rpath;
sb_log(2, "Start utimes(\"%s\", NULL).", path);
rpath = sb_rewrite(path, SB_R_FILE);
if (sb_has_access(rpath)) {
sb_log(4, "Do utimes(\"%s\", NULL).", path);
return sb_real_utimes(path, tvp);
}
if (sb_action == SB_A_HALT)
exit(0);
sb_log(1, "Attempt to utimes(\"%s\", NULL).", rpath);
free(rpath);
if (sb_action == SB_A_WARN)
return 0;
errno = EACCES;
return -1;
}