/* * Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro * * 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. * * $Id: installwatch.c,v 0.6.3.2 2001/12/22 23:01:20 izto Exp $ * * april-15-2001 - Modifications by Felipe Eduardo Sanchez Diaz Duran * * Added backup() and make_path() functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "localdecls.h" /* #define DEBUG 1 */ #define LOGLEVEL (LOG_USER | LOG_INFO | LOG_PID) #define BUFSIZE 1024 #define error(X) (X < 0 ? strerror(errno) : "success") int __installwatch_refcount = 0; int __installwatch_timecount = 0; #define REFCOUNT __installwatch_refcount++ #define TIMECOUNT __installwatch_timecount++ static time_t (*true_time) (time_t *); static int(*true_chmod)(const char *, mode_t); static int(*true_chown)(const char *, uid_t, gid_t); static int(*true_chroot)(const char *); static int(*true_creat)(const char *, mode_t); static int(*true_fchmod)(int, mode_t); static int(*true_fchown)(int, uid_t, gid_t); static FILE *(*true_fopen)(const char *,const char*); static int(*true_ftruncate)(int, TRUNCATE_T); static int(*true_lchown)(const char *, uid_t, gid_t); static int(*true_link)(const char *, const char *); static int(*true_mkdir)(const char *, mode_t); static int(*true_open)(const char *, int, ...); static int(*true_rename)(const char *, const char *); static int(*true_rmdir)(const char *); static int(*true_symlink)(const char *, const char *); static int(*true_truncate)(const char *, TRUNCATE_T); static int(*true_unlink)(const char *); #if(GLIBC_MINOR >= 1) static int(*true_creat64)(const char *, __mode_t); static FILE *(*true_fopen64)(const char *,const char *); static int(*true_ftruncate64)(int, __off64_t); static int(*true_open64)(const char *, int, ...); static int(*true_truncate64)(const char *, __off64_t); #endif static void log(const char *format, ...) #ifdef __GNUC__ /* Tell gcc that this function behaves like printf() * for parameters 1 and 2 */ __attribute__((format(printf, 1, 2))) #endif /* defined __GNUC__ */ ; void _init(void) { void *libc_handle; #ifdef BROKEN_RTLD_NEXT // printf ("RTLD_LAZY"); libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); #else // printf ("RTLD_NEXT"); libc_handle = RTLD_NEXT; #endif true_time = dlsym(libc_handle, "time"); true_chmod = dlsym(libc_handle, "chmod"); true_chown = dlsym(libc_handle, "chown"); true_chroot = dlsym(libc_handle, "chroot"); true_creat = dlsym(libc_handle, "creat"); true_fchmod = dlsym(libc_handle, "fchmod"); true_fchown = dlsym(libc_handle, "fchown"); true_fopen = dlsym(libc_handle, "fopen"); true_ftruncate = dlsym(libc_handle, "ftruncate"); true_lchown = dlsym(libc_handle, "lchown"); true_link = dlsym(libc_handle, "link"); true_mkdir = dlsym(libc_handle, "mkdir"); true_open = dlsym(libc_handle, "open"); true_rename = dlsym(libc_handle, "rename"); true_rmdir = dlsym(libc_handle, "rmdir"); true_symlink = dlsym(libc_handle, "symlink"); true_truncate = dlsym(libc_handle, "truncate"); true_unlink = dlsym(libc_handle, "unlink"); #if(GLIBC_MINOR >= 1) true_creat64 = dlsym(libc_handle, "creat64"); true_fopen64 = dlsym(libc_handle, "fopen64"); true_ftruncate64 = dlsym(libc_handle, "ftruncate64"); true_open64 = dlsym(libc_handle, "open64"); true_truncate64 = dlsym(libc_handle, "truncate64"); #endif } static void log(const char *format, ...) { char buffer[BUFSIZE], *logname; va_list ap; int count, logfd; va_start(ap, format); count = vsnprintf(buffer, BUFSIZE, format, ap); va_end(ap); if(count == -1) { /* The buffer was not big enough */ strcpy(&(buffer[BUFSIZE - 5]), "...\n"); count = BUFSIZE - 1; } if((logname = getenv("INSTALLWATCHFILE"))) { logfd = true_open(logname, O_WRONLY | O_CREAT | O_APPEND, 0666); if(logfd >= 0) { if(write(logfd, buffer, count) != count) syslog(LOGLEVEL, "Count not write `%s' in `%s': %s\n", buffer, logname, strerror(errno)); if(close(logfd) < 0) syslog(LOGLEVEL, "Could not close `%s': %s\n", logname, strerror(errno)); } else syslog(LOGLEVEL, "Could not open `%s' to write `%s': %s\n", logname, buffer, strerror(errno)); } else syslog(LOGLEVEL, buffer); } static void canonicalize(const char *path, char *resolved_path) { if(!realpath(path, resolved_path) && (path[0] != '/')) { /* The path could not be canonicalized, append it * to the current working directory if it was not * an absolute path */ getcwd(resolved_path, MAXPATHLEN - 2); strcat(resolved_path, "/"); strncat(resolved_path, path, MAXPATHLEN - 1); } } static void make_path (char *path) { char checkdir[BUFSIZ]; struct stat inode; int i = 0; #if DEBUG printf ("===== make_path: %s\n", path); #endif while ( path[i] != '\0' ) { checkdir[i] = path[i]; if (checkdir[i] == '/') { /* Each time a '/' is found, check if the */ checkdir[i+1] = '\0'; /* path exists. If it doesn't, we create it. */ if (stat (checkdir, &inode) < 0) true_mkdir (checkdir, S_IRWXU); } i++; } } static void backup(char *path) { char buffer[BUFSIZ]; char checkdir[BUFSIZ]; char backup_path[BUFSIZ]; int orig, dest, placeholder, bytes, i, blen; struct stat inode, newinode, backup_inode; #if DEBUG printf ("========= backup () =========\npath: %s\n", path); #endif /* If INSTALLWATCH_BACKUP_PATH is not defined then we won't do the backup */ if (!getenv("INSTALLWATCH_BACKUP_PATH")) { #ifdef DEBUG printf ("Backup not enabled, path: %s\n", path); #endif return; } /* Check if this is inside /dev */ if (strstr (path, "/dev") == path) { #if DEBUG printf ("%s is inside /dev. Ignoring.\n", path); #endif return; } /* Now check for /tmp */ if (strstr (path, "/tmp") == path) { #if DEBUG printf ("%s is inside /tmp. Ignoring.\n", path); #endif return; } /* Finally, the backup path itself */ if (strstr (path, getenv("INSTALLWATCH_BACKUP_PATH")) == path) { #if DEBUG printf ("%s is inside the backup path. Ignoring.\n", path); #endif return; } /* Does it exist already? */ #if DEBUG printf ("Existe %s?\n", path); #endif if (stat(path, &inode) < 0) { /* It doesn't exist, we'll tag it so we won't back it up */ /* if we run into it later */ strcpy (backup_path, getenv ("INSTALLWATCH_BACKUP_PATH")); strncat (backup_path, "/no-backup/", 11); strcat (backup_path, path); make_path(backup_path); /* This one's just a placeholder */ placeholder = true_creat(backup_path, S_IREAD); if (!(placeholder < 0)) close (placeholder); #if DEBUG printf ("NO EXISTE\n"); #endif return; } /* Is this one tagged for no backup (i.e. it didn't previously exist)? */ strcpy (backup_path, getenv ("INSTALLWATCH_BACKUP_PATH")); strncat (backup_path, "/no-backup/", 11); strcat (backup_path, path); if (stat (backup_path, &backup_inode) >= 0) { #if DEBUG printf ("%s no debe ser respaldado", backup_path); #endif return; } #if DEBUG printf ("Si existe, veamos de que tipo es.\n"); #endif /* Append the path to the backup_path */ strcpy (backup_path, getenv ("INSTALLWATCH_BACKUP_PATH")); strcat (backup_path, path); /* Create the directory tree for this file in the backup dir */ make_path (backup_path); /* What kind of file is this? */ /* Regular file */ if (S_ISREG(inode.st_mode)) { #if DEBUG printf ("%s es un archivo regular\n", path); #endif if ( (orig = true_open (path, O_RDONLY)) < 0) return; /* Write the backup */ if ( (dest = true_open (backup_path, O_WRONLY|O_CREAT|O_TRUNC, inode.st_mode)) < 0) { close (orig); return ; } while ( (bytes = read (orig, buffer, BUFSIZ)) > 0) write (dest, buffer, bytes); close( dest ); close( orig ); } /* Directory */ if (S_ISDIR(inode.st_mode)) { #if DEBUG printf ("%s es un directorio\n", path); #endif true_mkdir (backup_path, inode.st_mode); } /* Block special */ /* Since we're ignoring everything under /dev, * this one will be hard to find. */ if (S_ISBLK(inode.st_mode)) { #if DEBUG printf ("%s es un blkspecial\n", path); #endif mknod (backup_path, inode.st_mode | S_IFBLK, inode.st_rdev); } /* Character special */ if (S_ISCHR(inode.st_mode)) { #if DEBUG printf ("%s es un charspecial\n", path); #endif mknod (backup_path, inode.st_mode | S_IFCHR, inode.st_rdev); } /* FIFO */ if (S_ISFIFO(inode.st_mode)) { #if DEBUG printf ("%s es un fifo\n", path); #endif mknod (backup_path, inode.st_mode | S_IFIFO, 0); } /* If we have a new file, set it to the right owner and group */ if (!stat (path, &newinode)) true_chown (backup_path, inode.st_uid, inode.st_gid); /* Check the owner and permission of the created directories */ i=0; blen = strlen (getenv ("INSTALLWATCH_BACKUP_PATH")); while ( path[i] != '\0' ) { checkdir[i] = backup_path[blen+i] = path[i]; if (checkdir[i] == '/') { /* Each time a '/' is found, check if the */ checkdir[i+1] = '\0'; /* path exists. If it does, set it's perms. */ if (!stat (checkdir, &inode)) { backup_path[blen+i+1]='\0'; true_chmod (backup_path, inode.st_mode); true_chown (backup_path, inode.st_uid, inode.st_gid); } } i++; } } time_t time (time_t *timer) { TIMECOUNT; #if DEBUG puts("time\n"); #endif return true_time(timer); } int chmod(const char *path, mode_t mode) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(path, canonic); #if DEBUG puts ("in installwatch chmod\n"); #endif backup (canonic); result = true_chmod(path, mode); log("%d\tchmod\t%s\t0%04o\t#%s\n", result, canonic, mode, error(result)); return result; } int chown(const char *path, uid_t owner, gid_t group) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(path, canonic); #if DEBUG puts("chown\n"); #endif backup (canonic); result = true_chown(path, owner, group); log("%d\tchown\t%s\t%d\t%d\t#%s\n", result, canonic, owner, group, error(result)); return result; } int chroot(const char *path) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(path, canonic); result = true_chroot(path); /* From now on, another log file will be written if * * INSTALLWATCHFILE is set */ log("%d\tchroot\t%s\t#%s\n", result, canonic, error(result)); return result; } int creat(const char *pathname, mode_t mode) { /* Is it a system call? */ int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG printf("creat\n"); #endif backup(canonic); result = true_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); log("%d\tcreat\t%s\t#%s\n", result, canonic, error(result)); return result; } int fchmod(int filedes, mode_t mode) { int result; REFCOUNT; #if DEBUG puts("fchmod\n"); #endif result = true_fchmod(filedes, mode); log("%d\tfchmod\t%d\t0%04o\t#%s\n", result, filedes, mode, error(result)); return result; } int fchown(int fd, uid_t owner, gid_t group) { int result; REFCOUNT; #if DEBUG puts("fchown\n"); #endif result = true_fchown(fd, owner, group); log("%d\tfchown\t%d\t%d\t%d\t#%s\n", result, fd, owner, group, error(result)); return result; } FILE *fopen(const char *pathname, const char *mode) { FILE *result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG puts("fopen\n"); #endif if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') backup (canonic); result = true_fopen(pathname,mode); if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') log("%d\tfopen\t%s\t#%s\n", (int)result, canonic, error(result)); return result; } int ftruncate(int fd, TRUNCATE_T length) { int result; REFCOUNT; #if DEBUG puts ("ftruncate\n"); #endif result = true_ftruncate(fd, length); log("%d\tftruncate\t%d\t%d\t#%s\n", result, fd, (int)length, error(result)); return result; } int lchown(const char *path, uid_t owner, gid_t group) { /* Linux specific? */ int result; char canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("lchown\n"); #endif canonicalize(path, canonic); backup (canonic); result = true_chown(path, owner, group); log("%d\tlchown\t%s\t%d\t%d\t#%s\n", result, canonic, owner, group, error(result)); return result; } int link(const char *oldpath, const char *newpath) { int result; char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("link\n"); #endif canonicalize(oldpath, old_canonic); canonicalize(newpath, new_canonic); result = true_link(oldpath, newpath); log("%d\tlink\t%s\t%s\t#%s\n", result, old_canonic, new_canonic, error(result)); return result; } int mkdir(const char *pathname, mode_t mode) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); result = true_mkdir(pathname, mode); log("%d\tmkdir\t%s\t#%s\n", result, canonic, error(result)); return result; } int open(const char *pathname, int flags, ...) { /* Eventually, there is a third parameter: it's mode_t mode */ va_list ap; mode_t mode; int result; char canonic[MAXPATHLEN]; REFCOUNT; va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); canonicalize(pathname, canonic); #if DEBUG printf ("open\n"); #endif if(flags & (O_WRONLY | O_RDWR)) backup (canonic); result = true_open(pathname, flags, mode); if(flags & (O_WRONLY | O_RDWR)) log("%d\topen\t%s\t#%s\n", result, canonic, error(result)); return result; } int rename(const char *oldpath, const char *newpath) { int result; char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; REFCOUNT; canonicalize(oldpath, old_canonic); #if DEBUG puts ("rename\n"); #endif backup (old_canonic); canonicalize(newpath, new_canonic); result = true_rename(oldpath, newpath); log("%d\trename\t%s\t%s\t#%s\n", result, old_canonic, new_canonic, error(result)); return result; } int rmdir(const char *pathname) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG printf ("rmdir\n"); #endif backup(canonic); result = true_rmdir(pathname); log("%d\trmdir\t%s\t#%s\n", result, canonic, error(result)); return result; } int symlink(const char *oldpath, const char *newpath) { int result; char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("symlink\n"); #endif canonicalize(oldpath, old_canonic); canonicalize(newpath, new_canonic); result = true_symlink(oldpath, newpath); log("%d\tsymlink\t%s\t%s\t#%s\n", result, old_canonic, new_canonic, error(result)); return result; } int truncate(const char *path, TRUNCATE_T length) { int result; char canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("truncate\n"); #endif canonicalize(path, canonic); backup (canonic); result = true_truncate(path, length); log("%d\ttruncate\t%s\t%d\t#%s\n", result, path, (int)length, error(result)); return result; } int unlink(const char *pathname) { int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG printf ("unlink\n"); #endif backup(canonic); result = true_unlink(pathname); log("%d\tunlink\t%s\t#%s\n", result, canonic, error(result)); return result; } #if(GLIBC_MINOR >= 1) int creat64(const char *pathname, __mode_t mode) { /* Is it a system call? */ int result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG puts ("creat64\n"); #endif backup (canonic); result = true_open64(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); log("%d\tcreat\t%s\t#%s\n", result, canonic, error(result)); return result; } int ftruncate64(int fd, __off64_t length) { int result; REFCOUNT; #if DEBUG puts ("ftruncate64\n"); #endif result = true_ftruncate64(fd, length); log("%d\tftruncate\t%d\t%d\t#%s\n", result, fd, (int)length, error(result)); return result; } FILE *fopen64(const char *pathname, const char *mode) { FILE *result; char canonic[MAXPATHLEN]; REFCOUNT; canonicalize(pathname, canonic); #if DEBUG puts("fopen64\n"); #endif if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') backup (canonic); result = true_fopen(pathname,mode); if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') log("%d\tfopen64\t%s\t#%s\n", (int)result, canonic, error(result)); return result; } int open64(const char *pathname, int flags, ...) { /* Eventually, there is a third parameter: it's mode_t mode */ va_list ap; mode_t mode; int result; char canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("open64\n"); #endif va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); canonicalize(pathname, canonic); if(flags & (O_WRONLY | O_RDWR)) backup(canonic); result = true_open64(pathname, flags, mode); if(flags & (O_WRONLY | O_RDWR)) log("%d\topen\t%s\t#%s\n", result, canonic, error(result)); return result; } int truncate64(const char *path, __off64_t length) { int result; char canonic[MAXPATHLEN]; REFCOUNT; #if DEBUG puts ("truncate64\n"); #endif canonicalize(path, canonic); backup(canonic); result = true_truncate64(path, length); log("%d\ttruncate\t%s\t%d\t#%s\n", result, path, (int)length, error(result)); return result; } #endif /* GLIBC_MINOR >= 1 */