From: mmaslano Date: Fri, 17 Aug 2007 13:19:26 +0000 (+0200) Subject: Selinux ranges: for every selinux operation are now checked X-Git-Tag: v4.2~33 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dd0d760553cf093aa613f72bee70aa35031d4070;p=cronie Selinux ranges: for every selinux operation are now checked the ranges of user. Now is set not only context for user, but even ranges(enabled selinux or selinux in mls mode). --- diff --git a/crontab.c b/crontab.c index b1c730a..e704e78 100644 --- a/crontab.c +++ b/crontab.c @@ -33,6 +33,7 @@ static char rcsid[] = "$Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $"; #include "cron.h" #ifdef WITH_SELINUX #include +#include #include #endif @@ -408,8 +409,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 diff --git a/do_command.c b/do_command.c index 33a43b3..93191a6 100644 --- a/do_command.c +++ b/do_command.c @@ -238,6 +238,7 @@ child_process(entry *e, user *u) { } break; default: + cron_restore_default_security_context(); /* parent process */ break; } diff --git a/funcs.h b/funcs.h index 9019c21..bd2d973 100644 --- a/funcs.h +++ b/funcs.h @@ -80,13 +80,15 @@ long get_gmtoff(time_t *, struct tm *); /* Red Hat security stuff (security.c): */ +int cron_restore_default_security_context( void ); + int cron_set_job_security_context( entry *e, user *u, char ***jobenvp ); int cron_open_security_session( struct passwd *pw ); void cron_close_security_session( void ); -int cron_change_user( struct passwd *pw ); +int cron_change_user( struct passwd *pw, char *homedir ); int cron_get_job_context( user *u, void *scontextp, void *file_contextp, char **envp ); diff --git a/security.c b/security.c index 4115680..1a12241 100644 --- a/security.c +++ b/security.c @@ -23,6 +23,7 @@ #ifdef WITH_SELINUX #include +#include #include #include #include @@ -30,6 +31,15 @@ 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_restore_default_security_context() { + setexeccon(NULL); +} int cron_set_job_security_context( entry *e, user *u, char ***jobenv ) { time_t minutely_time = 0; @@ -58,9 +68,9 @@ int cron_set_job_security_context( entry *e, user *u, char ***jobenv ) * 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) @@ -68,38 +78,37 @@ int cron_set_job_security_context( entry *e, user *u, char ***jobenv ) return -1; } + if (cron_change_selinux_range(u, ucontext) != 0) + { + syslog(LOG_INFO,"CRON (%s) ERROR: failed to change SELinux context", + e->pwd->pw_name); + if ( ucontext ) + freecon(ucontext); + return -1; + } + if ( ucontext ) + freecon(ucontext); #endif - if ( cron_change_user( e->pwd ) != 0 ) + if ( cron_start_security_session( e->pwd ) != 0 ) { syslog(LOG_INFO, "CRON (%s) ERROR: failed to open PAM security session: %s", e->pwd->pw_name, strerror(errno) ); return -1; - } + } -#if WITH_SELINUX - if ( cron_change_selinux_context( u, scontext, file_context ) != 0 ) + if ( cron_change_user( e->pwd, env_get("HOME", *jobenv)) != 0 ) { - syslog(LOG_INFO,"CRON (%s) ERROR: failed to change SELinux context", - e->pwd->pw_name); - if ( file_context ) - freecon(file_context); + syslog(LOG_INFO, "CRON (%s) ERROR: failed to open PAM security session: %s", + e->pwd->pw_name, strerror(errno) + ); return -1; - } - if ( file_context ) - freecon(file_context); -#endif + } log_close(); openlog(ProgramName, LOG_PID, LOG_CRON); - if ( chdir(env_get("HOME", *jobenv)) == -1 ) - { - log_it("CRON", getpid(), "chdir(HOME) failed:", strerror(errno)); - return -1; - } - time_t job_run_time = time(0L); if( (minutely_time > 0) @@ -145,10 +154,20 @@ int cron_open_security_session( struct passwd *pw ) PAM_FAIL_CHECK; retcode = pam_acct_mgmt(pamh, PAM_SILENT); PAM_FAIL_CHECK; - retcode = pam_open_session(pamh, PAM_SILENT); - PAM_FAIL_CHECK; +#endif + + return retcode; +} + +int cron_start_security_session( struct passwd *pw ) +{ + int retcode = 0; + +#if defined(WITH_PAM) retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); PAM_FAIL_CHECK; + retcode = pam_open_session(pamh, PAM_SILENT); + PAM_FAIL_CHECK; log_close(); /* PAM has now re-opened our log to auth.info ! */ openlog(ProgramName, LOG_PID, LOG_CRON); #endif @@ -165,7 +184,7 @@ void cron_close_security_session( void ) #endif } -int cron_change_user( struct passwd *pw ) +int cron_change_user( struct passwd *pw, char *homedir ) { /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. @@ -176,6 +195,13 @@ int cron_change_user( struct passwd *pw ) return -1; } + if ( chdir(homedir) == -1 ) + { + log_it("CRON", getpid(), "chdir(HOME) failed:", strerror(errno)); + log_it("CRON", getpid(), homedir, strerror(errno)); + return -1; + } + if ( initgroups( pw->pw_name, pw->pw_gid ) != 0 ) { log_it("CRON", getpid(), "initgroups failed:", strerror(errno)); @@ -201,6 +227,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 +235,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 +314,75 @@ int cron_get_job_context( user *u, void *scontextp, void *file_contextp, char ** 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 (!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 +450,84 @@ int cron_change_selinux_context( user *u, void *scontext, void *file_context ) 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 ( strcmp(u->scontext, ucontext) ) + { + if ( ! cron_authorize_range( u->scontext, ucontext )) + { + if ( security_getenforce() > 0 ) + { + syslog(LOG_ERR, + "CRON (%s) ERROR:" + "Unauthorized range %s in MLS_LEVEL for user %s ", + u->name, (char*)ucontext, u->scontext + ); + return -1; + } else + { + syslog(LOG_INFO, + "CRON (%s) WARNING:" + "Unauthorized range %s in MLS_LEVEL for user %s," + " but SELinux in permissive mode, continuing", + u->name, (char*)ucontext, u->scontext + ); + } + } + } + + 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, @@ -449,3 +645,4 @@ static char ** build_env(char **cronenv) return env_copy(cronenv); #endif } +