#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #define _FCNTL_H #include #include #include #include #include #define MAXPATHLEN PATH_MAX #define MAXPROCPATHLEN 512 #define MAXSOCKETPATHLEN 108 #define MAXFILEBUFFLEN 2048 #define MAXSOCKETMSGLEN 8192 #define MAXENVSIZE 65536 #define MAXENVITEMSIZE 256 #define MAXARGS 1024 //extern int errorno; pthread_mutex_t socketblock = PTHREAD_MUTEX_INITIALIZER; int (*_open)(const char * pathname, int flags, ...); int (*_open64)(const char * pathname, int flags, ...); FILE * (*_fopen)(const char *path, const char *mode); FILE * (*_fopen64)(const char *path, const char *mode); ssize_t (*_read)(int fd, void *buf, size_t count); ssize_t (*_write)(int fd, const void *buf, size_t count); size_t (*_fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t (*_fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream); void *(*_mmap)(void *addr, size_t length, int prot, int flags, int fd, off_t offset); struct dirent * (*_readdir)(DIR *dirp); struct dirent64 * (*_readdir64)(DIR *dirp); int (*_readdir_r)(DIR *dirp, struct dirent *entry, struct dirent **result); int (*_readdir64_r)(DIR *dirp, struct dirent64 *entry, struct dirent64 **result); int (*_access)(const char *pathname, int mode); int (*_euidaccess)(const char *pathname, int mode); int (*_faccessat)(int dirfd, const char *pathname, int mode, int flags); // these stat functions are only source level standart of glibc // we no catch fxstat int (*___xstat)(int vers, const char *name, struct stat *buf); int (*___xstat64)(int vers, const char *name, struct stat64 *buf); int (*___lxstat)(int vers, const char *name, struct stat *buf); int (*___lxstat64)(int vers, const char *name, struct stat64 *buf); int (*_execve)(const char *filename, char *const argv[],char *const envp[]); int (*_execv)(const char *path, char *const argv[]); int (*_execvp)(const char *file, char *const argv[]); int (*_execvpe)(const char *file, char *const argv[], char *const envp[]); int (*_fexecve)(int fd, char *const argv[], char *const envp[]); int (*_system)(const char *command); pid_t (*_fork)(); int (*_setenv)(const char *name, const char *value, int overwrite); int (*_close)(int fd); // we hooking this, because some programs closes our socket // we not hook this functions but we should be sure that these functions // are from glibc int (*_snprintf)(char *str, size_t size, const char *format, ...); int is_initialized=0; // when init not lauched yet we should no do any actions int log_socket=-1; char ld_preload_orig[MAXPATHLEN]; char log_socket_name[MAXSOCKETPATHLEN]; char ld_preload_env[MAXENVITEMSIZE]; // value: LD_PRELOAD=ld_preload_orig char log_socket_env[MAXENVITEMSIZE]; // value: LOG_SOCKET=log_socket_name void __init_hooks() { if(is_initialized) return; char *log_socket_val=getenv("LOG_SOCKET"); if(log_socket_val==NULL) { fprintf(stderr,"LOG_SOCKET environment variable isn't defined." "Are this library launched by server?\n"); exit(1); } if(strlen(log_socket_val)>=MAXSOCKETPATHLEN) { fprintf(stderr,"Unable to create a unix-socket %s: socket name is too long,exiting\n", log_socket_name); exit(1); } strcpy(log_socket_name,log_socket_val); if(getenv("LD_PRELOAD")==NULL) { fprintf(stderr,"Unable to find LD_PRELOAD environment variable. " "Library will load only with this variable defined"); exit(1); } strcpy(ld_preload_orig,getenv("LD_PRELOAD")); _open = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open"); _open64 = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open64"); _fopen = (FILE * (*)(const char *path, const char *mode)) dlsym(RTLD_NEXT, "fopen"); _fopen64 = (FILE * (*)(const char *path, const char *mode)) dlsym(RTLD_NEXT, "fopen64"); _read= (ssize_t (*)(int fd, void *buf, size_t count)) dlsym(RTLD_NEXT, "read"); _write= (ssize_t (*)(int fd, const void *buf, size_t count)) dlsym(RTLD_NEXT, "write"); _mmap=(void* (*)(void *addr, size_t length, int prot, int flags, int fd, off_t offset)) dlsym(RTLD_NEXT, "mmap"); _readdir=(struct dirent * (*)(DIR *dirp)) dlsym(RTLD_NEXT, "readdir"); _readdir64=(struct dirent64 * (*)(DIR *dirp)) dlsym(RTLD_NEXT, "readdir64"); _readdir_r=(int (*)(DIR *dirp, struct dirent *entry, struct dirent **result)) dlsym(RTLD_NEXT, "readdir_r"); _readdir64_r=(int (*)(DIR *dirp, struct dirent64 *entry, struct dirent64 **result)) dlsym(RTLD_NEXT, "readdir64_r"); _access=(int (*)(const char *pathname, int mode)) dlsym(RTLD_NEXT, "access"); _euidaccess=(int (*)(const char *pathname, int mode)) dlsym(RTLD_NEXT, "euidaccess"); _faccessat=(int(*)(int dirfd, const char *pathname, int mode, int flags)) dlsym(RTLD_NEXT, "faccessat"); ___xstat=(int (*)(int vers, const char *name, struct stat *buf)) dlsym(RTLD_NEXT, "__xstat"); ___xstat64=(int (*)(int vers, const char *name, struct stat64 *buf)) dlsym(RTLD_NEXT, "__xstat64"); ___lxstat=(int (*)(int vers, const char *name, struct stat *buf)) dlsym(RTLD_NEXT, "__lxstat"); ___lxstat64=(int (*)(int vers, const char *name, struct stat64 *buf)) dlsym(RTLD_NEXT, "__lxstat64"); _fork = (pid_t (*)()) dlsym(RTLD_NEXT, "fork"); _execve = (int (*)(const char *filename, char *const argv[],char *const envp[])) dlsym(RTLD_NEXT, "execve"); _execv = (int (*)(const char *path, char *const argv[])) dlsym(RTLD_NEXT, "execv"); _execvp = (int (*)(const char *file, char *const argv[])) dlsym(RTLD_NEXT, "execvp"); _execvpe = (int (*)(const char *file, char *const argv[], char *const envp[])) dlsym(RTLD_NEXT, "execvpe"); _fexecve = (int (*)(int fd, char *const argv[], char *const envp[])) dlsym(RTLD_NEXT, "fexecve"); _system = (int (*)(const char *command)) dlsym(RTLD_NEXT, "system"); _setenv=(int (*)(const char *name, const char *value, int overwrite)) dlsym(RTLD_NEXT, "setenv"); _close= (int (*)(int fd)) dlsym(RTLD_NEXT, "close"); _snprintf=(int (*)(char *str, size_t size, const char *format, ...)) dlsym(RTLD_NEXT, "snprintf"); if(_open==NULL || _open64==NULL || _fopen==NULL || _fopen64==NULL || _read==NULL || _write==NULL || _mmap==NULL || _readdir==NULL || _readdir64 == NULL || _readdir_r==NULL || _readdir64_r==NULL || _access==NULL || _euidaccess==NULL || _faccessat==NULL || _fork==NULL || _execve==NULL || _execv==NULL || _execvp==NULL || _execvpe==NULL || _fexecve==NULL || _system==NULL || _setenv==NULL || _close==NULL) { fprintf(stderr,"Failed to load original functions of hook\n"); exit(1); } snprintf(ld_preload_env,MAXENVITEMSIZE,"LD_PRELOAD=%s",ld_preload_orig); snprintf(log_socket_env,MAXENVITEMSIZE,"LOG_SOCKET=%s",log_socket_name); is_initialized=1; } void __doconnect(){ if(! is_initialized) __init_hooks(); if(strlen(log_socket_name)>=MAXSOCKETPATHLEN) { fprintf(stderr,"Unable to create a unix-socket %s: socket name is too long,exiting\n", log_socket_name); exit(1); } log_socket=socket(AF_UNIX, SOCK_SEQPACKET, 0); if(log_socket==-1) { fprintf(stderr,"Unable to create a unix-socket %s: %s\n", log_socket_name, strerror(errno)); exit(1); } struct sockaddr_un serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sun_family = AF_UNIX; strcpy(serveraddr.sun_path, log_socket_name); int ret=connect(log_socket, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr)); if(ret==-1) { fprintf(stderr,"Unable to connect a unix-socket %s: %s\n",log_socket_name, strerror(errno)); fflush(stderr); exit(1); } } void __dodisconnect() { close(log_socket); } void __doreconnect() { __doconnect(); __dodisconnect(); } // this fucnction executes when library is loaded void _init() { __init_hooks(); __doconnect(); } void _fini() { __dodisconnect(); } /* * Format of log string: time event filename stage result/err */ static int __raw_log_event(const char *event_type, const char *filename, char *result,int err, char* stage) { if(! is_initialized) // it is essential to initialize hooks because we are __init_hooks(); // using _snprintf here char msg_buff[MAXSOCKETMSGLEN]; int bytes_to_send; if(strcmp(result,"ERR")==0) { bytes_to_send=_snprintf(msg_buff,MAXSOCKETMSGLEN,"%lld%c%s%c%s%c%s%c%s/%d", (unsigned long long)time(NULL),0,event_type,0,filename,0,stage,0,result,err); } else { bytes_to_send=_snprintf(msg_buff,MAXSOCKETMSGLEN,"%lld%c%s%c%s%c%s%c%s", (unsigned long long)time(NULL),0,event_type,0,filename,0,stage,0,result); } if(bytes_to_send>=MAXSOCKETMSGLEN) return 0; // we need to recount bytes_to_send here because some programs // use hackish snprintf which returns strlen(buf) if(send(log_socket,msg_buff,bytes_to_send,0)==-1) { __doreconnect(); // looks like our socket has been destroyed by logged program // try to recreate it if(send(log_socket,msg_buff,bytes_to_send,0)==-1) return 0; } return 1; } /* * Log an event */ static int __log_event(const char *event_type, const char *filename, char *result,int err, char* stage) { pthread_mutex_lock( &socketblock ); int ret=__raw_log_event(event_type,filename,result,err,stage); pthread_mutex_unlock( &socketblock ); return ret; } /* * Get a stage. Stage is from environment */ static char * __get_stage(){ char *ret=getenv("EBUILD_PHASE"); if(ret==NULL) return "unknown"; return ret; } /* * Get full path by fd */ ssize_t __get_path_by_fd(int fd, char *output, int output_len) { char path_to_fd_link[MAXPROCPATHLEN]; snprintf(path_to_fd_link,MAXPROCPATHLEN,"/proc/self/fd/%d",fd); ssize_t bytes_num=readlink(path_to_fd_link,output,output_len-1); output[bytes_num]=0; // because readlink don't do this if(output[0]!='/') return -1; // some odd string like pipe: return bytes_num; } /* * Realpath. Resolved path is empty string on error. If path not exists, * returns the path as is, but resolving current dir . Arguments must be not null */ char *myrealpath(const char *path, char *resolved){ char *ret=realpath(path,resolved); if(ret!=NULL) return ret; if(path==NULL) goto error; // difficult case: error returned char *dest; if(path[0]!='/') { if(! getcwd(resolved,MAXPATHLEN-2)) goto error; dest= resolved+strlen(resolved); dest[0]='/'; dest++; dest[0]='\0'; } else { dest = resolved; } char *border=resolved+MAXPATHLEN-1; // not crossing it and not stepping on it if(dest>=border) goto error; strncpy(dest, path, border-dest); return resolved; error: resolved[0]=0; return NULL; } /* * Ask for event "alloweness" */ static int __is_event_allowed(const char *event_type,const char *filename, char* stage) { char answer[8]; int bytes_recieved; //printf("asking %s\n",filename); pthread_mutex_lock( &socketblock ); __raw_log_event(event_type,filename,"ASKING",0,stage); bytes_recieved=recv(log_socket,answer,8,0); if(bytes_recieved==-1) { __doreconnect(); // looks like our socket has been destroyed by logged program // try to recreate it bytes_recieved=recv(log_socket,answer,8,0); } pthread_mutex_unlock( &socketblock ); if(strcmp(answer,"ALLOW")==0) { return 1; } else if(strcmp(answer,"DENY")==0) return 0; else { fprintf(stderr,"Protocol error, text should be ALLOW or DENY, got: '%s' recv retcode=%d(%s)" " socket=%d",answer,bytes_recieved,strerror(errno),log_socket); exit(1); } return 0; } void __fixenv() { _setenv("LOG_SOCKET",log_socket_name,1); _setenv("LD_PRELOAD",ld_preload_orig,1); snprintf(ld_preload_env,MAXENVITEMSIZE,"LD_PRELOAD=%s",ld_preload_orig); snprintf(log_socket_env,MAXENVITEMSIZE,"LOG_SOCKET=%s",log_socket_name); } /* * Fixes LD_PRELOAD and LOG_SOCKET in envp and puts modified value in envp_new */ void __fixenvp(char *const envp[], char *envp_new[]) { int i; int j=0; for(i=0; envp[i] && id_name); char abspath[MAXPATHLEN]; myrealpath(fullpath,abspath); if(! __is_event_allowed("open",abspath,stage)) { //__log_event("open",abspath,"DENIED",errno,stage); continue; } else break; } return ep; } struct dirent64 *readdir64(DIR *dirp) { char *stage=__get_stage(); struct dirent64 *ep; char dirpath[MAXPATHLEN]; int fd; fd=dirfd(dirp); // get dirname in dirpath if (__get_path_by_fd(fd,dirpath,MAXPATHLEN)==-1) return _readdir64(dirp); while((ep=_readdir64(dirp))!=NULL) { // Hope that readdir is not looping char fullpath[MAXPATHLEN]; snprintf(fullpath,MAXPATHLEN,"%s/%s",dirpath,ep->d_name); char abspath[MAXPATHLEN]; myrealpath(fullpath,abspath); if(! __is_event_allowed("open",abspath,stage)) { //__log_event("open",abspath,"DENIED",errno,stage); continue; } else break; } return ep; } // next two functions are almost equal int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result){ char *stage=__get_stage(); char dirpath[MAXPATHLEN]; int fd; fd=dirfd(dirp); // get dirname in dirpath if (__get_path_by_fd(fd,dirpath,MAXPATHLEN)==-1) return _readdir_r(dirp, entry, result); int ret; while((ret=_readdir_r(dirp, entry, result))==0) { if(*result==NULL) { break; // end of directory } char fullpath[MAXPATHLEN]; snprintf(fullpath,MAXPATHLEN,"%s/%s",dirpath,entry->d_name); char abspath[MAXPATHLEN]; myrealpath(fullpath,abspath); if(! __is_event_allowed("open",abspath,stage)) { //__log_event("open",abspath,"DENIED",errno,stage); continue; } else break; } return ret; } int readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result){ char *stage=__get_stage(); char dirpath[MAXPATHLEN]; int fd; fd=dirfd(dirp); // get dirname in dirpath if (__get_path_by_fd(fd,dirpath,MAXPATHLEN)==-1) return _readdir64_r(dirp, entry, result); int ret; while((ret=_readdir64_r(dirp, entry, result))==0) { if(*result==NULL) { break; // end of directory } char fullpath[MAXPATHLEN]; snprintf(fullpath,MAXPATHLEN,"%s/%s",dirpath,entry->d_name); char abspath[MAXPATHLEN]; myrealpath(fullpath,abspath); if(! __is_event_allowed("open",abspath,stage)) { //__log_event("open",abspath,"DENIED",errno,stage); continue; } else break; } return ret; } int __xstat (int vers, const char *name, struct stat *buf) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(name,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno=2; return -1; } if(___xstat==NULL) return -1; return ___xstat(vers,name,buf); } int __xstat64 (int vers, const char *name, struct stat64 *buf) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(name,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno=2; return -1; } if(___xstat64==NULL) return -1; return ___xstat64(vers,name,buf); } int __lxstat (int vers, const char *name, struct stat *buf) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(name,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno = 2; return -1; } if(___lxstat==NULL) return -1; return ___lxstat(vers,name,buf); } int __lxstat64 (int vers, const char *name, struct stat64 *buf) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(name,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno = 2; return -1; } if(___lxstat64==NULL) return -1; return ___lxstat64(vers,name,buf); } int access(const char *pathname, int mode) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(pathname,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno = 2; return -1; } return _access(pathname,mode); } int euidaccess(const char *pathname, int mode) { char *stage=__get_stage(); char fullpath[MAXPATHLEN]; myrealpath(pathname,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno = 2; return -1; } return _euidaccess(pathname,mode); } int faccessat(int dirfd, const char *pathname, int mode, int flags){ char *stage=__get_stage(); char filepath[MAXPATHLEN]; char fullpath[MAXPATHLEN]; int len=__get_path_by_fd(dirfd,filepath,MAXPATHLEN); if(len==-1) filepath[0]=0; strcat(filepath,"/"); strcat(filepath,pathname); myrealpath(filepath,fullpath); if(! __is_event_allowed("open",fullpath,stage)) { __log_event("open",fullpath,"DENIED",errno,stage); errno = 2; return -1; } return _faccessat(dirfd,pathname,mode,flags); } int setenv(const char *name, const char *value, int overwrite) { //printf (" CHANGING name: %s, value: %s",name,value); if(strcmp(name,"LD_PRELOAD")==0 || strcmp(name,"LOG_SOCKET")==0) return -1; int ret=_setenv(name,value,overwrite); return ret; } //int putenv(char *string){ // fprintf(stderr,"putenv 1 pid=%d cmd=%s",getpid(),string); // fflush(stderr); // // //return _system(command); // return 0; //} int close(int fd) { if(fd!=log_socket) { return _close(fd); } return -1; }