/* * - Virtual Filesystem Root * * Copyright (c) 2009-2017 * AVM GmbH, Berlin, Germany * * License: Free, use with no restriction. */ #ifdef HAVE_CONFIG_H # include #endif #if !defined (__GNUC__) && defined (_AIX) #pragma alloca #endif #ifndef alloca /* Make alloca work the best possible way. */ # ifdef __GNUC__ # define alloca __builtin_alloca # else /* not __GNUC__ */ # if HAVE_ALLOCA_H # include # else /* not __GNUC__ or HAVE_ALLOCA_H */ # ifndef _AIX /* Already did AIX, up at the top. */ char *alloca (); # endif /* not _AIX */ # endif /* not HAVE_ALLOCA_H */ # endif /* not __GNUC__ */ #endif /* not alloca */ #include #include #include #include #ifdef HAVE_SYS_WAIT_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #include #ifdef HAVE_MMAP #include #endif #include #include "virtual.h" #include "avm_acl.h" // #include "extern.h" #if 0 static void _fLog(char *fmt, ...) { FILE *fp = fopen("/dev/console", "w"); if (!fp) fp = fopen("/var/tmp/acl.debug", "a"); // NOTE that you have to "chmod 777 /var/tmp" before if (fp) { va_list ap; fprintf(fp, "acl(%d): ", getpid()); va_start(ap, fmt); vfprintf(fp, fmt, ap); va_end(ap); fprintf(fp, "\n"); fclose(fp); } } #define Log(x) _fLog x #else #define Log(x) #endif static char *g_real_root = 0; // absolute linux path that is the virtual "/" static int g_real_root_len; static char *tmpstring(size_t siz) { #define NTMPSTRING 16 static char *tmps[NTMPSTRING] = { 0, }; static int pos = 0; char *p; if (tmps[pos]) free(tmps[pos]); p = tmps[pos] = malloc(siz); pos++; if (pos == NTMPSTRING) pos = 0; return p; } #if 0 /* not used */ static int virt_dir_exists(const char *abs_virt_path, const char *dirname) { if (!g_real_root) return 0; char *path = tmpstring(g_real_root_len + strlen(abs_virt_path) + 1 + strlen(dirname) + 1); if (!path) return 0; sprintf(path, "%s%s/%s", g_real_root, abs_virt_path, dirname); struct stat sb; if (stat(path, &sb)) return 0; return sb.st_mode & S_IFDIR; } #endif /* 0 */ static char *virtual_to_real_path(const char *path) { char *real; if (!g_real_root) return 0; if (*path == '/' && strcmp(g_real_root, "/")) { // absolute client path -> change virt. to real root real = tmpstring(strlen(path) + g_real_root_len + 1); if (!real) return 0; strcpy(real, g_real_root); if (strcmp(path, "/")) { strcat(real, path); } } else { // relative virtual paths are autom. // relative real paths real = tmpstring(strlen(path) + 1); if (!real) return 0; strcpy(real, path); } return real; } // change the real path to a virtual client path void virtual_make_from_real_path(char **ppath) { if (!g_real_root) return; if (!strcmp(g_real_root, "/")) return; if (*ppath == strstr(*ppath, g_real_root)) { char *p; int diff = strlen(*ppath) - g_real_root_len; if (diff) { p = tmpstring(diff + 1); if (!p) return; strcpy(p, (*ppath) + g_real_root_len); } else { p = tmpstring(2); if (!p) return; strcpy(p, "/"); } *ppath = p; } } /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ static int IsAllowed(const char *path, char **preal_path, int *pwrite_access) { *preal_path = virtual_to_real_path(path); if (*preal_path) { if (acl_is_allowed_path(*preal_path, pwrite_access)) { Log(("%s %s real_path=%s is allwed write_access=%d", __FILE__, __FUNCTION__, *preal_path, *pwrite_access)); return 1; } else { Log(("%s %s not allowed to access real_path=%s", __FILE__, __FUNCTION__, *preal_path)); } } else { Log(("%s %s no real_path for %s", __FILE__, __FUNCTION__, path)); } errno = EACCES; return 0; } int virtual_is_write_access(const char *path, char **preal_path) { int write_access; if (!IsAllowed(path, preal_path, &write_access)) { Log(("virtual_is_write_access failed path=%s", path)); return 0; } Log(("virtual_is_write_access path=%s real=%s write_access=%d", path, *preal_path, write_access)); if (!write_access) errno = EACCES; return write_access; } int virtual_is_read_access(const char *path, char **preal_path) { int write_access; return IsAllowed(path, preal_path, &write_access); } /* ------------------------------------------------------------------------ */ void virtual_set_root(const char *real_root) { free(g_real_root); if (real_root) g_real_root = strdup(real_root); else g_real_root = 0; if (g_real_root) g_real_root_len = strlen(g_real_root); } FILE *virtual_fopen(const char *fn, const char *mode) { int is_write = 1; char *real; int write_access; int allowed = IsAllowed(fn, &real, &write_access); if (mode[0] == 'r' && mode[1] != '+') is_write = 0; if (!is_write) { FILE *fp = 0; if (real) { fp = fopen(real, mode); if (!fp) return 0; // use original errno } if (!allowed || !real) { if (fp) fclose(fp); errno = EACCES; return 0; } return fp; } else { if (!allowed || !write_access) { errno = EACCES; return 0; } return fopen(real, mode); } } int virtual_mkdir(const char *dir, mode_t mode) { char *real; if (!virtual_is_write_access(dir, &real)) { return -1; } return mkdir(real, mode); } int virtual_rename(const char *from, const char *to) { char *real_from; char *real_to; if (!virtual_is_write_access(from, &real_from)) { return -1; } if (!virtual_is_write_access(to, &real_to)) { return -1; } return rename(real_from, real_to); } int virtual_stat(const char *path, struct stat *buf) { char *real_path; struct stat sb; int write_access; int allowed = IsAllowed(path, &real_path, &write_access); if (real_path) { int ret = stat(real_path, &sb); if (ret) return ret; // use original errno } if (!allowed || !real_path) { errno = EACCES; return -1; } if (!write_access) sb.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); memcpy(buf, &sb, sizeof(struct stat)); return 0; } int virtual_rmdir(const char *pathname) { char *real_path; if (!virtual_is_write_access(pathname, &real_path)) { return -1; } return rmdir(real_path); } int virtual_unlink(const char *pathname) { char *real_path; if (!virtual_is_write_access(pathname, &real_path)) { return -1; } return unlink(real_path); } int virtual_chdir(const char *path) { char *real_path; if (!virtual_is_read_access(path, &real_path)) return -1; return chdir(real_path); } DIR *virtual_opendir(const char *name) { char *real_path; int allowed = virtual_is_read_access(name, &real_path); DIR *d = 0; if (real_path) { d = opendir(real_path); if (!d) return 0; // use original errno } if (!allowed || !real_path) { if (d) closedir(d); errno = EACCES; return 0; } return d; } int virtual_chmod(const char *path, mode_t mode) { char *real_path; if (!acl_chmod_allowed()) { errno = EACCES; return -1; } if (!virtual_is_write_access(path, &real_path)) { return -1; } #if 0 // never allow chmod of a mountpoint if (acl_is_mountpoint(real_path)) { errno = EACCES; return -1; } #endif uid_t uid = geteuid(); seteuid(0); int ret = chmod(real_path, mode); seteuid(uid); return ret; }