/* * Soapbox - A way to deny processes to write files outside some directories * * Copyright (C) 2003 by Dag Wieers * Copyright (C) 2012 by Ralf S. Engelschall * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #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; }