diff -Nurp vixie-cron/Makefile vixie-cron-mls/Makefile --- vixie-cron/Makefile 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/Makefile 2005-06-30 13:23:43.000000000 -0500 @@ -21,7 +21,7 @@ # Makefile for ISC cron # -# $Id: Makefile,v 1.9 2004/01/23 18:56:42 vixie Exp $ +# $Id: Makefile,v 1.1.1.1 2005/03/15 18:40:50 colmo Exp $ # # vix 03mar88 [moved to RCS, rest of log is in there] # vix 30mar87 [goodbye, time.c; hello, getopt] @@ -70,7 +70,9 @@ LINTFLAGS = -hbxa $(INCLUDE) $(DEBUGGING #<> CC = gcc -Wall -Wno-unused -Wno-comment #<> -DEFS = -DWITH_SELINUX -DWITH_PAM +#DEFS = -DWITH_SELINUX -DWITH_PAM +# TCS mls range support +DEFS = -DWITH_SELINUX -DWITH_PAM -DWITH_SELINUX_MLS #(SGI IRIX systems need this) #DEFS = -D_BSD_SIGNALS -Dconst= #<> diff -Nurp vixie-cron/cron.c vixie-cron-mls/cron.c --- vixie-cron/cron.c 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/cron.c 2005-06-30 13:23:43.000000000 -0500 @@ -19,14 +19,20 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + #if !defined(lint) && !defined(LINT) -static char rcsid[] = "$Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp $"; +static char rcsid[] = "$Id: cron.c,v 1.1.1.2 2005/04/22 18:03:16 dgoeddel Exp $"; #endif #define MAIN_PROGRAM #include "cron.h" +#ifdef WITH_SELINUX_MLS +#include +#endif /* WITH_SELINUX_MLS */ + enum timejump { negative, small, medium, large }; static void usage(void), @@ -40,6 +46,11 @@ static void usage(void), quit(int), parse_args(int c, char *v[]); +#ifdef WITH_SELINUX_MLS +static void do_run_reboot_jobs(cron_db *); +static void do_find_jobs(int, cron_db *, int, int); +#endif /* WITH_SELINUX_MLS */ + static volatile sig_atomic_t got_sighup, got_sigchld; static int timeRunning, virtualTime, clockTime; static long GMToff; @@ -133,6 +144,49 @@ main(int argc, char *argv[]) { database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; +#ifdef WITH_SELINUX_MLS + database.prev_db = NULL; + database.next_db = NULL; + database.range = NULL; + /* + * Initialize the first cron_db element to have a range equal to + * the SYSCRONTAB + */ + if (is_selinux_mls_enabled() == 1) { + security_context_t context; + context_t con = NULL; + const char *range = NULL; + int rc = 0; + + /* Determine the security context of the system crontab */ + if ((rc = getfilecon(SYSCRONTAB, &context)) < 0) { + log_it("CRON", getpid(), "UNABLE TO GET FILE CONTEXT", + SYSCRONTAB); + (void) exit(ERROR_EXIT); + } + con = context_new(context); + if (!con) { + log_it("CRON", getpid(), + "UNABLE TO GET CONTEXT STRUCTURE", SYSCRONTAB); + (void) exit(ERROR_EXIT); + } + /* separate out the actual mls range string from the context */ + range = context_range_get(con); + if (!range) { + log_it("CRON", getpid(), "main", "UNABLE TO GET RANGE"); + (void) exit(ERROR_EXIT); + } + database.range = strdup(range); + if (!database.range) { + log_it("CRON", getpid(), "main", + "UNABLE TO ALLOCATE MEMORY FOR RANGE"); + (void) exit(ERROR_EXIT); + } + freecon(context); + context_free(con); + + } +#endif /* WITH_SELINUX_MLS */ load_database(&database); set_time(TRUE); run_reboot_jobs(&database); @@ -268,8 +322,105 @@ main(int argc, char *argv[]) { } } +#ifdef WITH_SELINUX_MLS +/* + * TCS: Intermediate routine to step through the multilevel database structure + * and alter the process' security context to match that of the database + * element. Then do_run_reboot_jobs is called to handle the database element + */ +static void +run_reboot_jobs(cron_db *db) +{ + cron_db *current_db; + security_context_t original_con = NULL; + security_context_t new_con = NULL; + context_t con = NULL; + int rc = 0; + char *range_s = NULL; + + if (is_selinux_mls_enabled() < 1) + return do_run_reboot_jobs(db); + + rc = getcon(&original_con); + if (rc < 0) { + log_it("CRON", getpid(), "run_reboot_jobs", + "UNABLE TO GET CURRENT CONTEXT"); + (void) exit(ERROR_EXIT); + } + + for (current_db = db; current_db != NULL; + current_db = current_db->next_db) { + con = context_new(original_con); + if (!con) { + log_it("CRON", getpid(), "run_reboot_jobs", + "CONTEXT_NEW FAILED"); + freecon(original_con); + (void) exit(ERROR_EXIT); + } + range_s = build_new_range(current_db->range, + context_range_get(con)); + if (!range_s) { + log_it("CRON", getpid(), "run_reboot_jobs", + "COULD NOT BUILD NEW RANGE"); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + + if (context_range_set(con, range_s)) { + log_it("CRON", getpid(), "run_reboot_jobs", + "CONTEXT_RANGE_SET FAILED"); + free(range_s); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + new_con = context_str(con); + if (!new_con) { + log_it("CRON", getpid(), "run_reboot_jobs", + "CONTEXT_STR FAILED"); + free(range_s); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + rc = setcon(new_con); + if (rc < 0) { + /* + * problem. If we can't get to the new context + * then we really shouldn't be running jobs that deal + * with that range. Log it and move on + */ + log_it("CRON", getpid(), "run_reboot_jobs", + "UNABLE TO SET CONTEXT FOR JOBS"); + } else { + /* at the proper context time to run the jobs */ + do_run_reboot_jobs(current_db); + } + free(range_s); + context_free(con); + } + + /* before returning reset the context to the original */ + rc = setcon(original_con); + if (rc < 0) /* for now log it but this could lead to issues later */ + log_it("CRON", getpid(), "run_reboot_jobs", + "UNABLE TO RESET CONTEXT TO ORIGINAL"); + freecon(original_con); +} +#endif /* WITH_SELINUX_MLS */ + +#ifdef WITH_SELINUX_MLS +/* + * TCS: renamed routine to facilitate the injection of an intermediate routine + * to handle multilevel functionality + */ +static void +do_run_reboot_jobs(cron_db *db) { +#else static void run_reboot_jobs(cron_db *db) { +#endif /* WITH_SELINUX_MLS */ user *u; entry *e; @@ -282,8 +433,99 @@ run_reboot_jobs(cron_db *db) { (void) job_runqueue(); } +#ifdef WITH_SELINUX_MLS +/* + * TCS: Intermediate routine to step through the multilevel database structure + * and alter the process' security context to match that of the database + * element. Then do_find_jobs is called to handle the database element + */ +static void +find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) +{ + cron_db *current_db; + security_context_t original_con = NULL; + security_context_t new_con = NULL; + context_t con = NULL; + int rc = 0; + char *range_s = NULL; + + if (is_selinux_mls_enabled() < 1) + return do_find_jobs(vtime, db, doWild, doNonWild); + + rc = getcon(&original_con); + if (rc < 0) { + log_it("CRON", getpid(), "find_jobs", + "UNABLE TO GET CURRENT CONTEXT"); + (void) exit(ERROR_EXIT); + } + + for (current_db = db; current_db != NULL; current_db = current_db->next_db) { + con = context_new(original_con); + if (!con) { + log_it("CRON", getpid(), "find_jobs", + "CONTEXT_NEW FAILED"); + freecon(original_con); + (void) exit(ERROR_EXIT); + } + range_s = build_new_range(current_db->range, + context_range_get(con)); + if (!range_s) { + log_it("CRON", getpid(), "find_jobs", + "COULD NOT BUILD NEW RANGE"); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + + if (context_range_set(con, range_s)) { + log_it("CRON", getpid(), "find_jobs", + "CONTEXT_RANGE_SET FAILED"); + free(range_s); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + new_con = context_str(con); + if (!new_con) { + log_it("CRON", getpid(), "find_jobs", + "CONTEXT_STR FAILED"); + free(range_s); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + rc = setcon(new_con); + if (rc < 0) { + log_it("CRON", getpid(), "find_jobs", + "UNABLE TO CHANGE CONTEXT"); + } else { + /* at the proper context time to run the jobs */ + do_find_jobs(vtime, current_db, doWild, doNonWild); + } + free(range_s); + context_free(con); + } + + /* before returning reset the context to the original */ + rc = setcon(original_con); + if (rc < 0) /* for now I'll log it but need a better err handling */ + log_it("CRON", getpid(), "find_jobs", + "UNABLE TO RESET CONTEXT TO ORIGINAL"); + freecon(original_con); +} +#endif /* WITH_SELINUX_MLS */ + +#ifdef WITH_SELINUX_MLS +/* + * TCS: renamed routine to facilitate the injection of an intermediate routine + * to handle multilevel functionality + */ +static void +do_find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) { +#else static void find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) { +#endif /* WITH_SELINUX_MLS */ time_t virtualSecond = vtime * SECONDS_PER_MINUTE; struct tm *tm = gmtime(&virtualSecond); int minute, hour, dom, month, dow; diff -Nurp vixie-cron/database.c vixie-cron-mls/database.c --- vixie-cron/database.c 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/database.c 2005-06-30 13:23:43.000000000 -0500 @@ -19,8 +19,10 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + #if !defined(lint) && !defined(LINT) -static char rcsid[] = "$Id: database.c,v 1.7 2004/01/23 18:56:42 vixie Exp $"; +static char rcsid[] = "$Id: database.c,v 1.6 2005/05/10 22:00:52 dthayer Exp $"; #endif /* vix 26jan87 [RCS has the log] @@ -28,19 +30,272 @@ static char rcsid[] = "$Id: database.c,v #include "cron.h" +#ifdef WITH_SELINUX_MLS +#include +#endif /* WITH_SELINUX_MLS */ + #define TMAX(a,b) ((a)>(b)?(a):(b)) static void process_crontab(const char *, const char *, const char *, struct stat *, cron_db *, cron_db *); +#ifdef WITH_SELINUX_MLS +#define MEMBER_PREFIX ".MEMBER." +void do_load_database(cron_db *); + +/* + * TCS: Intermediate routine that will handle the creation of the multilevel + * database structure. A new database element will be created for each + * member directory of the polyinstantiated cron spool directory. Prior to + * calling the original load_database routine, now renamed to do_load_database + * the range in the current security context will be changed so that the process + * is at the same level as that of the member directory. + */ + +/* + * XXX: Currently this has potential to use more memory than is necessary. + * Member directories can exist in the polyinstantiated directory that contain + * no files. If this looks to be a potential problem then before load_database + * returns it should run through the db list and remove all cron_db elements + * with empty user pointers but then they would be regenerated the next time + * through. The only other solution is to actual check inside of the directory + * before the allocation and skip the directory if there are no files. + */ +void +load_database(cron_db *old_db) +{ + cron_db *new_db = NULL, *current_db = NULL; + cron_db *prev_db = NULL; + struct dirent *dirp; + DIR *dir; + int rc; + int found_range; + char dname[PATH_MAX+1]; + size_t len; + const char *range = NULL; + context_t dcon = NULL; + context_t con = NULL; + security_context_t dir_context = NULL; + security_context_t original_con = NULL; + security_context_t new_con = NULL; + char *range_s = NULL; + char *poly_path = NULL; + + if (is_selinux_mls_enabled() < 1) + return do_load_database(old_db); + + + poly_path = (char *)malloc(strlen(SPOOL_DIR)+strlen(SPOOL_POLY_DIR)+2); + strcpy(poly_path, SPOOL_DIR); + strcat(poly_path, "/"); + strcat(poly_path, SPOOL_POLY_DIR); + + if (!(dir = opendir(poly_path))) { + log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_POLY_DIR); + return do_load_database(old_db); + } + + rc = getcon(&original_con); + if (rc < 0) { + log_it("CRON", getpid(), "load_database", + "UNABLE TO GET CURRENT CONTEXT"); + (void) exit(ERROR_EXIT); + } + + while ((dirp = readdir(dir)) != NULL) { + /* Only concerned with directories that start with .MEMBER. */ + rc = strncmp(dirp->d_name, MEMBER_PREFIX, + strlen(MEMBER_PREFIX)); + if (rc != 0) + continue; + len = strlen(dirp->d_name); + /* path name has to come under path_max and we need to fit + * CRONDIR + / + poly_path + / + directory name + NULL */ + if (len > (PATH_MAX-strlen(CRONDIR)-strlen(poly_path)-3)) + { + log_it("CRON", getpid(), "DIRECTORY NAME TOO BIG", + dirp->d_name); + continue; + } + strcpy(dname, CRONDIR); + strcat(dname, "/"); + strcat(dname, poly_path); + strcat(dname, "/"); + strcat(dname, dirp->d_name); + /* Determine the security context of the directory */ + if ((rc = getfilecon(dname, &dir_context)) < 0) { + log_it("CRON", getpid(), + "UNABLE TO GET CONTEXT OF DIRECTORY", dname); + goto reset_path; + } + dcon = context_new(dir_context); + if (!dcon) { + log_it("CRON", getpid(), + "UNABLE TO GET CONTEXT STRUCTURE", dname); + goto reset_con; + } + /* separate out the actual mls range string from the context */ + range = context_range_get(dcon); + if (!range) { + log_it("CRON", getpid(), "UNABLE TO GET RANGE", dname); + goto reset; + } + + current_db = old_db; + found_range = 0; + while (current_db != NULL) { + /* + * need to locate a previously created cron_db + * for this range or get to the end of the list + */ + if ((strcmp(range, current_db->range) == 0)) { + found_range = 1; + break; + } else { + prev_db = current_db; + current_db = current_db->next_db; + } + } + if (!found_range) { + /* + * need to allocate a cron_db struct for the range and + * link it in to the list + */ + new_db = (cron_db *)malloc(sizeof(cron_db)); + if (new_db == NULL) { + log_it("CRON", getpid(), + "UNABLE TO ALLOCATE MEMORY FOR CRON_DB", + range); + (void) exit(ERROR_EXIT); + } + new_db->range = strdup(range); + new_db->next_db = NULL; + new_db->prev_db = prev_db; + new_db->head = NULL; + new_db->tail = NULL; + new_db->mtime = (time_t) 0; + /* + * there is at least one element in the list so + * prev_db can only be NULL when we are looking + * at the first element in which case we wouldn't + * be in this section of code but just to be safe + */ + if (prev_db == NULL) + *old_db = *new_db; /*XXX:potential mem leak*/ + else + prev_db->next_db = new_db; + current_db = new_db; + } /* end of if !found_range */ + + /* + * At this point current_db either points to a cron_db element + * with a matching range or to a new cron_db element + * The process needs to be set to the same range so that + * when it opens the spool directory during do_load_database + * it is opening the polyinstantiated directory with that + * range + */ + con = context_new(original_con); + if (!con) { + log_it("CRON", getpid(), "load_database", + "CONTEXT_NEW FAILED"); + freecon(original_con); + (void) exit(ERROR_EXIT); + } + range_s = build_new_range(current_db->range, + context_range_get(con)); + if (!range_s) { + log_it("CRON", getpid(), "load_database", + "COULD NOT BUILD NEW RANGE"); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + if (context_range_set(con, range_s)) { + log_it("CRON", getpid(), "context_range_set failed for", + range_s); + free(range_s); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + + free(range_s); + new_con = context_str(con); + if (!new_con) { + log_it("CRON", getpid(), "load_database", + "CONTEXT_STR FAILED"); + freecon(original_con); + context_free(con); + (void) exit(ERROR_EXIT); + } + + rc = setcon(new_con); + if (rc < 0) { + /* + * If context change fails then there is no reason + * to attempt to process the directory + */ + log_it("CRON", getpid(), "UNABLE TO CHANGE CONTEXT", new_con); + context_free(con); + goto reset; + } + + context_free(con); + + /* + * do_load_database can now go about it's normal way + */ + do_load_database(current_db); + + /* Clean up and get ready to process the next + * poly-instantiated directory */ +reset: + context_free(dcon); +reset_con: + freecon(dir_context); +reset_path: + memset(dname, 0, PATH_MAX+1); + rc = setcon(original_con); + if (rc < 0) { + log_it("CRON", getpid(), "load_database", + "UNABLE TO RESET CONTEXT"); + } + } /* end of while polyinstantiated directory entries */ + + closedir(dir); + + /* Reset context to the original just in case */ + rc = setcon(original_con); + if (rc < 0) { + log_it("CRON", getpid(), "load_database", + "UNABLE TO RESET CONTEXT"); + } + freecon(original_con); +} +#endif /* WITH_SELINUX_MLS */ + +#ifdef WITH_SELINUX_MLS +/* + * TCS: renamed routine to facilitate the injection of an intermediate routine + * to handle multilevel functionality + */ +void +do_load_database(cron_db *old_db) +{ +#else void load_database(cron_db *old_db) { +#endif /* WITH_SELINUX_MLS */ struct stat statbuf, syscron_stat, crond_stat; cron_db new_db; DIR_T *dp; DIR *dir; user *u, *nu; +#ifdef WITH_SELINUX_MLS + int skip = 0; +#endif /* WITH_SELINUX_MLS */ Debug(DLOAD, ("[%ld] load_database()\n", (long)getpid())) @@ -55,7 +310,15 @@ load_database(cron_db *old_db) { if (stat(RH_CROND_DIR, &crond_stat) < OK) { log_it("CRON", getpid(), "STAT FAILED", RH_CROND_DIR); +#ifndef WITH_SELINUX_MLS (void) exit(ERROR_EXIT); +#else + /* + * We may be at a level in which we are unable to stat the + * system cron.d directory. There is still more work to be done + */ + skip = 1; +#endif /* WITH_SELINUX_MLS */ } /* track system crontab file @@ -91,10 +354,23 @@ load_database(cron_db *old_db) { process_crontab("root", NULL, SYSCRONTAB, &syscron_stat, &new_db, old_db); +#ifdef WITH_SELINUX_MLS + if (!skip) { + /* + * Only attempt to process the directory if we + * were able to previously stat it + */ + if (!(dir = opendir(RH_CROND_DIR))) { + log_it("CRON", getpid(), "OPENDIR FAILED", + RH_CROND_DIR); + (void) exit(ERROR_EXIT); + } +#else if (!(dir = opendir(RH_CROND_DIR))) { log_it("CRON", getpid(), "OPENDIR FAILED", RH_CROND_DIR); (void) exit(ERROR_EXIT); } +#endif /* WITH_SELINUX_MLS */ while (NULL != (dp = readdir(dir))) { char fname[MAXNAMLEN+1], @@ -138,6 +414,10 @@ load_database(cron_db *old_db) { } closedir(dir); +#ifdef WITH_SELINUX_MLS + } /* close if !skip */ +#endif /* WITH_SELINUX_MLS */ + /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and * we fork a lot more often than the mtime of the dir changes. @@ -187,6 +467,16 @@ load_database(cron_db *old_db) { free_user(u); } +#ifdef WITH_SELINUX_MLS + /* + * before we overwrite the old_db we need to copy + * the additional data fields into the new db element + */ + new_db.next_db = old_db->next_db; + new_db.prev_db = old_db->prev_db; + new_db.range = old_db->range; +#endif /* WITH_SELINUX_MLS */ + /* overwrite the database control block with the new one. */ *old_db = new_db; @@ -235,6 +525,82 @@ process_crontab(const char *uname, const int crontab_fd = OK - 1; user *u; +#ifdef WITH_SELINUX_MLS + /* + * TCS: Limit the processing of crontab files to only those that + * are at the same level as the current process + */ + if (is_selinux_mls_enabled()) { + security_context_t p_context = NULL; + security_context_t f_context = NULL; + context_t p_con; + context_t f_con; + char *p_lowlevel = NULL; + char *f_lowlevel = NULL; + int rc = 0; + + if (getcon(&p_context) < 0) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET CURRENT CONTEXT"); + goto next_crontab; + } + if (getfilecon(tabname, &f_context) < 0) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET CONTEXT OF CRONTAB"); + freecon(p_context); + goto next_crontab; + } + p_con = context_new(p_context); + if (!p_con) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET NEW CONTEXT"); + freecon(p_context); + freecon(f_context); + goto next_crontab; + } + f_con = context_new(f_context); + if (!f_con) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET NEW CONTEXT"); + freecon(p_context); + freecon(f_context); + context_free(p_con); + goto next_crontab; + } + + freecon(p_context); + freecon(f_context); + + if (get_lowlevel(context_range_get(p_con), &p_lowlevel)) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET LOW LEVEL"); + context_free(p_con); + context_free(f_con); + goto next_crontab; + } + if (get_lowlevel(context_range_get(f_con), &f_lowlevel)) { + log_it("CRON", getpid(), "process_crontab", + "UNABLE TO GET LOW LEVEL"); + context_free(p_con); + context_free(f_con); + free(p_lowlevel); + goto next_crontab; + } + + context_free(p_con); + context_free(f_con); + + rc = strcmp(f_lowlevel, p_lowlevel); + + free(p_lowlevel); + free(f_lowlevel); + + /* If the process and file are not at the same level skip */ + if (rc) + goto next_crontab; + } +#endif /* WITH_SELINUX_MLS */ + if (fname == NULL) { /* must be set to something for logging purposes. */ diff -Nurp vixie-cron/funcs.h vixie-cron-mls/funcs.h --- vixie-cron/funcs.h 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/funcs.h 2005-06-30 13:23:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $ + * $Id: funcs.h,v 1.1.1.1 2005/03/15 18:40:50 colmo Exp $ */ /* @@ -77,3 +77,7 @@ struct passwd *pw_dup(const struct passw #ifndef HAVE_TM_GMTOFF long get_gmtoff(time_t *, struct tm *); #endif +#ifdef WITH_SELINUX_MLS +char *build_new_range(char *, const char *); +int get_lowlevel(const char *, char **); +#endif /* WITH_SELINUX_MLS */ diff -Nurp vixie-cron/misc.c vixie-cron-mls/misc.c --- vixie-cron/misc.c 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/misc.c 2005-06-30 13:23:43.000000000 -0500 @@ -19,8 +19,10 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + #if !defined(lint) && !defined(LINT) -static char rcsid[] = "$Id: misc.c,v 1.16 2004/01/23 18:56:43 vixie Exp $"; +static char rcsid[] = "$Id: misc.c,v 1.1.1.1 2005/03/15 18:40:50 colmo Exp $"; #endif /* vix 26jan87 [RCS has the rest of the log] @@ -757,3 +759,81 @@ long get_gmtoff(time_t *clock, struct tm return (offset); } #endif /* HAVE_TM_GMTOFF */ + +#ifdef WITH_SELINUX_MLS +/* + * TCS: TCS created function pulled from SELinux newrole.c + * This function takes a char *newlevel representing the newlevel to set in + * the range (*range), and will return a new malloc'd char string containing + * the new range. + */ +char * +build_new_range(char *newlevel, const char *range) +{ + char *newrangep = NULL, *tmpptr; + size_t len; + + /* a missing or empty string */ + if (!range || !strlen(range) || !newlevel || !strlen(newlevel)) + return NULL; + + /* if the newlevel is actually a range - just use that */ + if (strchr(newlevel, '-')) { + newrangep = strdup(newlevel); + return newrangep; + } + + /* look for MLS range */ + tmpptr = strchr(range, '-'); + + if (tmpptr) { + /* we are inserting into a ranged MLS context */ + len = strlen(newlevel) + 1 + strlen(tmpptr + 1) + 1; + newrangep = (char *)malloc(len); + if (!newrangep) + return NULL; + snprintf(newrangep, len, "%s-%s", newlevel, tmpptr + 1); + } else { + /* we are inserting into a currently non-ranged MLS context */ + if (!strcmp(newlevel, range)) { + newrangep = strdup(range); + } else { + len = strlen(newlevel) + 1 + strlen(range) + 1; + newrangep = (char *)malloc(len); + if (!newrangep) + return NULL; + snprintf(newrangep, len, "%s-%s", newlevel, range); + } + } + + return newrangep; +} + +/* + * This function takes a const char *range representing a MLS range and a + * char **level where the function will store a new malloc'd string containing + * only the lowlevel that was present in the MLS range. + */ +int +get_lowlevel(const char *range, char **level) +{ + char *ptr = NULL; + int len = 0; + + ptr = strchr(range, '-'); + if (!ptr) { + *level = strdup(range); + if (!*level) + return 1; + } else { + len = ptr - range; + *level = malloc(len + 1); + if (!*level) + return 1; + strncpy(*level, range, len); + *(*level + len) = '\0'; + } + return 0; +} + +#endif /* WITH_SELINUX_MLS */ diff -Nurp vixie-cron/pathnames.h vixie-cron-mls/pathnames.h --- vixie-cron/pathnames.h 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/pathnames.h 2005-06-30 13:23:43.000000000 -0500 @@ -19,8 +19,10 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + /* - * $Id: pathnames.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ + * $Id: pathnames.h,v 1.1.1.1 2005/03/15 18:40:50 colmo Exp $ */ #ifndef _PATHNAMES_H_ @@ -48,6 +50,10 @@ */ #define SPOOL_DIR "cron" +#ifdef WITH_SELINUX_MLS +#define SPOOL_POLY_DIR ".POLY." +#endif + /* cron allow/deny file. At least cron.deny must * exist for ordinary users to run crontab. */ diff -Nurp vixie-cron/structs.h vixie-cron-mls/structs.h --- vixie-cron/structs.h 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/structs.h 2005-06-30 13:23:43.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: structs.h,v 1.7 2004/01/23 18:56:43 vixie Exp $ + * $Id: structs.h,v 1.1.1.1 2005/03/15 18:40:50 colmo Exp $ */ /* @@ -19,6 +19,8 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + typedef struct _entry { struct _entry *next; struct passwd *pwd; @@ -56,6 +58,10 @@ typedef struct _user { } user; typedef struct _cron_db { +#ifdef WITH_SELINUX_MLS /* TCS */ + struct _cron_db *next_db, *prev_db; /* links for db list */ + char *range; /* mls range */ +#endif /* TCS */ user *head, *tail; /* links */ time_t mtime; /* last modtime on spooldir */ } cron_db; diff -Nurp vixie-cron/user.c vixie-cron-mls/user.c --- vixie-cron/user.c 2005-06-30 13:23:28.000000000 -0500 +++ vixie-cron-mls/user.c 2005-06-30 13:23:43.000000000 -0500 @@ -19,8 +19,10 @@ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Copyright (C) 2005 Trusted Computer Solutions, Inc. */ + #if !defined(lint) && !defined(LINT) -static char rcsid[] = "$Id: user.c,v 1.5 2004/01/23 18:56:43 vixie Exp $"; +static char rcsid[] = "$Id: user.c,v 1.4 2005/04/22 18:08:31 dgoeddel Exp $"; #endif /* vix 26jan87 [log is in RCS file] @@ -31,11 +33,199 @@ static char rcsid[] = "$Id: user.c,v 1.5 #include #include #include +#include #endif #include "cron.h" #ifdef WITH_SELINUX +#ifdef WITH_SELINUX_MLS + +/* + * TCS: Reworked get_security_context which will examine a list of contexts + * for a given user and determine which context from the list is the proper + * context to execute the the user's crontab jobs at + */ + +static int +get_security_context(const char *name, + int crontab_fd, + security_context_t *rcontext, + const char *tabname) +{ + security_context_t scontext = NULL; + security_context_t tcontext = NULL; + security_context_t file_context = NULL; + security_context_t *context_list = NULL; + security_context_t ptr = NULL; + context_t con = NULL; + context_t f_con = NULL; + struct av_decision avd; + int retval = 0; + int bad_filecon = 0; + int number = 0; + int count = 0; + int found_context = 0; + char buf[20]; + int enforcing = 0; + char *f_lowlevel = NULL; + char *p_lowlevel = NULL; + char *range_s = NULL; + int rc = 0; + + *rcontext = NULL; + + enforcing = security_getenforce(); + + if (fgetfilecon(crontab_fd, &file_context) < OK) { + if (enforcing > 0) { + log_it(name, getpid(), "getfilecon FAILED", tabname); + return -1; + } else { + log_it(name, getpid(), "getfilecon FAILED but SELinux in permissive mode, continuing", tabname); + bad_filecon = 1; + } + } + + number = get_ordered_context_list(name, NULL, &context_list); + + if (number <= 0) { + if (enforcing > 0) { + log_it(name, getpid(), + "No SELinux security context",tabname); + return -1; + } else { + /* XXX: I think this could lead to a segfault -colmo */ + log_it(name, getpid(), + "No security context but SELinux in permissive mode, continuing", tabname); + } + } + + if (bad_filecon && (number > 0)) { + /* If getfilecon failed then return the default context */ + scontext = strdup(context_list[0]); + freeconary(context_list); + *rcontext = scontext; + return 0; + } else if (bad_filecon) { + return -1; + } + + for (count = 0; count < number; count++) { + ptr = context_list[count]; + retval = security_compute_av(ptr, + file_context, + SECCLASS_FILE, + FILE__ENTRYPOINT, + &avd); + if (retval || + ((FILE__ENTRYPOINT & avd.allowed) != FILE__ENTRYPOINT)) { + /* Not the proper context. Hope there are more */ + continue; + } else { + found_context = 1; + break; + } + } + + /* XXX: There is no mls check performed for entrypoint and since the + * context we currently have for the user may not be at the same level + * as that of the crontab file we need to process */ + + if (found_context == 0) { + if (enforcing > 0) { + log_it(name, getpid(), + "ENTRYPOINT FAILED", tabname); + freeconary(context_list); + return -1; + } else { + log_it(name, getpid(), "ENTRYPOINT FAILED but SELinux in permissive mode, continuing", tabname); + } + /* Send back the default context */ + if (number >= 1) + tcontext = strdup(context_list[0]); + else { + scontext = NULL; + goto out; + } + } else { + tcontext = strdup(context_list[count]); + } + + /* XXX: Set the level in the context to that of the file */ + f_con = context_new(file_context); + if (!f_con) { + freecon(tcontext); + goto error_out; + } + con = context_new(tcontext); + if (!con) { + freecon(tcontext); + context_free(f_con); + goto error_out; + } + freecon(tcontext); + rc = get_lowlevel(context_range_get(f_con), &f_lowlevel); + if (!f_lowlevel || rc) { + context_free(f_con); + context_free(con); + goto error_out; + } + rc = get_lowlevel(context_range_get(con), &p_lowlevel); + if (!p_lowlevel || rc) { + free(f_lowlevel); + context_free(f_con); + context_free(con); + goto error_out; + } + if (strcmp(f_lowlevel, p_lowlevel)) { + range_s = build_new_range(f_lowlevel, context_range_get(con)); + if (!range_s) { + free(f_lowlevel); + free(p_lowlevel); + context_free(f_con); + context_free(con); + goto error_out; + } + if (context_range_set(con, range_s)) { + free(f_lowlevel); + free(p_lowlevel); + free(range_s); + context_free(f_con); + context_free(con); + goto error_out; + } + } + tcontext = context_str(con); + scontext = strdup(tcontext); + free(range_s); + context_free(f_con); + context_free(con); + free(f_lowlevel); + free(p_lowlevel); + + + *rcontext = scontext; + +out: + freecon(file_context); + + freeconary(context_list); + return 0; + +error_out: + freecon(file_context); + freeconary(context_list); + if (enforcing > 0) { + log_it(name, getpid(), "UNABLE TO SET USER CONTEXT TO SAME LEVEL AS CRONTAB FILE", tabname); + return -1; + } else { + log_it(name, getpid(), "UNABLE TO SET USER CONTEXT TO SAME LEVEL AS CRONTAB FILE but SELinux in permissive mode, continuing", tabname); + return 0; + } +} + +#else /* !WITH_SELINUX_MLS */ static int get_security_context(const char *name, int crontab_fd, security_context_t *rcontext, @@ -92,7 +282,8 @@ static int get_security_context(const ch *rcontext=scontext; return 0; } -#endif +#endif /* WITH_SELINUX_MLS */ +#endif /* WITH_SELINUX */ void free_user(user *u) {