/* * * Block access from links to dev mount points specified in PARAMCONF file * * Copyright (C) Ronald Kuetemeier, 2001 * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_UTIME_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #include #ifdef HAVE_FCNTL_H #include #endif #include #include DIR *block_opendir(struct connection_struct *conn, char *fname); int block_connect(struct connection_struct *conn, char *service, char *user); void block_disconnect(struct connection_struct *conn); /* VFS operations */ extern struct vfs_ops default_vfs_ops; /* For passthrough operation */ struct vfs_ops execute_vfs_ops = { /* Disk operations */ block_connect, block_disconnect, NULL, /* disk free */ /* Directory operations */ block_opendir, NULL, /* readdir */ NULL, NULL, NULL, /* closedir */ /* File operations */ NULL, NULL, NULL, /* read */ NULL, /* write */ NULL, /* lseek */ NULL, NULL, /* fsync */ NULL, /* stat */ NULL, /* fstat */ NULL, /* lstat */ NULL, NULL, NULL, NULL, /* chown */ NULL, NULL, /* chdir */ NULL, /* getwd */ NULL, /* utime */ NULL, /* ftruncate */ NULL, /* lock */ NULL, /* fget_nt_acl */ NULL, /* get_nt_acl */ NULL, /* fset_nt_acl */ NULL, /* set_nt_acl */ NULL, NULL }; #ifndef PARAMCONF #define PARAMCONF "/etc/samba-block.conf" #endif extern BOOL pm_process(char *FileName, BOOL (*sfunc)(char *), BOOL(*pfunc)(char * , char *)); //functions BOOL enter_pblock_mount(char *dir); BOOL get_section(char *sect); BOOL get_parameter_value(char *param, char *value); BOOL load_param(void); BOOL search(struct stat *stat_buf); BOOL dir_search(char *link, char *dir); BOOL enter_pblock_dir(char *dir); typedef struct block_dir { dev_t st_dev; int str_len; char *dir_name; struct block_dir *next; } block_dir; static char *params[] = {"mount_point","dir_name"}; enum { MOUNT_POINT , DIR_NAME }; static struct block_dir *pblock_mountp = NULL; static struct block_dir *pblock_dir = NULL; /* * Load the conf file into a table */ BOOL load_param(void) { if ((pm_process(PARAMCONF,&get_section,&get_parameter_value)) == TRUE) { return TRUE; } return FALSE; } /* * Enter the key and data into the list * */ BOOL enter_pblock_mount(char *dir) { struct stat stat_buf; static struct block_dir *tmp_pblock; if((stat(dir,&stat_buf)) != 0) { return FALSE; } if(pblock_mountp == NULL) { pblock_mountp = calloc(1, sizeof(block_dir)); if( pblock_mountp == NULL) { return FALSE; } tmp_pblock = pblock_mountp; tmp_pblock->next = NULL; }else { tmp_pblock->next = calloc(1, sizeof(block_dir)); if(tmp_pblock->next == NULL) { return FALSE; } tmp_pblock = tmp_pblock->next; tmp_pblock->next = NULL; } tmp_pblock->st_dev = stat_buf.st_dev; tmp_pblock->dir_name = strdup(dir); return TRUE; } /* * Enter the key and data into the list * */ BOOL enter_pblock_dir(char *dir) { static struct block_dir *tmp_pblock; if(pblock_dir == NULL) { pblock_dir = calloc(1, sizeof(block_dir)); if( pblock_dir == NULL) { return FALSE; } tmp_pblock = pblock_dir; tmp_pblock->next = NULL; }else { tmp_pblock->next = calloc(1, sizeof(block_dir)); if(tmp_pblock->next == NULL) { return FALSE; } tmp_pblock = tmp_pblock->next; tmp_pblock->next = NULL; } tmp_pblock->dir_name = strdup(dir); tmp_pblock->str_len = strlen(dir); return TRUE; } /* * Function callback for config section names */ BOOL get_section(char *sect) { return TRUE; } /* * Function callback for config parameter value pairs * */ BOOL get_parameter_value(char *param, char *value) { int i = 0, maxargs = sizeof(params) / sizeof(char *); for( i= 0; i < maxargs; i++) { if (strcmp(param,params[i]) == 0) { switch(i) { case MOUNT_POINT : enter_pblock_mount(value); break; case DIR_NAME : enter_pblock_dir(value); break; default : break; } } } return TRUE; } /* VFS initialisation function. Return initialised vfs_ops structure back to SAMBA. */ struct vfs_ops *vfs_init(int *vfs_version) { *vfs_version = SMB_VFS_INTERFACE_VERSION; return(&execute_vfs_ops); } /* * VFS connect and param file loading */ int block_connect(struct connection_struct *conn, char *service, char *user) { if((load_param()) == FALSE) { return -1; } DEBUG(0,("%s connecting \n",conn->user)); return (default_vfs_ops.connect(conn, service,user)); } /* * Free allocated structures and disconnect * */ void block_disconnect(struct connection_struct *conn) { struct block_dir *tmp_pblock = (pblock_mountp == NULL ? pblock_dir : pblock_mountp); struct block_dir *free_pblock = NULL; while(tmp_pblock != NULL) { free(tmp_pblock->dir_name); free_pblock = tmp_pblock; tmp_pblock = tmp_pblock->next; free(free_pblock); if(tmp_pblock == NULL && pblock_dir != NULL) { tmp_pblock = (pblock_mountp == NULL ? pblock_dir : NULL); pblock_dir = NULL; } } default_vfs_ops.disconnect(conn); } /* * VFS opendir */ DIR *block_opendir(struct connection_struct *conn, char *fname) { char *dir_name = NULL; struct stat stat_buf; dir_name = alloca((strlen(conn->origpath) + strlen(fname) + 2) * sizeof(char)); pstrcpy(dir_name,conn->origpath); pstrcat(dir_name, "/"); strncat(dir_name, fname, strcspn(fname,"/")); if((lstat(dir_name,&stat_buf)) == 0) { if((S_ISLNK(stat_buf.st_mode)) == 1) { stat(dir_name,&stat_buf); if((search(&stat_buf) || dir_search(dir_name, fname) ) == TRUE) { DEBUG(0,("%s used link to blocked dir: %s \n", conn->user, dir_name)); errno = EACCES; return NULL; } } } return (default_vfs_ops.opendir(conn, fname)); } /* * Find mount point to block in list */ BOOL search(struct stat *stat_buf) { struct block_dir *tmp_pblock = pblock_mountp; while(tmp_pblock != NULL) { if(tmp_pblock->st_dev == stat_buf->st_dev) { return TRUE; } tmp_pblock = tmp_pblock->next; } return FALSE; } /* * Find dir in list to block id the starting point is link from a share */ BOOL dir_search(char *link, char *dir) { char buf[PATH_MAX +1], *ext_path; int len = 0; struct block_dir *tmp_pblock = pblock_dir; if((len = readlink(link,buf,sizeof(buf))) == -1) { return TRUE; }else { buf[len] = '\0'; } if((ext_path = strchr(dir,'/')) != NULL) { pstrcat(buf,&ext_path[1]); len = strlen(buf); } while(tmp_pblock != NULL) { if(len < tmp_pblock->str_len) { tmp_pblock = tmp_pblock->next; continue; } if((strstr(buf,tmp_pblock->dir_name)) != NULL) { return TRUE; } tmp_pblock = tmp_pblock->next; } return FALSE; }