/* * CHDIR HOOK * Hook, filter, and monitor the chdir syscall. * By Michael Rywalt - 2569 * Operating System Concepts */ #include #include #include #include #include #include /* * If you add directories to the list, * you MUST update this count to the correct * total! */ #define DIR_ENTRY_COUNT 8 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Rywalt"); /********************** * Parameter Defaults * ********************** * * These are default values for parameters if the module is loaded * without supplying parameters. */ static long userID = 500; static int mode = 2; static char *username = "mrywalt"; /********************* * Module Parameters * *********************/ module_param(userID, long, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); /* User ID Parameter. */ module_param(mode, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); /* 0 - Restrict, 1 - Limit, 2 - Monitor. */ module_param(username, charp, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); /* Username (corresponding to userID param.) */ /****************** * sys_call_table * ****************** * * Linux 2.6 does not export this, as it could be a security hole. * I want it back from the sysadmin's standpoint. I would like to * be able to hook the chdir syscall so I can control who goes * where on the system. */ void **sys_call_table; /********************* * acReplacementPath * ********************* * * Array of directories to ban or allow. This is a simplistic implementation * that would not serve well in practical applications. This is only * done as part of a demonstration for a simple kernel module. * * This array is filled in the initialization function. */ char *g_acDirectories[DIR_ENTRY_COUNT]; /************* * old_chdir * ************* * * Function Pointer. * * Function pointer to our original chdir syscall. We need to back * this up so we can send authorized requests back to the kernel. * We will be hooking this with our replacement function. */ asmlinkage long (*old_chdir) (const char *szName); /************************* * FillDirectoryEntries * ************************* * * Function: FillDirectoryEntries * * Purpose: Provides a list of directories which the user may either * change directories to, or not change directories to, * depending on the mode option that was passed into the * module parameter list upon loading. * * Note: These entries can be configured to taste. If you * want to add more entries, be sure to increase the * #define DIR_ENTRY_COUNT at the top of this file * to reflect the number of entries in your table. * * Parameters: None. * * Returns: Nothing. */ void FillDirectoryEntries() { int i; for(i = 0; i < DIR_ENTRY_COUNT; ++i) { g_acDirectories[i] = " "; } g_acDirectories[0] = "/home/"; g_acDirectories[1] = "/home"; g_acDirectories[2] = "home/"; g_acDirectories[3] = "home"; sprintf(g_acDirectories[4], "%s/", username); strcpy(g_acDirectories[5], username); sprintf(g_acDirectories[6], "/home/%s/", username); sprintf(g_acDirectories[7], "/home/%s", username); } /********************* * ScanForDireEntries * ********************* * * Function: ScanForDirEntries * * Purpose: Scans the array of directory names for matches. * * Parameters: Pointer to string we're checking. * * Returns: 1 means match found. * 0 means no match found. */ int ScanForDirEntries(const char *pcDir) { int i; /* Scan all entries for matches. */ for(i = 0; i < DIR_ENTRY_COUNT; ++i) { if(strcmp(pcDir, g_acDirectories[i]) == 0) { return 0; } } return 1; } /********************** * LocateSysCallTable * ********************** * * Function: LocateSysCallTable * * Purpose: Attempts to find the sys_call_table, which is not * exported in Linux 2.6.x. * * Parameters: None yet. * * Returns: Address of sys_call_table, if found. * If not found, returns NULL. */ unsigned long **LocateSysCallTable(void) { extern int loops_per_jiffy; unsigned long **pSysCallTable = NULL; unsigned long ulSearchIndex; unsigned long ulStartEntry = (unsigned long)&loops_per_jiffy; unsigned long ulEndEntry = (unsigned long)&boot_cpu_data; unsigned long ulStepSize = sizeof(void *); /* Search for sys_call_table in memory. */ for (ulSearchIndex = ulStartEntry; ulSearchIndex < ulEndEntry; ulSearchIndex += ulStepSize) { unsigned long *pTest; pTest = (unsigned long *)ulSearchIndex; /* If an offset into our current pointer equals a known system call, * we have found the sys_call_table! */ if (pTest[__NR_open] == (unsigned long) sys_open) { /* Return the address of the sys_call_table. */ pSysCallTable = (unsigned long **)pTest; return &pSysCallTable[0]; } } return NULL; } /************* * new_chdir * ************* * * Function: new_chdir * * Purpose: This is the replacement (hook) chdir syscall entry. * * Parameters: Same params as the old syscall. * * Returns: Same return as old syscall. */ asmlinkage long new_chdir(const char *cPath) { if(current->uid == userID) { if(ScanForDirEntries(cPath) == mode) { /* If mode is 2 (monitor only mode), we will never touch * this piece of code, which is exactly what we intend. */ printk("Project5: Denying user %d's changedir request to %s\n", (int)current->uid, cPath); return -EACCES; /* Permission Denied. */ } } printk("Project5: Allowing user %d to chdir to %s\n", (int)current->uid, cPath); return old_chdir(cPath); } /*************** * init_module * *************** * * Function: init_module * * Purpose: The initialization function for the event driven code in this module. * * Parameters: None. * * Returns: Error level. */ int init_module() { /* Load the syscall table. */ sys_call_table = (void**)LocateSysCallTable(); if (sys_call_table == NULL) { printk(KERN_ERR "Project5: Unable to locate sys_call_table address.\nExiting...\n"); return -1; } /* Save the old chdir, and point the table entry to our new chdir. * Our new version will 'filter' the requests and call the old chdir * only if an authorized request is issued. */ old_chdir = (long(*) (const char *cPath) ) (sys_call_table[__NR_chdir]); sys_call_table[__NR_chdir] = (unsigned long*) new_chdir; /* * Fill the array of directory names to ban or allow. */ FillDirectoryEntries(); printk("Project5: Module starting...\n"); return 0; } /****************** * cleanup_module * ****************** * * Function: cleanup_module * * Purpose: Called when the module is unloaded. * * Parameters: None. * * Returns: Nothing. */ void cleanup_module() { /* Set the syscal chdir entry back to its original link. */ sys_call_table[__NR_chdir] = (unsigned long*) old_chdir; printk("Project5: Exiting module...\n"); }