/* * =========================================================================== * trickymod1.c * --------------------------------------------------------------------------- * Description: * Installs itself as a module and detects each new installed module. * Sends a message to a remote syslogd when insmod and/or rmmod is called. * * Should be installed as the last "allowed" module. * --------------------------------------------------------------------------- * Version: 1.0 * Date : 30 June 2003 * Author : Jerome Delamarche (jd@inodes-fr.com/jd@trickytools.com) * =========================================================================== */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jerome Delamarche"); MODULE_DESCRIPTION("Detects new module installation"); MODULE_SUPPORTED_DEVICE(""); #define MODNAME "trickymod1" static int facility = 10; MODULE_PARM(facility,"i"); MODULE_PARM_DESC(facility,"Decimal value for the facility (default: 10=AUTHPRIV)"); static int severity = 1; MODULE_PARM(severity,"i"); MODULE_PARM_DESC(severity,"Decimal value for the severity (default: 1=ALERT)"); static char *remote = "127.0.0.1"; MODULE_PARM(remote,"s"); MODULE_PARM_DESC(remote,"IP address of the remote syslogd (default: 127.0.0.1)"); static int verbose = 0; MODULE_PARM(verbose,"i"); MODULE_PARM_DESC(verbose,"Set to 1 to enable debug logs (default: 0)"); /* * Stuff to send message to the remote syslog daemon: */ static int (*gf_socketcall)(int, unsigned long *); static int errno; static unsigned long g_remoteAddr; static struct new_utsname g_utsname; extern void *sys_call_table[]; /* * Simulate data from user space: */ static inline _syscall1(int, brk, void *, end_data_segment); static void *simulate_user_space(char *args, int size) { unsigned long mmm = current->mm->brk; int ret = brk((void *)mmm + 256); if (ret < 0) { printk(KERN_ERR "[%s]: Could not allocate user space\n",MODNAME,ret); return NULL; } copy_to_user((void *)(mmm + 2),args,size); return (void *)(mmm + 2); } /* * Create a UDP socket and connect it to the remote syslogd */ static int my_socket_create() { unsigned long args [3]; void *ptr; int sock; int ret; struct sockaddr_in address; int addrlen = sizeof(address); args[0] = AF_INET; args[1] = SOCK_DGRAM; args[2] = 0; /* 'args' must be in the user space ! */ if ((ptr = simulate_user_space((void *)args,sizeof(unsigned long) * 3)) == NULL) return 0; if (verbose) printk(KERN_DEBUG "[%s]: init_module after simulate_user_space\n",MODNAME); ret = sock = (*gf_socketcall)(SYS_SOCKET,ptr); if (ret < 0) { printk(KERN_ERR "[%s]: Could not create the client socket [%d] !\n",MODNAME,ret); return ret; } if (verbose) printk(KERN_DEBUG "[%s]: init_module after SYS_SOCKET\n",MODNAME); memset(&address,0,sizeof(address)); address.sin_family = AF_INET; address.sin_addr.s_addr = g_remoteAddr; address.sin_port = htons(514); args[0] = sock; args[1] = (unsigned long)simulate_user_space((void *)&address,sizeof(address)); args[2] = addrlen; /* 'args' must be in the user space ! */ if ((ptr = simulate_user_space((void *)args,sizeof(unsigned long) * 3)) == NULL) return 0; if (ret = (*gf_socketcall)(SYS_CONNECT,ptr)) { printk(KERN_ERR "[%s]: Could not connect to the client [%d] !\n",MODNAME,ret); return -1; } return sock; } /* * Get the current time in a SYSLOG protocol format: */ /* since year 2003 has passed, we can make the following asssumption: * curtime > STARTTIME (number of elapsed seconds since 1st Jan 2003) */ #define STARTTIME 1041375600 struct my_time { int tm_year; int tm_month; int tm_mday; int tm_hour; int tm_min; int tm_sec; }; static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static struct my_time *get_syslog_time() { static struct my_time curtime; long seconds; int is_leap; long (*gf_gettimeofday)(struct timeval *, struct timezone *); struct timeval tv; void *ptv = simulate_user_space((char *)&tv,sizeof(tv)); gf_gettimeofday = sys_call_table[SYS_gettimeofday]; (*gf_gettimeofday)(ptv,NULL); copy_from_user(&tv,ptv,sizeof(tv)); /* convert into a SYSLOG TIMESTAMP: */ memset(&curtime,0,sizeof(curtime)); curtime.tm_year = 2003; tv.tv_sec -= STARTTIME; /* sanity check: */ if (tv.tv_sec < 0) return &curtime; /* loop on the year: */ for (;;) { if (curtime.tm_year % 4) seconds = 365*24*3600; else seconds = 366*24*3600; if (tv.tv_sec >= seconds) { curtime.tm_year++; tv.tv_sec -= seconds; } else break; } is_leap = !(curtime.tm_year % 4); /* loop on the month: */ for (;;) { int month = curtime.tm_month; if (month == 0 || month == 2 || month == 4 || month == 6 || month == 7 || month == 9 || month == 12) seconds = 31*24*3600; else if (month != 1) seconds = 30*24*3600; else if (is_leap) seconds = 29*24*3600; else seconds = 28*24*3600; if (tv.tv_sec >= seconds) { curtime.tm_month++; tv.tv_sec -= seconds; } else break; } /* loop on the day: */ curtime.tm_mday = 1; while (tv.tv_sec > 24*3600) { curtime.tm_mday++; tv.tv_sec -= 24*3600; } /* time of day: */ curtime.tm_hour = tv.tv_sec / 3600; curtime.tm_min = (tv.tv_sec / 60) % 60; curtime.tm_sec = tv.tv_sec % 60; return &curtime; } /* * Send a message to the remote daemon: */ static void send_syslogd(char *text) { int ret; unsigned long args[4]; char buffer[1024]; size_t len; void *ptr, *ptr_text; struct my_time *curtime = get_syslog_time(); /* RFC 3164 compliant: */ len = sprintf(buffer,"<%d>%s %2d %02d:%02d:%02d %s %s: %s", facility * 8 + severity, months[curtime->tm_month],curtime->tm_mday, curtime->tm_hour,curtime->tm_min,curtime->tm_sec, g_utsname.nodename,MODNAME,text); ptr_text = simulate_user_space(buffer,len+1); if (!ptr_text) return; args[0] = my_socket_create(); args[1] = (unsigned long)ptr_text; args[2] = len; args[3] = 0; ptr = simulate_user_space((void *)args,sizeof(unsigned long) * 4); if (!ptr) return; if ((ret = (*gf_socketcall)(SYS_SEND,ptr)) < 0) { printk(KERN_ERR "[%s]: Could not send a message to server [%d] !\n",MODNAME,ret); } /* free the socket: */ int (*gf_close)(int fd) = sys_call_table[SYS_close]; (*gf_close)(args[0]); } /* * Everything to intercept sys_create_module call: */ int (*old_create_mod)(char *name, unsigned long size); int my_create_module(char *name, unsigned long size) { char *mod_name = (char *)kmalloc(256,GFP_KERNEL); char buffer[256]; copy_from_user(mod_name,name,255); /* local message: */ printk(KERN_ALERT "[%s]: Module '%s' inserted !\n",MODNAME,mod_name); /* remote message: */ buffer[230] = '\0'; sprintf(buffer,"Module '%s' inserted",mod_name); send_syslogd(buffer); kfree(mod_name); return (*old_create_mod)(name,size); } /* * Init made here: */ int init_module(void) { int ret; unsigned long args[3]; void *ptr; if (verbose) printk(KERN_INFO "[%s]: init_module\n",MODNAME); old_create_mod = sys_call_table[SYS_create_module]; sys_call_table[SYS_create_module] = my_create_module; gf_socketcall = sys_call_table[SYS_socketcall]; /* Test the remote host address consistency: */ if ((g_remoteAddr = in_aton(remote)) == INADDR_ANY) { printk(KERN_ERR "[%s]: address IP of remote host is incorrect: %s !\n",MODNAME,remote); return -1; } /* Get the local hostname: * (useful to trace the source node when log message are relayed) */ { void *ptr = simulate_user_space((char *)&g_utsname,sizeof(g_utsname)); long (*gf_uname)(struct new_utsname *); gf_uname = sys_call_table[SYS_uname]; (*gf_uname)(ptr); copy_from_user(&g_utsname,ptr,sizeof(g_utsname)); if (verbose) printk(KERN_INFO "[%s]: hostname=%s\n",MODNAME,g_utsname.nodename); } return 0; } /* * Nice cleanup: */ void cleanup_module(void) { /* At this point, ressources have been deallocated, we cannot send * any more message to the syslogd. * send_syslogd("is unactive"); */ if (verbose) printk(KERN_DEBUG "[%s]: cleanup_module\n",MODNAME); sys_call_table[SYS_create_module] = old_create_mod; } /* EOF */