/*
* Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro <p@demauro.net>
*
* 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
* <izto@asic-linux.com.mx>
* Added backup() and make_path() functions.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <syslog.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <dlfcn.h>
#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 */
syntax highlighted by Code2HTML, v. 0.9.1