/* * egnyte vfs module * Copied template skeleton VFS module (skel_transparent.c)from samba4 examples and made changes */ #include "../source3/include/includes.h" #include "lib/util/tevent_unix.h" #include #include #include #include #include #include "vfs_macros.h" #include "version.h" // samba version #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../libcli/security/security.h" #include "../lib/util/samba_modules.h" #include "regex_parser.h" #include "egnyte_utils.h" #include "samba.pb-c.h" #include "egnyte_vfs_fs.h" //egnyte permission be #include "egnyte_perms.h" //egnyte permissions #include "perm_utils.h" // define debug class per debug.h #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS /* * When we figure out how to distinguish between RW and RWD we will undefine this * and the special handling will go into effect */ #define VFSPERMS_RWISRWD /* Function prototypes */ static int read_egnyte_config(void); static int read_and_compile_patterns(void); char *get_full_path(char *folder, const char *path); static int is_deletable_file(char *fname); static void free_compiled_patterns(void); /* regex pattern buffer */ extern e_regex_matchres_t match_regex_string (char *pattern, char *string); extern e_regex_matchres_t get_regex_matchbuf(char *pattern, regex_t **reg_buf); extern e_regex_matchres_t match_string_with_regex_buf(char *strng, regex_t *regbuf); pattern_buf_t pattern_buf; typedef struct sig_handle_ctx_s { uid_t myeuid; } sig_handle_ctx_t; TALLOC_CTX *egnyte_eventmem_ctx = NULL; struct event_context *egnyte_event_ctx = NULL; sig_handle_ctx_t *sighandle_private_data = NULL; uid_t reserved_user_uid = -1; static int is_amove(char *src_fsobj, char *dst_fsobj) { char *parent_src = NULL; char *parent_dst = NULL; path_array_t *src = NULL; path_array_t *dst = NULL; int ret = 0; int cnt = 0; //short circuit here return 0; DEBUG(0, ("egnyte:%s\n",__func__)); if (!src_fsobj || !dst_fsobj) { DEBUG(0,("Invalid sourc or destination name src %s dst %s\n", (src_fsobj == NULL ? "Null" : src_fsobj), (dst_fsobj == NULL ? "NULL": dst_fsobj))); return -1; } parent_src = parent_dir_from_path(src_fsobj); if (!parent_src) { DEBUG(0, ("Invalid parent %s\n",src_fsobj)); return -1; } parent_dst = parent_dir_from_path(dst_fsobj); if (!parent_dst) { DEBUG(0, ("Invalid parent %s\n",dst_fsobj)); free(parent_src); return -1; } get_path_elem_smb4((char*)parent_src, (unsigned int)strlen(parent_src),&src); if (!src) { DEBUG(0, ("Could not convert path %s\n", parent_src)); free(parent_src); free(parent_dst); return -1; } PERR("%s: get_path_elem_smb4: src=%p num_elements=%d\n",__func__,src,src->num_elems); get_path_elem_smb4((char*)parent_dst, (unsigned int)strlen(parent_dst),&dst); if (!dst) { free(parent_src); free(parent_dst); cleanup_path_array_smb4(&src); DEBUG(0, ("Could not convert path %s\n", parent_dst)); return -1; } PERR("%s: get_path_elem_smb4: dst=%p num_elements=%d\n",__func__,dst,dst->num_elems); if (src->num_elems != dst->num_elems) ret = 1; //short circuit here // free(parent_src); // free(parent_dst); // cleanup_path_array_smb4(&src); // cleanup_path_array_smb4(&dst); // return 0; //end short circuit for(cnt =0; cnt < src->num_elems; cnt ++) { if(src->elems[cnt]->hashval != dst->elems[cnt]->hashval) { ret = 1; break; } if (src->elems[cnt]->len != dst->elems[cnt]->len) { ret = 1; break; } //if (StrnCaseCmp(src->elems[cnt]->name_elem, // dst->elems[cnt]->name_elem, dst->elems[cnt]->len) != 0) { if (strncasecmp_m(src->elems[cnt]->name_elem, dst->elems[cnt]->name_elem, dst->elems[cnt]->len) != 0) { ret = 1; break; } ret = 0; } free(parent_src); free(parent_dst); cleanup_path_array_smb4(&src); cleanup_path_array_smb4(&dst); return ret; } void set_reserved_user_uid(void) { struct passwd pswd; struct passwd *tmppswd; char pwdbuffer[512]; int pwdbuflen = sizeof(pwdbuffer); if ((getpwnam_r(EGNYTE_RESERVED_USER, &pswd, pwdbuffer, pwdbuflen, &tmppswd)) != 0) { DEBUG(0,("Error: Could not determine egnyte reserved user %s info\n", EGNYTE_RESERVED_USER)); } else { reserved_user_uid = pswd.pw_uid; } return; } /* Populate a VFS event with PID, UID, and timestamp. */ int _pop_vfs_event(VFSEvent *ve) { struct timeval tv; ve->client_pid = (int) getpid(); ve->uid = (int) geteuid(); gettimeofday(&tv, NULL); ve->time_sec = (int) tv.tv_sec; ve->time_usec = (int) tv.tv_usec; DEBUG(0,("Time stamp is %lu sec %lu msces\n",ve->time_sec,ve->time_usec)); return 0; } /* Write the VFS event to shared mem/disk, etc */ int _write_vfs_event(VFSEvent *ve) { PERR("%s: ENTRY\n",__func__); int len; void *buf; /* stores the serialization of the buffer */ staging_data_t *dat; if (ve == NULL) { PERR("vfsevent is null\n"); return -1; } /* reserved uid or root user changes are not recorded */ if ((ve->uid == reserved_user_uid) || (ve->uid ==0)) { DEBUG(0,("Ignoring reserved user events [%s] pid %d\n", ve->path, getpid())); return 0; } len = vfsevent__get_packed_size(ve); buf = calloc(len,sizeof(char)); if (!buf) { PERR("could not allocate vfs buffer. errno = %d(%s)\n", errno, strerror(errno)); return -1; } vfsevent__pack(ve, buf); dat = (staging_data_t *)alloc_staging_data(len); if (dat == NULL) { PERR("could not allocate staging data. errno = %d(%s)\n", errno, strerror(errno)); free(buf); buf = NULL; return -1; } memcpy(dat->buffer, buf, len); dat->size = len; DEBUG(0,("event %d [%s]\n", len, dat->buffer)); write_to_staging_buffer(dat); free(buf); buf = NULL; free(dat); dat = NULL; } /* * OPENMODE_READ and OPENMODE_WRITE applies only to OPTYPE_OPEN. Rest should be * OPENMODE_INVALID. */ typedef enum e_open_mode { OPENMODE_INVALID, OPENMODE_READ, OPENMODE_WRITE, OPENMODE_CREATE, OPENMODE_CHATTR } e_open_mode_t; static e_open_mode_t mode2egnytemode(int flags) { e_open_mode_t ret = OPENMODE_READ; if (flags & O_TRUNC) DEBUG(0,("mode2egnytemode O_TRUNC set returning OPENMODE_WRITE\n")); //return OPENMODE_CHATTR; return OPENMODE_WRITE; if (flags & O_CREAT) return OPENMODE_CREATE; if (flags & (O_APPEND | O_WRONLY | O_RDWR)) { ret = OPENMODE_WRITE; } return ret; } global_param_t *global_params = NULL; #define DEFAULT_USER_ID 0 #define DEFAULT_GROUP_ID 0 uid_t get_default_owner() { if (global_params[PARAM_DEFAULT_USER_ID].value.intval >= 0) return (uid_t)global_params[DEFAULT_USER_ID].value.intval; else return (uid_t)DEFAULT_USER_ID; } gid_t get_default_owning_group() { if (global_params[PARAM_DEFAULT_GROUP_ID].value.intval >= 0) return (gid_t)global_params[DEFAULT_GROUP_ID].value.intval; else return (gid_t)DEFAULT_GROUP_ID; } /* * Change this path if you want to relocate the fifo */ #define EGNYTE_AUTH_FIFO_NAME "/dev/egnyte_auth_dm_fifo" /* XXX: obsolete? */ /* * In this version the temp FIFO is unused yet sent in the packet. Ignore it */ #define EGNYTE_TMPFIFO_PERMPREFIX "/var/run/egnyte_permtmp_" #define EGNYTE_IGNORE_EGNYTE_VERDICT 1 /* undef this if you need to wait for egnyte return */ /* filename is pid.randomint for pid and 4 integers for randomint caller frees */ #define EGNYTE_TMPFIFO_SIZE 32 /* XXX: obsolete? */ int int2str4digits(char *str, int buff_sz, int val) { int t = 0; int h = 0; int te = 0; int o = 0; if (!str) return -1; memset(str, '\0', buff_sz); t = val / 1000; h = (val - t * 1000) / 100; te = (val - t * 1000 - h * 100) / 10; o = (val - t * 1000 - h * 100 - te * 10); str[0] = (char) ('0' + (unsigned char) t); str[1] = (char) ('0' + (unsigned char) h); str[2] = (char) ('0' + (unsigned char) te); str[3] = (char) ('0' + (unsigned char) o); } /* XXX: obsolete? */ static inline char *get_permfifo_name() { char *ret; int x; x = asprintf(&ret, "%s%d.%d", EGNYTE_TMPFIFO_PERMPREFIX, getpid() % 9999, random() % 9999); return ret; } static char *child_from_path(const char *path) { const char *pname = path; char *c; char *bf; if (!pname) return NULL; if(!strrchr(pname,'/')) { asprintf(&bf,"%s",pname); return bf; } c = (char *)pname + strlen(pname) - 1; while (c && *c == '/') { c--; } while (c && *c != '/') { c--; if (c == pname) { break; } } c++; asprintf(&bf, "%s", c); c = bf + strlen(bf) - 1; while (c && *c == '/') { *c='\0'; c--; } return bf; } /* * * Is this ELC share? * */ #define EGNYTE_SHARE_NAME "ELC" int is_ELC_Share(const char *share_name) { PERR("%s: ENTRY\n",__func__); char *shname; int ret = 0; DEBUG(0, ("egnyte:%s\n",__func__)); if (!share_name) return 0; if ((shname = child_from_path(share_name)) == NULL) { DEBUG(0, ("Err in getting child %s\n", share_name)); return 0; } if (!strcmp(shname,EGNYTE_SHARE_NAME)) { DEBUG(0, ("Egnyte share %s child %s\n", share_name, shname)); ret = 1; } free(shname); return ret; } /* XXX: obsolete ??? */ #define POLLTIMER 1000 #define ADMIN_USER_NAME_STR "admin" static int is_admin_or_root(uid_t uid) { PERR("%s: ENTRY\n",__func__); struct passwd p; struct passwd *pptr = &p; struct passwd *tmppptr = NULL; char pwstrings[256]; int pwstrings_size = sizeof(pwstrings); DEBUG(0, ("egnyte:%s\n",__func__)); if (uid == 0) return 1; if ((getpwuid_r(uid, pptr, pwstrings, pwstrings_size, &tmppptr)) != 0) { PERR("error in getpwuid for uid %d. errno = %d(%s)\n", uid, errno, strerror(errno)); } if (strcmp(ADMIN_USER_NAME_STR, p.pw_name) == 0) { return 1; } return 0; } /* Returns a new null-terminated string folder/path. * Caller MUST free this string */ char *get_full_path(char *folder, const char *path) { PERR("%s: ENTRY\n",__func__); char *full_path; int path_size; DEBUG(0, ("egnyte:%s\n",__func__)); path_size = strlen(folder) + strlen(path) + 4; full_path = (char *)calloc(path_size,sizeof(char)); if (full_path == NULL) { PERR("calloc failed. errno = %d(%s)\n", errno, strerror(errno)); return NULL; } //memset(full_path, '\0', path_size); //snprintf(full_path, path_size, "%s/%s\0", folder, path); snprintf(full_path, path_size, "%s/%s", folder, path); return full_path; } char *get_nullterminated_str(char *str, int len) { char *buf = NULL; DEBUG(0, ("egnyte:%s\n",__func__)); if ((!str) || (!len)) { DEBUG(0, ("Cannot convert NULL string\n")); return NULL; } buf = (char *)calloc(len+2,sizeof(char)); if (!buf) { DEBUG(0, ("Malloc error\n")); return NULL; } //memset(buf,'\0',len+2); //return StrnCpy_fn(NULL, 0, buf,str,len); return StrnCpy(buf,str,len); } #define VFS_TLB_SIZE 128 /* * The cache is comprised of several components: * - an array of pathnames * - an array of sets of 4 numbers * - an array of indices into the first 2 arrays * * For the array of sets of 4 numbers: * * The first 2 numbers are a bitmap with the operation enum value as the index. * If the bit is set then a permission value has been set for that operation. * * The second 2 numbers are also a bitmap index by operation. If the * bit is set then permission is allowed, otherwise it is denied. * * 2 sets of 32 bits each are used for both the set indication and the value, * to allow for up to 64 operations. * * The 64 set bits indicate the operations for which a value is set. * The 64 value bits provide the values for operations that are set. * * The ordering of the cache is that the 0 slot contains the most * recently updated or accessed path, the 1 slot contains the next most * recently updated or accessed path, and so on. An addition to a full * cache results in the new path in slot 0, other paths moved down a * slot, and the path in the last slot removed from the cache. */ static char *tlb_paths[VFS_TLB_SIZE]; static int tlb_index_table[VFS_TLB_SIZE]; // 21 means flush the tlb, 9 means do not. These values make it easier // to track down inintended changes to this variable than 0 and 1. static int flush_tlb = 9; static struct { uint32_t set[2]; uint32_t value[2]; } tlb_perms[VFS_TLB_SIZE]; /* the value of the next available slot, or VFS_TLB_SIZE if full */ static int vfs_tlb_end; /* signal handler to flush the tbl when permissions change */ static void flush_perms_handler(int signal, uid_t myeuid) { PERR("%s: ENTRY\n",__func__); struct passwd pd; struct passwd* pwdptr=&pd; struct passwd* tempPwdPtr = NULL; char *pwdbuffer = NULL; int buffsize = 0; uid_t euid; uid_t uid; int ngroups = 0; DEBUG(0, ("egnyte:%s\n",__func__)); DEBUG(0, ("Caught SIGUSR1 %d\n", getpid())); if (signal == SIGUSR1) { flush_tlb = 21; // 21 means flush the buffer DEBUG(0,("USR1 signal caught %d, flush_tlb: %d, flush_tlb addr: %p\n", getpid(), flush_tlb, &flush_tlb)); /* * Reset user group membership */ euid = myeuid; uid = getuid(); DEBUG(0,("Resetting groups for uid %d euid %d\n", uid, euid)); buffsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (buffsize == -1) buffsize = 16384; pwdbuffer = (char *)calloc(buffsize+1,sizeof(char)); if (!pwdbuffer) { DEBUG(0, ("Cannot allocate memory of size %d\n", buffsize)); return; } //memset(pwdbuffer, '0', buffsize); if ((getpwuid_r(euid,pwdptr,pwdbuffer,buffsize,&tempPwdPtr)) != 0) { DEBUG(0,("Cannot get username for uid %d to update user's groups: %s\n", euid, strerror(errno))); } else if (tempPwdPtr == NULL) { DEBUG(0,("No user with uid %d\n", euid)); } else { DEBUG(0,("password struct retrieved with euid %u for %s, gid %u\n", geteuid(), pd.pw_name, pd.pw_gid)); DEBUG(0,("getting group list count using %d\n", ngroups)); if (getgrouplist(pd.pw_name, pd.pw_gid, NULL, &ngroups) == -1) { gid_t *groupbuf = NULL; DEBUG(0,("There are %d group(s) for user %s\n", ngroups, pd.pw_name)); groupbuf = (gid_t *)malloc(ngroups * sizeof(gid_t)); if (!groupbuf) { DEBUG(0, ("Cannot allocate memory to retrieve grouplist\n")); } else { if (getgrouplist(pd.pw_name, pd.pw_gid, groupbuf, &ngroups) == -1) { DEBUG(0,("getgrouplist(%s, %u, %d) failed with errno %s\n", pd.pw_name, pd.pw_gid, ngroups, strerror(errno))); } else { DEBUG(0,("retrieved group list, setting group list\n")); become_root(); setgid(pd.pw_gid); if (setgroups(ngroups, groupbuf) == -1) { DEBUG(0,("Cannot get group list to update user %s's groups: %s\n", pd.pw_name, strerror(errno))); } unbecome_root(); DEBUG(0,("Reset group list for user %s\n", pd.pw_name)); } free(groupbuf); } } else { DEBUG(0,("Cannot retrieve the number of groups for user %d error %s\n", euid, strerror(errno))); } } free(pwdbuffer); } else { DEBUG(0,("Unexpected signal %d caught \n", signal)); } return; } static void egnyte_sigusr1_handler( struct tevent_context *ev, struct tevent_signal *se, int signum, int count, void *siginfo, void *private_data) { PERR("%s: ENTRY\n",__func__); uid_t myeuid = -1; DEBUG(0, ("egnyte:%s\n",__func__)); sig_handle_ctx_t *sigctx = talloc_get_type(private_data, sig_handle_ctx_t); if (!sigctx) { DEBUG(0,("Could not get signal context\n")); return; } myeuid = sigctx->myeuid; flush_perms_handler(SIGUSR1, myeuid); return; } void init_tlb() { int i; DEBUG(0, ("egnyte:%s\n",__func__)); for (i = 0; i < VFS_TLB_SIZE; i++) { DEBUG(0,("TLB Init %d \n", i)); tlb_index_table[i] = i; tlb_paths[i] = NULL; tlb_perms[i].set[0] = 0; tlb_perms[i].set[1] = 0; tlb_perms[i].value[0] = 0; tlb_perms[i].value[1] = 0; } vfs_tlb_end = 0; return; } void destroy_tlb() { PERR("%s: ENTRY\n",__func__); int i; for (i = 0; i < VFS_TLB_SIZE; i++) { DEBUG(0,("TLB Destroy %d \n", i)); tlb_index_table[i] = i; if (tlb_paths[i]) free(tlb_paths[i]); tlb_paths[i] = NULL; tlb_perms[i].set[0] = 0; tlb_perms[i].set[1] = 0; tlb_perms[i].value[0] = 0; tlb_perms[i].value[1] = 0; } vfs_tlb_end = 0; return; } /* * Update the tlb order with the input hit. * Currently we place it at the top. */ static int _update_tlb_hit(int slot) { PERR("%s: ENTRY\n",__func__); int i; int tmp = tlb_index_table[slot]; DEBUG(0, ("egnyte:%s\n",__func__)); DEBUG(0,("TLB: Updating slot %d\n", slot)); for (i = slot; i > 0; i--) tlb_index_table[i] = tlb_index_table[i-1]; tlb_index_table[0] = tmp; return 0; } /* return the slot of the path, or -1 if not found */ static int _tlb_lookup_path(char *path) { int i; DEBUG(0, ("egnyte:%s\n",__func__)); for (i = 0; i < vfs_tlb_end; i++) if (!strncmp(path, tlb_paths[tlb_index_table[i]], PATH_MAX)) return i; return -1; } /* * Update the op and value pair for the existing entry slot in the table. * * The input slot must be an existing value in the populated range of the table. */ static void update_tlb_entry(int slot, e_fsop_type_t op, e_vfs_enforce_perms_t value) { PERR("%s: ENTRY\n",__func__); int op_index; int which; int op_bit; int perm_bit; DEBUG(0, ("egnyte:%s\n",__func__)); assert(slot >= 0 && slot < vfs_tlb_end); /* ops start at 1 so index 0 is never used */ assert(op != 0); op_index = op % (sizeof(uint32_t)*8); if (op_index == op) which = 0; else which = 1; op_bit = (1 << op_index); /* convert the op to a bit position */ if (value == EGPERMS_HAS_PERMS) { perm_bit = 1; perm_bit <<= op_index; } else { perm_bit = 0; } tlb_perms[slot].set[which] |= op_bit; /* mark the bit as set */ tlb_perms[slot].value[which] &= ~op_bit; /* clear the perm bit */ tlb_perms[slot].value[which] |= perm_bit;/* set the corresponding value */ return; } /* * Delete the path/op entry from the tlb. */ static void del_tlb_entry(char *path) { int slot; int index; int i; PERR("%s: ENTRY\n",__func__); PERR("%s: Deleting path %s\n", __func__,path); slot = _tlb_lookup_path(path); if (slot == -1) /* not found, just return */ { PERR("%s: did not find path %s\n",__func__,path); return; } index = tlb_index_table[slot]; free(tlb_paths[index]); /* clear the old values */ tlb_paths[index] = NULL; tlb_perms[index].set[0] = 0; tlb_perms[index].set[1] = 0; tlb_perms[index].value[0] = 0; tlb_perms[index].value[1] = 0; /* move everything up */ for (i = slot; i + 1 < vfs_tlb_end; i++) tlb_index_table[i] = tlb_index_table[i+1]; tlb_index_table[i] = tlb_index_table[slot]; vfs_tlb_end--; PERR("%s: EXIT\n",__func__); return; } /* * Add the path/op/value entry into the provided slot. */ static void add_tlb_entry(char *path, int slot, e_fsop_type_t op, e_vfs_enforce_perms_t value) { PERR("%s: ENTRY\n",__func__); int op_index; int which; int index; int op_bit; int perm_bit; int len; DEBUG(0, ("egnyte:%s\n",__func__)); if (slot == -1) { /* new cache entry */ if (vfs_tlb_end < VFS_TLB_SIZE) { vfs_tlb_end++; index = tlb_index_table[vfs_tlb_end-1]; assert(tlb_paths[index] == NULL); } else { index = tlb_index_table[vfs_tlb_end-1]; free(tlb_paths[index]); } len = strlen(path)+1; tlb_paths[index] = malloc(len); if (tlb_paths[index] == NULL) { DEBUG(0,("Cannot create path for TLB cache, out of memory\n")); return; } snprintf(tlb_paths[index], len, "%s", path); tlb_perms[index].set[0] = 0; tlb_perms[index].set[1] = 0; tlb_perms[index].value[0] = 0; tlb_perms[index].value[1] = 0; slot = vfs_tlb_end-1; } else { /* existing cache entry */ index = tlb_index_table[slot]; } /* ops start at 1 so index 0 is never used */ assert(op != 0); op_index = op % (sizeof(uint32_t)*8); if (op_index == op) which = 0; else which = 1; op_bit = (1 << op_index); /* convert the op to a bit position */ if (value == EGPERMS_HAS_PERMS) { perm_bit = 1; perm_bit <<= op_index; } else { perm_bit = 0; } tlb_perms[index].set[which] |= op_bit; /* mark the bit as set */ tlb_perms[index].value[which] |= perm_bit; /* set the corresponding value */ (void) _update_tlb_hit(slot); return; } /* * Check whether the path/operation is in the cache * * Returns: * EGPERMS_HAS_PERMS or EGPERMS_NO_PERM on a cache hit * EGPERMS_NOT_FOUND if the path is not in the cache * EGPERMS_NOT_SET if the path is in the cache but the input operation is * not set, in which case the index is returned */ static e_vfs_enforce_perms_t check_tlb(char *path, e_fsop_type_t op, int *index) { PERR("%s: ENTRY\n",__func__); int slot; int op_index; int which; e_vfs_enforce_perms_t retval; int op_bit; DEBUG(0, ("egnyte:%s\n",__func__)); #if 0 DEBUG(0,("Setting signal handler \n")); if (CatchSignal(SIGUSR1, SIGNAL_CAST flush_perms_handler) == SIG_ERR) DEBUG(0,("Setting of signal handler failed \n")); #endif *index = -1; DEBUG(0,("flush_tlb value: %d, flush_tlb addr: %p\n", flush_tlb, &flush_tlb)); if (flush_tlb == 21) { // 21 means flush the buffer DEBUG(0,("Flushing and rebuilding tlb \n")); destroy_tlb(); init_tlb(); flush_tlb = 9; // 21 means do not flush the buffer } slot = _tlb_lookup_path(path); if (slot == -1) return EGPERMS_NOT_FOUND; op_index = op % (sizeof(uint32_t)*8); if (op_index == op) which = 0; else which = 1; op_bit = (1 << op_index); /* convert the op to a bit position */ /* if value is set - a cache hit - return the value */ if (tlb_perms[tlb_index_table[slot]].set[which] & op_bit) { if (tlb_perms[tlb_index_table[slot]].value[which] & op_bit) retval = EGPERMS_HAS_PERMS; else retval = EGPERMS_NO_PERM; *index = _update_tlb_hit(slot); return retval; } *index = slot; return EGPERMS_NOT_SET; } static int check_perms(char *shareroot, const char *path, e_fsop_type_t op, process_trustees_t *in_pts) { PERR("%s: ENTRY\n",__func__); int retval; e_fsop_type_t ret_tlb; int slot; char *clean_path; char *nlpath = NULL; DEBUG(0, ("egnyte:%s\n",__func__)); /* * normalize_path() needs to take care of any special pathname handling * including samba's terminating 0 instead of '\0', or 0x80 to indicate * extended characters, so that no subsequent calls need to deal with it */ clean_path = normalize_path(path); if (!clean_path) { if (errno == ENOMEM) { DEBUG(0,("path normalization failed %s: %s\n", path, strerror(errno))); return EGPERMS_MALLOC_ERR; } else if (errno == ENOENT) { /* samba attempts operations such as 'stat("./..")' */ DEBUG(0,("returning HAS_PERMS, path normalization returned an unusable path %s: %s\n", path, strerror(errno))); return EGPERMS_HAS_PERMS; } else { DEBUG(0,("path normalization failed for %s: code=%d (%s)\n", path, errno, strerror(errno))); assert(0); return EGPERMS_SYSTEM_ERR; } } DEBUG(0,("%s normalized to %s\n", path, clean_path)); ret_tlb = check_tlb(clean_path, op, &slot); if (ret_tlb == EGPERMS_HAS_PERMS || ret_tlb == EGPERMS_NO_PERM) { DEBUG(0,("TLB: Cache hit for %s, op %d, perms %d, slot %d\n", path, op, ret_tlb, slot)); free(clean_path); return(ret_tlb); } retval = vfs_enforce_perms(shareroot, clean_path, op, in_pts); if (retval == EGPERMS_NO_PERM || retval == EGPERMS_HAS_PERMS) { if (ret_tlb == EGPERMS_NOT_FOUND) { add_tlb_entry(clean_path, slot, op, retval); DEBUG(0,("TLB: Adding entry for %s, op %d, perms %d, slot %d\n", path, op, retval, slot)); } else if (ret_tlb == EGPERMS_NOT_SET) { DEBUG(0,("TLB: Updating entry for %s, op %d, perms %d, slot %d\n", path, op, retval, slot)); update_tlb_entry(slot, op, retval); } else { assert(0); } } free(clean_path); return retval; } /* Implementation of vfs_ops. Pass everything on to the default operation but log event first. */ static int egnyte_connect(vfs_handle_struct *handle, const char *svc, const char *user) { PERR("%s: ENTRY\n",__func__); struct tevent_signal *se; uid_t my_euid; struct passwd pd; struct passwd* tempPwdPtr; char pwdbuf[1024]; //flush_tlb = 21; DEBUG(0,("Calling init_tlb \n")); init_tlb(); DEBUG(0,("After calling init_tlb \n")); read_egnyte_config(); DEBUG(0,("After Calling config \n")); #ifndef VFSPERMS_RWISRWD read_and_compile_patterns(); DEBUG(0,("After calling patterns \n")); #endif if (init_perm_map_semaphore() == -1) return -1; DEBUG(0,("After calling init_perm_map_semaphore \n")); if (egnyte_eventmem_ctx != NULL) { talloc_free(egnyte_eventmem_ctx); } egnyte_eventmem_ctx = talloc_new(NULL); if (egnyte_eventmem_ctx == NULL) { return ENOMEM; } if ((getpwnam_r(user, &pd, pwdbuf, 1024, &tempPwdPtr))!=0) { DEBUG(0,("Error: cound not resolve user %s",user)); return -1; } egnyte_event_ctx = tevent_context_init(egnyte_eventmem_ctx); if (egnyte_event_ctx == NULL) { DEBUG(0,("Cannot allocate event_context\n")); return ENOMEM; } sighandle_private_data = talloc(egnyte_eventmem_ctx,sig_handle_ctx_t); if (sighandle_private_data == NULL) { DEBUG(0,("Memory error: Cannot allocate signal handler private data\n")); return ENOMEM; } sighandle_private_data->myeuid = pd.pw_uid; DEBUG(0, ("Set sighandler uid %d\n", sighandle_private_data->myeuid)); se = tevent_add_signal( egnyte_event_ctx, sighandle_private_data, SIGUSR1, 0, egnyte_sigusr1_handler, sighandle_private_data); if (!se) { DEBUG(0, ("Failed to set up SIGUSR1 signal handler")); } #if 0 if (CatchSignal(SIGUSR1, SIGNAL_CAST flush_perms_handler) == SIG_ERR) DEBUG(0,("Setting of signal handler failed \n")); #endif set_reserved_user_uid(); return SMB_VFS_NEXT_CONNECT(handle, svc, user); } static void egnyte_disconnect(vfs_handle_struct *handle) { PERR("%s: ENTRY\n",__func__); //flush_tlb = 21; free_compiled_patterns(); destroy_tlb(); flush_staging_buffer(); SMB_VFS_NEXT_DISCONNECT(handle); } static DIR *egnyte_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr) { PERR("%s: ENTRY\n",__func__); //flush_tlb = 21; char *full_path; DIR *ret = 0; int retval; char *nlpath = NULL; DEBUG(0, ("egnyte:%s\n",__func__)); /* resolve full path */ full_path = get_full_path(handle->conn->connectpath, fname); if(!full_path) { PERR("%s: unable to allocate full_path\n",__func__); return NULL; } nlpath = get_nullterminated_str((char *)fname,strlen(fname)); if (!nlpath) { free(full_path); return NULL; } retval = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPENDIR, NULL); free(nlpath); if (retval != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return NULL; } free(full_path); become_root(); ret = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); unbecome_root(); return ret; } static int egnyte_mkdir(vfs_handle_struct *handle, const char *path, mode_t mode) { PERR("%s: ENTRY\n",__func__); int result; char *full_path; int path_size; VFSEvent ve; char *nlpath = NULL; int became_root = 0; //flush_tlb = 21; PERR("egnyte:%s handle->conn->connectpath=%s path=%s\n",__func__,handle->conn->connectpath,path); full_path = get_full_path(handle->conn->connectpath, path); if (!full_path) { errno = ENOMEM; return -1; } nlpath = get_nullterminated_str(path,strlen(path)); if (!nlpath) { free(full_path); return ENOMEM; } //For test short circuit here // become_root(); //result = SMB_VFS_NEXT_MKDIR(handle, path, mode); // unbecome_root(); //free(full_path); //free(nlpath); //return result; //Remove the above lines later result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_MKDIR, NULL); free(nlpath); if (result != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return -1; } //become_root(); result = SMB_VFS_NEXT_MKDIR(handle, path, mode); //unbecome_root(); if (result < 0) { /* operation failed, dont log event */ DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); return result; } else { /* resolve full path */ path_size = strlen(full_path); vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_MKDIR; strncpy(ve.path, full_path, path_size); ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_DIR; PERR("%s(%d) calls _write_vfs_event\n",__FUNCTION__,__LINE__); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event open =================> %s\n",__FUNCTION__,__LINE__, ve.path)); free(ve.path); } free(full_path); return result; } static int egnyte_rmdir(vfs_handle_struct *handle, const char *path) { PERR("%s: ENTRY param=%s read_only=%d \n",__func__,handle->param,handle->conn->read_only); int result; char *full_path = NULL; int path_size; VFSEvent ve; char *nlpath = NULL; //flush_tlb = 21; full_path = get_full_path(handle->conn->connectpath, path); if (!full_path) { errno = ENOMEM; return -1; } nlpath = get_nullterminated_str(path,strlen(path)); if (!nlpath) { free(full_path); errno = ENOMEM; return -1; } #ifndef VFSPERMS_RWISRWD result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_RMDIR, NULL); #else //RW => RWD //result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPEN_WRITE, NULL); //changed for test PERR("%s: calls vfs_enforce_perms op=%d \n",__func__,FSOP_MKDIR); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_MKDIR, NULL); //PERR("%s: calls del_tlb_entry for path_nl=%s path_full=%s\n",__func__,nlpath,full_path); //del_tlb_entry(nlpath); PERR("%s: calls vfs_enforce_perms op=%d result=%d\n",__func__,FSOP_MKDIR,result); #endif free(nlpath); if (result != EGPERMS_HAS_PERMS) { //PERR("%s: calls del_tlb_entry for path_nl=%s path_full=%s\n",__func__,nlpath,full_path); //del_tlb_entry(nlpath); free(full_path); errno = EPERM; return -1; //return EPERM; } become_root(); result = SMB_VFS_NEXT_RMDIR(handle, path); unbecome_root(); if (result < 0) { /* operation failed, dont log event */ DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); free(full_path); return result; } else { /* resolve full path */ path_size = strlen(full_path); /* report results to shared memory */ vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_RMDIR; strncpy(ve.path,full_path, path_size); ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_DIR; _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event open =================> %s\n",__FUNCTION__,__LINE__, ve.path)); free(ve.path); } free(full_path); return result; } static int egnyte_open(vfs_handle_struct *handle, struct smb_filename *fname, files_struct *fsp, int flags, mode_t mode) { PERR("%s: ENTRY\n",__func__); int result; int fsop; e_open_mode_t open_mode; char *full_path; int path_size; VFSEvent ve; e_fsop_type_t op = FSOP_INVAL; char *nlpath = NULL; //flush_tlb = 21; open_mode = mode2egnytemode(flags); DEBUG(0,("Got open mode %d for flags %d\n",open_mode, flags)); full_path = get_full_path(handle->conn->connectpath, fname->base_name); if (!full_path) { errno = ENOMEM; return -1; } switch (open_mode) { case OPENMODE_READ: op = FSOP_OPEN_READ; break; case OPENMODE_WRITE: op = FSOP_OPEN_WRITE; break; case OPENMODE_CREATE: open_mode = OPENMODE_WRITE; op = FSOP_CREATE; break; case OPENMODE_CHATTR: open_mode = OPENMODE_WRITE; op = FSOP_OPEN_CHATTR; break; default: errno = EACCES; free(full_path); return -1; }; DEBUG(0,("Got open mode %d for flags %d and op %d\n",open_mode, flags, op)); nlpath = get_nullterminated_str(fname->base_name,strlen(fname->base_name)); DEBUG(0, ("After null path path %s len %d nlpath %s len %d\n", fname->base_name, strlen(fname->base_name), nlpath, strlen(nlpath))); if (!nlpath) { free(full_path); return ENOMEM; } //result = check_perms(handle->conn->connectpath, nlpath, op, NULL); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, op, NULL); free(nlpath); if (result != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return -1; } if (open_mode != OPENMODE_WRITE) { // if in read mode, dont process become_root(); result = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); unbecome_root(); free(full_path); return result; } become_root(); result = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); unbecome_root(); if (result < 0) { /* operation failed, dont log event */ DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); } else { if(!fsp->is_directory) { struct stat st; //Sometimes while browing folders for some reason the fsp->is_directory is still set to false for folders also so checking it again int err_stat = stat(full_path,&st); if(err_stat == 0) { if (S_ISDIR(st.st_mode)) { PERR("%s(%d) stat path=%s is a directory will not write to events file\n",__FUNCTION__,__LINE__,full_path); free(full_path); return result; } else { PERR("%s(%d) stat path=%s is not a directory will write to events file\n",__FUNCTION__,__LINE__,full_path); } } else { PERR("%s(%d) stat path=%s failed\n",__FUNCTION__,__LINE__,full_path); //let it just continue despite failure } /* resolve full path */ path_size = strlen(full_path); /* report results to shared memory */ vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_OPEN; //strncpy(ve.path, full_path, path_size + 1); strncpy(ve.path, full_path, path_size); ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_FILE; PERR("%s(%d) calls _write_vfs_event path=%s is_directory=%d\n",__FUNCTION__,__LINE__,full_path,fsp->is_directory); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event open =================> %s\n",__FUNCTION__,__LINE__, ve.path)); free(ve.path); } else { if(full_path) DEBUG(0,("%s(%d): egnyte_open path=%s is a directory will not write to events file\n",__FUNCTION__,__LINE__,full_path)); else DEBUG(0,("%s(%d): egnyte_open path is a directory will not write to events file\n",__FUNCTION__,__LINE__)); } } free(full_path); return result; } static int egnyte_close(vfs_handle_struct *handle, files_struct *fsp) { PERR("%s: ENTRY\n",__func__); //flush_tlb = 21; struct timeval tv; gettimeofday(&tv, NULL); if ( fsp->access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)) { /* * Not all files are closed with the name. But files are guaranteed to * be closed with a file descriptor */ int closefd; char *mpath = NULL; char *proc_path = NULL; int became_root = 0; int retlnk = 0; if (fsp && fsp->fh) { closefd = fsp->fh->fd; if (closefd > 0) { mpath = (char *)calloc(PATH_MAX+1,sizeof(char)); if (!mpath) { DEBUG(0,("Cannot allocate memory\n")); goto bail_out; } proc_path = (char *)calloc(PATH_MAX+1,sizeof(char)); if (!proc_path) { free(mpath); DEBUG(0,("Cannot allocate memory\n")); goto bail_out; } snprintf(proc_path, PATH_MAX, "/proc/%d/fd/%d", getpid(), closefd); if (geteuid() != 0) { become_root(); became_root = 1; } if ((retlnk = readlink(proc_path, mpath, PATH_MAX)) <= 0) { DEBUG(0,("Readlink operation on %s failed %s\n", proc_path, strerror(errno))); } if (became_root == 1) unbecome_root(); if (retlnk > 0) { VFSEvent ve; int path_size; path_size = strlen(mpath); /* report results to shared memory */ vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_FILE; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_OPEN; strncpy(ve.path, mpath, path_size); PERR("%s(%d) calls _write_vfs_event\n",__FUNCTION__,__LINE__); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event open (close)=================> %s\n", __FUNCTION__,__LINE__, ve.path)); free(ve.path); } free(proc_path); free(mpath); } else { DEBUG(0, ("invalid file descriptor %d in fsp\n", closefd)); } } else { /* * This is being overly parannoid but safe. How samba would * close a file without a filedescriptor is yet to be seen * But thats samba's problem */ DEBUG(0,("Missed event due to missing file descriptor in fsp\n")); } } bail_out: return SMB_VFS_NEXT_CLOSE(handle, fsp); } static int egnyte_rename(vfs_handle_struct *handle, const struct smb_filename *oldname, const struct smb_filename *newname) { PERR("%s: ENTRY\n",__func__); int result; char *old_full_path, *new_full_path; int old_path_size, new_path_size; VFSEvent ve; char *nlpath = NULL; char *nlpath_dst = NULL; int ap_ret = 0; int isdir = 0; int deletable = 0; e_fsop_type_t src_op; int mvret = 0; DEBUG(0, ("egnyte:%s\n",__func__)); DEBUG(8,("Rename case encountered\n")); //flush_tlb = 21; #ifndef VFSPERMS_RWISRWD DEBUG(0,("Checking rename permissions regex conf %d \n", global_params[PARAM_CHECK_REGEX_UNLINK].value.intval)); if (global_params[PARAM_CHECK_REGEX_UNLINK].value.intval > 0) { DEBUG(0,("Chehcking regex for %s\n",oldname->base_name)); deletable = is_deletable_file(oldname->base_name); } else { DEBUG(0,("Implying delete for %s\n",oldname->base_name)); deletable = 1; //check only RW permissions for delete for rename src } DEBUG(0,("Checking rename permissions regex conf %d deletable %d \n", global_params[PARAM_CHECK_REGEX_UNLINK].value.intval, deletable)); #else e_fsop_type_t dst_op; #endif DEBUG(8,("Getting old full path\n")); old_full_path = get_full_path(handle->conn->connectpath, oldname->base_name); if (!old_full_path) { errno = ENOMEM; return -1; } else { struct stat st; int became_root = 0; if (geteuid() != 0) { become_root(); became_root = 1; } DEBUG(8,("Calling stat\n")); if (stat(old_full_path, &st) != 0) { //rename source does not exist. DEBUG(0,("Rename source %s does not exist err %s\n",old_full_path, strerror(errno))); errno = ENOENT; if (became_root) { unbecome_root(); } return -1; } else { DEBUG(8,("stat successful\n")); if (S_ISDIR(st.st_mode)) { DEBUG(8,("path is a dir\n")); isdir = 1; } else { DEBUG(8,("path is not a dir\n")); isdir = 0; } } if (became_root) { unbecome_root(); } } DEBUG(8,("Getting new full path\n")); new_full_path = get_full_path(handle->conn->connectpath, newname->base_name); if (!new_full_path) { errno = ENOMEM; free(old_full_path); return -1; } nlpath = get_nullterminated_str(oldname->base_name,strlen(oldname->base_name)); if (!nlpath) { free(new_full_path); free(old_full_path); return ENOMEM; } nlpath_dst = get_nullterminated_str(newname->base_name,strlen(newname->base_name)); if (!nlpath_dst) { free(new_full_path); free(old_full_path); free(nlpath); errno = ENOMEM; return -1; } #ifndef VFSPERMS_RWISRWD mvret = is_amove(nlpath, nlpath_dst); if (mvret < 0) { DEBUG(0,("Could not assess whether move or rename. Assuming move src %s dst %s\n", oldname->base_name, newname->base_name)); src_op = FSOP_MOVE_SRC; dst_op = FSOP_MOVE_DEST; } else if (mvret == 1) { src_op = FSOP_MOVE_SRC; dst_op = FSOP_MOVE_DEST; DEBUG(0, ("This is a move src %s dst %s\n", oldname->base_name, newname->base_name)); } else { src_op = FSOP_RENAME_SRC; dst_op = FSOP_RENAME_DEST; } if (deletable) { DEBUG(0, ("Found a deletable file %s\n",oldname->base_name)); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPEN_WRITE, NULL); } else DEBUG(0, ("Is not a deletable file %s\n",oldname->base_name)); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, src_op, NULL); } #else /* * RW implies the delete permission flag */ src_op = FSOP_OPEN_WRITE; dst_op = FSOP_RENAME_DEST; // sync-611: for folder moves enforce permissions DEBUG(8,("isdir is %d\n", isdir)); if (isdir) { DEBUG(8,("Checking whether a move\n")); mvret = is_amove(nlpath, nlpath_dst); if (mvret < 0) { DEBUG(0,("Could not tell whether move or rename, denying...\n")); free(nlpath); free(old_full_path); free(new_full_path); free(nlpath_dst); errno = EACCES; return -1; } DEBUG(8,("mvret is %d\n", mvret)); if (mvret == 1) { src_op = FSOP_MOVE_SRC; dst_op = FSOP_MOVE_DEST; DEBUG(8, ("This is a move folder src %s dst %s\n", oldname->base_name, newname->base_name)); } else { DEBUG(8, ("This is not a move folder src %s dst %s\n", oldname->base_name, newname->base_name)); } } DEBUG(8, ("Checking source perms for %s, op %d\n", nlpath, src_op)); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, src_op, NULL); #endif free(nlpath); if (result != EGPERMS_HAS_PERMS) { DEBUG(8, ("Permission denied for source %s\n",oldname->base_name)); free(old_full_path); free(new_full_path); free(nlpath_dst); errno = EACCES; return -1; } result = vfs_enforce_perms(handle->conn->connectpath, nlpath_dst, dst_op, NULL); free(nlpath_dst); if (result != EGPERMS_HAS_PERMS) { free(old_full_path); free(new_full_path); errno = EACCES; return -1; } /* * Distinguish between the move and the rename */ become_root(); result = SMB_VFS_NEXT_RENAME(handle, oldname, newname); unbecome_root(); if (result < 0) { /* operation failed, dont log event */ DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); return result; } else { old_path_size = strlen(old_full_path); new_path_size = strlen(new_full_path); /* report results to shared memory */ vfsevent__init(&ve); ve.path = (char *)calloc(old_path_size + 1,sizeof(char)); ve.rename_path = (char *)calloc(new_path_size + 1,sizeof(char)); _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_RENAME; strncpy(ve.path, old_full_path, old_path_size); strncpy(ve.rename_path, new_full_path, new_path_size); if (isdir) { ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_DIR; } else { ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_FILE; } PERR("%s(%d) calls _write_vfs_event\n",__FUNCTION__,__LINE__); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event open =================> %s\n", __FUNCTION__,__LINE__, ve.path)); free(ve.path); free(ve.rename_path); } free(old_full_path); free(new_full_path); return result; } static int egnyte_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { PERR("%s: ENTRY\n",__func__); int result; char *nlpath = NULL; //flush_tlb = 21; nlpath = get_nullterminated_str(smb_fname->base_name,strlen(smb_fname->base_name)); if (!nlpath) { errno = ENOMEM; return -1; } result = check_perms(handle->conn->connectpath, nlpath, FSOP_STAT, NULL); //result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_STAT, NULL); free(nlpath); DEBUG(0,("Back from enforce perms\n")); if (result != EGPERMS_HAS_PERMS) { //errno = EACCES; errno = EPERM; return -1; } become_root(); result = SMB_VFS_NEXT_STAT(handle, smb_fname); unbecome_root(); DEBUG(0,("Back from next stat, result is: %d\n", result)); smb_fname->st.st_ex_mode |= S_IRWXO; //fake that others have rwx perms return result; } static int egnyte_lstat(vfs_handle_struct *handle, struct smb_filename *path) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); int ret = -1; char *full_path; int result; char *nlpath = NULL; //flush_tlb = 21; full_path = get_full_path(handle->conn->connectpath, path->base_name); if (!full_path) { errno = ENOMEM; return -1; } nlpath = get_nullterminated_str(path->base_name,strlen(path->base_name)); if (!nlpath) { free(full_path); return ENOMEM; } //result = check_perms(handle->conn->connectpath, nlpath, FSOP_LSTAT, NULL); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_LSTAT, NULL); free(nlpath); if (result != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return -1; } free(full_path); become_root(); ret = SMB_VFS_NEXT_LSTAT(handle, path); unbecome_root(); if (!is_ELC_Share(handle->conn->connectpath)) { DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); path->st.st_ex_mode |= S_IRWXO; return ret; } DEBUG (0, ("In egnyte lstat for path %s ret %d\n", path->base_name, ret)); if (ret == 0) { path->st.st_ex_mode |= S_IRWXO; } return ret; } static int egnyte_unlink(vfs_handle_struct *handle, const struct smb_filename *path) { PERR("%s: ENTRY\n",__func__); int result; char *full_path = NULL; int path_size; VFSEvent ve; char *nlpath = NULL; //flush_tlb = 21; /* resolve full path */ full_path = get_full_path(handle->conn->connectpath, path->base_name); if (!full_path) { errno = ENOMEM; return -1; } nlpath = get_nullterminated_str(path->base_name,strlen(path->base_name)); if (!nlpath) { free(full_path); return ENOMEM; } #ifndef VFSPERMS_RWISRWD /* * Check explicit unlink look out for the delete on close flag * Enforce unlink permissions on the delete on close flag */ { struct file_id id; struct share_mode_lock *lck = NULL; struct timespec old_write_time = path->st.st_ex_mtime; id = vfs_file_id_from_sbuf(handle->conn, &path->st); lck = get_share_mode_lock(talloc_tos(), id, handle->conn->connectpath, path, &old_write_time); if (lck == NULL) { DEBUG(0,("Could not get share mode lock\n")); free(full_path); free(nlpath); nlpath = NULL; errno = EACCES; return -1; } else { DEBUG(0,("Checking unlink permissions delete on close %d regex conf %d \n", lck->delete_on_close, global_params[PARAM_CHECK_REGEX_UNLINK].value.intval)); if (lck->delete_on_close) { /*enforce D perm */ result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_UNLINK, NULL); DEBUG(0,("Delete on close is set for %s\n",nlpath)); } else { /* * If a process has RW permissions D permissions is implicit for unlink for * temporary files */ if (global_params[PARAM_CHECK_REGEX_UNLINK].value.intval > 0) { DEBUG(0,("Chehcking regex for %s\n",nlpath)); if (is_deletable_file(nlpath)) { result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPEN_WRITE, NULL); } else { result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_UNLINK, NULL); } } else { DEBUG(0,("Implying Delete for %s\n",nlpath)); result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPEN_WRITE, NULL); } DEBUG(0,("Delete on close is not set set for %s\n", nlpath)); } free(nlpath); nlpath = NULL; } TALLOC_FREE(lck); } #else /* * RW implies RWD */ result = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_OPEN_WRITE, NULL); free(nlpath); nlpath = NULL; #endif if (result != EGPERMS_HAS_PERMS) { free(full_path); if(nlpath) free(nlpath); errno = EACCES; return -1; } become_root(); result = SMB_VFS_NEXT_UNLINK(handle, path); unbecome_root(); if (result < 0) { DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); /* operation failed, dont log event */ return result; } else { /* report results to shared memory */ path_size = strlen(full_path); vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_UNLINK; strncpy(ve.path, full_path,path_size); ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_FILE; PERR("%s(%d) calls _write_vfs_event\n",__FUNCTION__,__LINE__); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event unlink =================> %s\n", __FUNCTION__,__LINE__, ve.path)); free(ve.path); } free(full_path); return result; } /* static int egnyte_chmod(vfs_handle_struct *handle, const char *path, mode_t mode) { PERR("%s: ENTRY\n",__func__); int result; //flush_tlb = 21; DEBUG(0, ("egnyte:%s\n",__func__)); // permission changes have little impact on permissions now :) result = SMB_VFS_NEXT_CHMOD(handle, path, mode); if (result == 0) { DEBUG(0, ("Permission set did not succeed %d\n", errno)); } return 0; } static int egnyte_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { PERR("%s: ENTRY\n",__func__); int result; DEBUG(0, ("egnyte:%s\n",__func__)); //flush_tlb = 21; // permission changes have little impact on permissions now :) result = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); if (result == 0) { DEBUG(0, ("Permission set did not succeed %d\n", errno)); } return 0; } */ static int egnyte_chmod_acl(vfs_handle_struct *handle, const char *path, mode_t mode) { PERR("%s: ENTRY\n",__func__); //flush_tlb = 21; return 0; } static int egnyte_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { PERR("%s: ENTRY\n",__func__); //flush_tlb = 21; return 0; } static egnyte_acl_set_file(vfs_handle_struct *conn, const char *to, SMB_ACL_TYPE_T type, SMB_ACL_T acl) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("Silently failing permissions to set perms %s\n", __FUNCTION__)); return 0; } static egnyte_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T acl) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); DEBUG(0, ("silently failing permissions to set perms %s\n", __FUNCTION__)); return 0; } static int egnyte_chdir(vfs_handle_struct *handle, const char *path) { PERR("%s: ENTRY\n",__func__); int result; int retval; char *nlpath = NULL; //flush_tlb = 21; nlpath = get_nullterminated_str(path,strlen(path)); if (!nlpath) { return ENOMEM; } //retval = check_perms(handle->conn->connectpath, nlpath, FSOP_CHDIR, NULL); retval = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_CHDIR, NULL); if (retval != EGPERMS_HAS_PERMS) { errno = EACCES; free(nlpath); return -1; } DEBUG(0,("Enabled chdir for '%s'\n",nlpath)); free(nlpath); become_root(); result = SMB_VFS_NEXT_CHDIR(handle, path); unbecome_root(); return result; } static int egnyte_statvfs(struct vfs_handle_struct *handle, const char *path, struct vfs_statvfs_struct *statbuf) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); int result; char *full_path; int retval; char *nlpath = NULL; //flush_tlb = 21; full_path = get_full_path(handle->conn->connectpath, path); if (!full_path) { errno = ENOMEM; return -1; } nlpath = get_nullterminated_str(path,strlen(path)); if (!nlpath) { free(full_path); return ENOMEM; } //retval = check_perms(handle->conn->connectpath, nlpath, FSOP_STATVFS, NULL); retval = vfs_enforce_perms(handle->conn->connectpath, nlpath, FSOP_STATVFS, NULL); free(nlpath); if (retval != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return -1; } DEBUG(0,("Enabled statvfs for %s\n",full_path)); free(full_path); become_root(); result = SMB_VFS_NEXT_STATVFS(handle, path, statbuf); unbecome_root(); return result; } /* static ssize_t egnyte_getxattr(struct vfs_handle_struct *handle, const char *path, const char *name, void *value, size_t size) { PERR("%s: ENTRY\n",__func__); ssize_t result; int became_root = 0; //flush_tlb = 21; if (geteuid() != 0) { become_root(); became_root = 1; } result = SMB_VFS_NEXT_GETXATTR(handle, path, name, value, size); if(became_root) { unbecome_root(); } return result; } static int egnyte_setxattr(struct vfs_handle_struct *handle, const char *path, const char *name, const void *value, size_t size, int flags) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); //flush_tlb = 21; int result; int became_root = 0; if (geteuid() != 0) { become_root(); became_root = 1; } result = SMB_VFS_NEXT_SETXATTR(handle, path, name, value, size, flags); if(became_root) { unbecome_root(); } return result; } */ /* static uint64_t egnyte_disk_free(vfs_handle_struct *handle, const char *path, bool small_query, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize) { DEBUG(0, ("egnyte:%s\n",__func__)); uint64_t result; int became_root = 0; if(geteuid() != 0) { become_root(); became_root =1; } result = SMB_VFS_NEXT_DISK_FREE(handle, path, small_query, bsize, dfree, dsize); if (became_root == 1) { unbecome_root(); } return result; } */ static int egnyte_ntimes(vfs_handle_struct *handle, const struct smb_filename *smb_fname, struct smb_file_time *ft) { PERR("%s: ENTRY\n",__func__); int result; char *full_path = NULL; int path_size; VFSEvent ve; int became_root = 0; DEBUG(0, ("egnyte:%s\n",__func__)); //flush_tlb = 21; /* No need to report time changes on dirs */ if (S_ISDIR(smb_fname->st.st_ex_mode)) { if (geteuid() != 0) { become_root(); became_root = 1; } result = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft); if (became_root == 1) { unbecome_root(); } return result; } full_path = get_full_path(handle->conn->connectpath, smb_fname->base_name); if (!full_path) { errno = ENOMEM; return -1; } if (geteuid() != 0) { become_root(); became_root = 1; } result = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft); if (became_root == 1) { unbecome_root(); } if (result < 0) { /* operation failed, dont log event */ DEBUG(0,("(%s:%d) Failed event \n",__FUNCTION__,__LINE__)); } else { /* resolve full path */ path_size = strlen(full_path); /* report results to shared memory */ vfsevent__init(&ve); ve.path = (char *)calloc(path_size + 1,sizeof(char)); ve.rename_path = NULL; _pop_vfs_event(&ve); ve.optype = VFSEVENT__OP_TYPE__OPTYPE_OPEN; strncpy(ve.path, full_path,path_size); ve.fselem_type = VFSEVENT__FS_ELEM_TYPE__FSELEMTYPE_FILE; PERR("%s(%d) calls _write_vfs_event\n",__FUNCTION__,__LINE__); _write_vfs_event(&ve); DEBUG(0,("%s(%d):wrote VFS event ntimes =================> %s\n", __FUNCTION__,__LINE__, ve.path)); free(ve.path); } free(full_path); return result; } static char *egnyte_realpath(vfs_handle_struct *handle,const char *path) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); char *result; #if 0 char *full_path; int retval; full_path = get_full_path(handle->conn->connectpath, path); if (full_path == NULL ) { errno = ENOMEM; return NULL; } DEBUG(0,("Checking realpath for %s\n",full_path)); retval = vfs_enforce_perms(path, FSOP_REALPATH, NULL); if (retval != EGPERMS_HAS_PERMS) { free(full_path); errno = EACCES; return NULL; } free(full_path); #endif become_root(); result = SMB_VFS_NEXT_REALPATH(handle, path); unbecome_root(); return result; } static void egnyte_doctor_acl(struct security_descriptor **ppdesc) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); struct security_ace *ace = NULL; struct security_acl *theACL = NULL; struct security_descriptor *pdesc; int num_aces =0; pdesc = *ppdesc; if (pdesc == NULL) { DEBUG(0, ("No DACL\n")); return; } theACL = pdesc->dacl; if (!theACL) { DEBUG(0, ("No DACL present\n")); return; } if (theACL->num_aces == 0) { DEBUG(0, ("No ACES in DACL \n")); return; } for (num_aces = 0; num_aces < theACL->num_aces; num_aces ++) { ace = &(theACL->aces[num_aces]); if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) { DEBUG(0,("Setting ACE perms old %0x new %0x\n",ace->access_mask, UNIX_ACCESS_RWX)); ace->access_mask = UNIX_ACCESS_RWX; } } return; } static NTSTATUS egnyte_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info,TALLOC_CTX *mem_ctx,struct security_descriptor **ppdesc) { PERR("%s: ENTRY\n",__func__); NTSTATUS result; DEBUG(0, ("egnyte:%s\n",__func__)); //flush_tlb = 21; become_root(); result = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, mem_ctx,ppdesc); egnyte_doctor_acl(ppdesc); unbecome_root(); return result; } static NTSTATUS egnyte_get_nt_acl(vfs_handle_struct *handle, const char *name, uint32 security_info, TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc) { PERR("%s: ENTRY\n",__func__); NTSTATUS result; DEBUG(0, ("egnyte:%s\n",__func__)); //flush_tlb = 21; become_root(); result = SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info, mem_ctx,ppdesc); egnyte_doctor_acl(ppdesc); unbecome_root(); return result; } static int read_egnyte_config(void) { PERR("%s: ENTRY\n",__func__); DEBUG(0, ("egnyte:%s\n",__func__)); key_t shmk; int shmid; void *addr = NULL; size_t len; /* * Allocate the memory to workaround a problem with the shared memory * appearing to get detached by a child process. This memory is not * freed on disconnect in case a connect/connect/disconnect/disconnect * sequence occurs #63985. It will be freed when the process exits. */ len = sizeof(global_param_t)*PARAM_MAX_INDEX; global_params = (global_param_t *)realloc(global_params, len); if (!global_params) { PERR("could not allocate global_params buffer\n"); return -1; } if ((shmk = ftok(CONFIG_SHM_PATH, CONFIG_SHM_PROJECT)) == (key_t)-1) { PERR("cannot create sema key for shm. errno = %d(%s)\n", errno, strerror(errno)); return -1; } if ((shmid = shmget(shmk, len, IPC_CREAT|0666)) < 0) { PERR("cannot create shared memory for global param. errno = %d(%s)\n", errno, strerror(errno)); return -1; } if ((addr = shmat(shmid, 0, 0)) == (void *)-1) { PERR("cannot attach to shm. errno = %d(%s)\n", errno, strerror(errno)); return -1; } memset((void *)global_params, '\0', len); memcpy((void *)global_params, addr, len); shmdt(addr); } /* VFS operations structure */ static struct vfs_fn_pointers vfs_egnyte_fns = { .connect_fn = egnyte_connect, .disconnect_fn = egnyte_disconnect, .opendir_fn = egnyte_opendir, .mkdir_fn = egnyte_mkdir, .rmdir_fn = egnyte_rmdir, .open_fn = egnyte_open, .close_fn = egnyte_close, .rename_fn = egnyte_rename, .unlink_fn = egnyte_unlink, // .chmod_fn = egnyte_chmod, // .fchmod_fn = egnyte_fchmod, .chmod_acl_fn = egnyte_chmod_acl, .sys_acl_set_fd_fn = egnyte_acl_set_fd, .sys_acl_set_file_fn = egnyte_acl_set_file, // .setxattr_fn = egnyte_setxattr, // .getxattr_fn = egnyte_getxattr, .stat_fn = egnyte_stat, .lstat_fn = egnyte_lstat, .realpath_fn = egnyte_realpath, .fget_nt_acl_fn = egnyte_fget_nt_acl, .get_nt_acl_fn = egnyte_get_nt_acl, .chdir_fn = egnyte_chdir, // .disk_free_fn = egnyte_disk_free, .ntimes_fn = egnyte_ntimes }; #define vfs_egnyte_init init_samba_module static void free_compiled_patterns(void) { DEBUG(0, ("egnyte:%s\n",__func__)); int cnt; for(cnt = 0; cnt < pattern_buf.num_elems; cnt ++) { if (pattern_buf.elems[cnt].pattern) { free(pattern_buf.elems[cnt].pattern); } } } static int read_and_compile_patterns(void) { DEBUG(0, ("egnyte:%s\n",__func__)); FILE* fp; struct stat st; char *buf; size_t bufsize=1024; int indx=0; if (global_params[PARAM_REGEX_FILE_NAME].value.strval[0] == '\0') { PERR("no regex file specified\n"); return -1; } if (stat(global_params[PARAM_REGEX_FILE_NAME].value.strval, &st) != 0) { PERR("regular expression file does not exist\n"); return -1; } if ((fp = fopen(global_params[PARAM_REGEX_FILE_NAME].value.strval, "r")) == NULL) { PERR("cannot open regular expression file\n"); return -1; } buf = (char*)calloc(bufsize,sizeof(char)); pattern_buf.num_elems = 0; while (getline(&buf, &bufsize, fp) > 0) { if (indx < MAX_REGEX_PATTERNS) { //precompute the pattern buffers for matching pattern_buf.elems[indx].compiled_pattern = NULL; pattern_buf.elems[indx].pattern = (char *)malloc(strlen(buf)+1); memset(pattern_buf.elems[indx].pattern, '\0', strlen(buf)+1); strncpy(pattern_buf.elems[indx].pattern, buf, strlen(buf)-1); if (get_regex_matchbuf(pattern_buf.elems[indx].pattern, &pattern_buf.elems[indx].compiled_pattern) == REGEX_ERROR) { free(pattern_buf.elems[indx].pattern); continue; } PDBG("read pattern %s\n", pattern_buf.elems[indx].pattern); } pattern_buf.num_elems ++; indx++; memset(buf, '\0', bufsize); } free(buf); } /* * return 1 if match, 0 if mismatch */ static int is_deletable_file(char *fname) { int indx; DEBUG(0, ("Checking for deletable file\n")); for (indx = 0; indx < pattern_buf.num_elems; indx++) { if (match_string_with_regex_buf(fname, pattern_buf.elems[indx].compiled_pattern) == REGEX_TRUE) { DEBUG(0, ("Found deletable file %s\n",fname)); return 1; } } return 0; } NTSTATUS vfs_egnyte_init(void) { PERR("Loading egnyte module...\n"); // since we chown everything to root, we do not want umask as 022. umask(0); return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "egnyte", &vfs_egnyte_fns); } NTSTATUS samba_init_module(void) { PERR("%s: ENTRY\n",__func__); return vfs_egnyte_init(); }