Only in vixie-cron-4.1: crond.pam.pamd_crond diff -rup vixie-cron-4.1-orig/crontab.c vixie-cron-4.1/crontab.c --- vixie-cron-4.1-orig/crontab.c 2006-11-02 22:28:04.000000000 -0500 +++ vixie-cron-4.1/crontab.c 2006-12-08 11:01:10.000000000 -0500 @@ -33,6 +33,7 @@ static char rcsid[] = "$Id: crontab.c,v #include "cron.h" #ifdef WITH_SELINUX #include +#include #include #endif @@ -415,8 +416,25 @@ edit_cmd(void) { #ifdef WITH_SELINUX if ( selinux_context ) { - fprintf(NewCrontab,"SELINUX_ROLE_TYPE=%s\n", selinux_context); - selinux_context = 0; + context_t ccon = NULL; + char *level = NULL; + + if (!(ccon = context_new(selinux_context))) + { + fprintf(stderr, "context_new failed\n"); + goto fatal; + } + + if (!(level = context_range_get(ccon))) + { + fprintf(stderr, "context_range failed\n"); + goto fatal; + } + + fprintf(NewCrontab,"MLS_LEVEL=%s\n", level); + context_free(ccon); + freecon(selinux_context); + selinux_context = NULL; } #endif Only in vixie-cron-4.1: crontab.c.selinux-contains-range diff -rup vixie-cron-4.1-orig/security.c vixie-cron-4.1/security.c --- vixie-cron-4.1-orig/security.c 2006-11-02 22:28:04.000000000 -0500 +++ vixie-cron-4.1/security.c 2006-12-08 11:01:14.000000000 -0500 @@ -23,6 +23,7 @@ #ifdef WITH_SELINUX #include +#include #include #include #include @@ -30,6 +31,12 @@ static char ** build_env(char **cronenv); +#ifdef WITH_SELINUX +static int cron_change_selinux_range( user *u, + security_context_t ucontext ); +static int cron_get_job_range( user *u, security_context_t *ucontextp, char **jobenv ); +#endif + int cron_set_job_security_context( entry *e, user *u, char ***jobenv ) { time_t minutely_time = 0; @@ -58,9 +65,9 @@ int cron_set_job_security_context( entry * we'll not be permitted to read the cron spool directory :-) */ - security_context_t scontext=0, file_context=0; + security_context_t ucontext=0; - if ( cron_get_job_context(u, &scontext, &file_context, *jobenv) < OK ) + if ( cron_get_job_range(u, &ucontext, *jobenv) < OK ) { syslog(LOG_ERR, "CRON (%s) ERROR: failed to get selinux context: %s", e->pwd->pw_name, strerror(errno) @@ -79,16 +86,16 @@ int cron_set_job_security_context( entry } #if WITH_SELINUX - if ( cron_change_selinux_context( u, scontext, file_context ) != 0 ) + if (cron_change_selinux_range(u, ucontext) != 0) { syslog(LOG_INFO,"CRON (%s) ERROR: failed to change SELinux context", e->pwd->pw_name); - if ( file_context ) - freecon(file_context); + if ( ucontext ) + freecon(ucontext); return -1; } - if ( file_context ) - freecon(file_context); + if ( ucontext ) + freecon(ucontext); #endif log_close(); @@ -201,6 +208,7 @@ cron_authorize_context #ifdef WITH_SELINUX struct av_decision avd; int retval; + unsigned int bit = FILE__ENTRYPOINT; /* * Since crontab files are not directly executed, * crond must ensure that the crontab file has @@ -208,13 +216,35 @@ cron_authorize_context * the user cron job. It performs an entrypoint * permission check for this purpose. */ - retval = security_compute_av(scontext, - file_context, - SECCLASS_FILE, - FILE__ENTRYPOINT, - &avd); + retval = security_compute_av(scontext, file_context, + SECCLASS_FILE, bit, &avd); + + if (retval || ((bit & avd.allowed) != bit)) + return 0; +#endif + return 1; +} + +static int +cron_authorize_range +( + security_context_t scontext, + security_context_t ucontext +) +{ +#ifdef WITH_SELINUX + struct av_decision avd; + int retval; + unsigned int bit = CONTEXT__CONTAINS; + /* + * Since crontab files are not directly executed, + * so crond must ensure that any user specified range + * falls within the seusers-specified range for that Linux user. + */ + retval = security_compute_av(scontext, ucontext, + SECCLASS_CONTEXT, bit, &avd); - if (retval || ((FILE__ENTRYPOINT & avd.allowed) != FILE__ENTRYPOINT)) + if (retval || ((bit & avd.allowed) != bit)) return 0; #endif return 1; @@ -265,6 +295,79 @@ int cron_get_job_context( user *u, void return 0; } +#if WITH_SELINUX +/* always uses u->scontext as the default process context, then changes the + level, and retuns it in ucontextp (or NULL otherwise) */ +static int cron_get_job_range( user *u, security_context_t *ucontextp, + char **jobenv ) +{ + char *range; + + if ( is_selinux_enabled() <= 0 ) + return 0; + if ( ucontextp == 0L ) + return -1; + + *ucontextp = 0L; + + if ( (range = env_get("MLS_LEVEL",jobenv)) != 0L ) + { + context_t ccon; + + if (!(ccon = context_new(u->scontext))) + { + log_it(u->name, + getpid(), "context_new FAILED for MLS_LEVEL", + range); + return -1; + } + + if (context_range_set(ccon, range)) + { + log_it(u->name, + getpid(), "context_range_set FAILED for MLS_LEVEL", + range); + return -1; + } + + if (!(*ucontextp = context_str(ccon))) + { + log_it(u->name, + getpid(), "context_str FAILED for MLS_LEVEL", + range); + return -1; + } + + if (!(*ucontextp = strdup(*ucontextp))) + { + log_it(u->name, + getpid(), "strdup FAILED for MLS_LEVEL", + range); + return -1; + } + + context_free(ccon); + } + else if ( strcmp(u->name,"*system*") == 0 ) + { /* no setexeccon() for system cron jobs */ + return 0; + } + else if (!u->scontext) + { /* cron_change_selinux_range() deals with this */ + return 0; + } + else if (!(*ucontextp = strdup(u->scontext))) + { + log_it(u->name, + getpid(), "strdup FAILED for MLS_LEVEL", + range); + return -1; + } + + return 0; +} +#endif + int cron_change_selinux_context( user *u, void *scontext, void *file_context ) { #ifdef WITH_SELINUX @@ -332,6 +435,87 @@ int cron_change_selinux_context( user *u return 0; } +#ifdef WITH_SELINUX +static int cron_change_selinux_range( user *u, + security_context_t ucontext ) +{ + if ( is_selinux_enabled() <= 0 ) + return 0; + + if ( u->scontext == 0L ) + { + if (security_getenforce() > 0) + { + log_it( u->name, getpid(), + "NULL security context for user", + "" + ); + return -1; + }else + { + log_it( u->name, getpid(), + "NULL security context for user, " + "but SELinux in permissive mode, continuing", + "" + ); + return 0; + } + } + + if ( ucontext == NULL ) /* *system* jobs don't have an exec context */ + return 0; + + if ( strcmp(u->scontext, ucontext) ) + { + if ( ! cron_authorize_range( u->scontext, ucontext )) + { + if ( security_getenforce() > 0 ) + { + syslog(LOG_ERR, + "CRON (%s) ERROR:" + "Unauthorized range in MLS_LEVEL %s for user", + u->name, (char*)ucontext + ); + return -1; + } else + { + syslog(LOG_INFO, + "CRON (%s) WARNING:" + "Unauthorized range in MLS_LEVEL %s for user," + " but SELinux in permissive mode, continuing", + u->name, (char*)ucontext + ); + } + } + } + + if ( setexeccon(ucontext) < 0 ) + { + if (security_getenforce() > 0) + { + syslog(LOG_ERR, + "CRON (%s) ERROR:" + "Could not set exec context to %s for user", + u->name, (char*)ucontext + ); + + return -1; + } else + { + syslog(LOG_ERR, + "CRON (%s) ERROR:" + "Could not set exec context to %s for user, " + " but SELinux in permissive mode, continuing", + u->name, (char*)ucontext + ); + + return 0; + } + } + return 0; +} +#endif + int get_security_context( const char *name, int crontab_fd, security_context_t *rcontext, @@ -366,6 +550,12 @@ int get_security_context( const char *na return (security_getenforce() > 0); } + if ( strcmp(name,"system_u") == 0 ) /* skip checks for system */ + { + *rcontext = scontext; + return 0; + } + if (fgetfilecon(crontab_fd, &file_context) < OK) { if (security_getenforce() > 0) { log_it(name, getpid(), "getfilecon FAILED", tabname); Only in vixie-cron-4.1: security.c.security Only in vixie-cron-4.1: security.c.selinux-contains-range