rpms/vixie-cron/devel vixie-cron-4.1-_53_bz199294_selinux_mls.patch, NONE, 1.1 vixie-cron-4.1-_54_bz198019_database_changes.patch, NONE, 1.1 vixie-cron.spec, 1.71, 1.72
fedora-cvs-commits at redhat.com
fedora-cvs-commits at redhat.com
Fri Jul 21 02:01:27 UTC 2006
Author: jvdias
Update of /cvs/dist/rpms/vixie-cron/devel
In directory cvs.devel.redhat.com:/tmp/cvs-serv2981
Modified Files:
vixie-cron.spec
Added Files:
vixie-cron-4.1-_53_bz199294_selinux_mls.patch
vixie-cron-4.1-_54_bz198019_database_changes.patch
Log Message:
fix bugs 198019, 199294
vixie-cron-4.1-_53_bz199294_selinux_mls.patch:
crontab.1 | 8 ++
crontab.5 | 13 +++
crontab.c | 52 ++++++++++----
funcs.h | 4 -
security.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------
5 files changed, 242 insertions(+), 54 deletions(-)
--- NEW FILE vixie-cron-4.1-_53_bz199294_selinux_mls.patch ---
--- vixie-cron-4.1/crontab.c.selinux_mls 2006-07-20 20:10:27.000000000 -0400
+++ vixie-cron-4.1/crontab.c 2006-07-20 20:10:27.000000000 -0400
@@ -43,10 +43,19 @@
#if DEBUGGING
static char *Options[] = { "???", "list", "delete", "edit", "replace" };
+# ifdef WITH_SELINUX
+static char *getoptargs = "u:lerisx:";
+# else
static char *getoptargs = "u:lerix:";
+# endif
#else
+# ifdef WITH_SELINUX
+static char *getoptargs = "u:leris";
+# else
static char *getoptargs = "u:leri";
+# endif
#endif
+static char *selinux_context = 0;
static PID_T Pid;
static char User[MAX_UNAME], RealUser[MAX_UNAME];
@@ -75,6 +84,9 @@
fprintf(stderr, "\t-l\t(list user's crontab)\n");
fprintf(stderr, "\t-r\t(delete user's crontab)\n");
fprintf(stderr, "\t-i\t(prompt before deleting user's crontab)\n");
+#ifdef WITH_SELINUX
+ fprintf(stderr, "\t-s\t(selinux context)\n");
+#endif
exit(ERROR_EXIT);
}
@@ -198,6 +210,16 @@
case 'i':
PromptOnDelete = 1;
break;
+#ifdef WITH_SELINUX
+ case 's':
+ if ( getprevcon( (security_context_t*)&(selinux_context) ) )
+ {
+ fprintf(stderr,
+ "Cannot obtain SELinux process context\n");
+ exit(ERROR_EXIT);
+ }
+ break;
+#endif
default:
usage("unrecognized option");
}
@@ -317,7 +339,7 @@
edit_cmd(void) {
char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
FILE *f;
- int ch='\0', t, x;
+ int ch='\0', t;
struct stat statbuf;
struct utimbuf utimebuf;
WAIT_T waiter;
@@ -379,26 +401,22 @@
}
Set_LineNum(1)
-
- /* ignore the top NHEADER_LINES comment lines since we put them there.
+ /*
+ * NHEADER_LINES processing removed for clarity
+ * (NHEADER_LINES == 0 in all Red Hat crontabs)
*/
- x = 0;
- while ((x < NHEADER_LINES) && (EOF != (ch = get_char(f)))) {
- if ('#' != ch) {
- putc(ch, NewCrontab);
- break;
- }
- while (EOF != (ch = get_char(f)))
- if (ch == '\n')
- break;
- ++x;
- }
-
+
/* copy the rest of the crontab (if any) to the temp file.
*/
if (EOF != ch)
while (EOF != (ch = get_char(f)))
putc(ch, NewCrontab);
+
+#ifdef WITH_SELINUX
+ if ( selinux_context )
+ fprintf(NewCrontab,"SELINUX_ROLE_TYPE=%s\n", selinux_context);
+#endif
+
fclose(f);
if (fflush(NewCrontab) < OK) {
perror(Filename);
@@ -610,6 +628,10 @@
*fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
*fprintf(tmp, "# (Cron version %s -- %s)\n", CRON_VERSION, rcsid);
*/
+#ifdef WITH_SELINUX
+ if ( selinux_context )
+ fprintf(tmp,"SELINUX_ROLE_TYPE=%s\n", selinux_context);
+#endif
/* copy the crontab to the tmp
*/
--- vixie-cron-4.1/funcs.h.selinux_mls 2006-07-20 20:10:27.000000000 -0400
+++ vixie-cron-4.1/funcs.h 2006-07-20 20:10:27.000000000 -0400
@@ -88,7 +88,9 @@
int cron_change_user( struct passwd *pw );
-int cron_change_selinux_context( user *u );
+int cron_get_job_context( user *u, void *scontextp, void *file_contextp, char **envp );
+
+int cron_change_selinux_context( user *, void *scontext, void *file_context );
int get_security_context(const char *name,
int crontab_fd,
--- vixie-cron-4.1/crontab.1.selinux_mls 2006-07-20 20:10:26.000000000 -0400
+++ vixie-cron-4.1/crontab.1 2006-07-20 20:10:27.000000000 -0400
@@ -32,6 +32,7 @@
.RB [ -u
.IR user ]
.RB [ -l " | " -r " | " -e ] [ -i ]
+.RB [ -s ]
.SH DESCRIPTION
.I Crontab
is the program used to install, deinstall or list the tables
@@ -89,6 +90,13 @@
.I -i
option modifies the -r option to prompt the user for a 'y/Y' response
before actually removing the crontab.
+.PP
+The
+.I -s
+option will append the current SELinux security context string as an
+SELINUX_ROLE_TYPE setting to the crontab file before editing / replacement
+occurs - see the documentation of SELINUX_ROLE_TYPE in
+.IR crontab(5) .
.SH "SEE ALSO"
crontab(5), cron(8)
.SH FILES
--- vixie-cron-4.1/crontab.5.selinux_mls 2006-07-20 20:10:27.000000000 -0400
+++ vixie-cron-4.1/crontab.5 2006-07-20 20:10:27.000000000 -0400
@@ -87,6 +87,19 @@
setting the CONTENT_TYPE and CONTENT_TRANSFER_ENCODING variables in crontabs,
to the correct values of the mail headers of those names.
.PP
+The SELINUX_ROLE_TYPE environment variable provides support for multiple per-job
+SELinux security contexts in the same crontab.
+By default, cron jobs execute with the default SELinux security context of the
+user that created the crontab file.
+When using multiple security levels and roles, this may not be sufficient, because
+the same user may be running in a different role or at a different security level.
+You can set SELINUX_ROLE_TYPE to the SELinux security context string specifying
+the SELinux security context in which you want the job to run, and crond will set
+the execution context of the or jobs to which the setting applies to the specified
+context.
+See also the
+.IR crontab(1) -s option.
+.PP
The format of a cron command is very much the V7 standard, with a number of
upward-compatible extensions. Each line has five time and date fields,
followed by a user name if this is the system crontab file,
--- vixie-cron-4.1/security.c.selinux_mls 2006-07-20 20:10:27.000000000 -0400
+++ vixie-cron-4.1/security.c 2006-07-20 20:29:32.000000000 -0400
@@ -49,23 +49,49 @@
);
return -1;
}
-
+
+ *jobenv = build_env( e->envp );
+
+#ifdef WITH_SELINUX
+
+ /* we must get the crontab context BEFORE changing user, else
+ * we'll not be permitted to read the cron spool directory :-)
+ */
+
+ security_context_t scontext=0, file_context=0;
+
+ if ( cron_get_job_context(u, &scontext, &file_context, *jobenv) < OK )
+ {
+ syslog(LOG_ERR, "CRON (%s) ERROR: failed to get selinux context: %s",
+ e->pwd->pw_name, strerror(errno)
+ );
+ return -1;
+ }
+
+#endif
+
if ( cron_change_user( 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 ( cron_change_selinux_context( u ) != 0 )
+ }
+
+ if ( cron_change_selinux_context( u, scontext, file_context ) != 0 )
{
syslog(LOG_INFO,"CRON (%s) ERROR: failed to change SELinux context",
e->pwd->pw_name);
+#if WITH_SELINUX
+ if ( file_context )
+ freecon(file_context);
+#endif
return -1;
}
-
- *jobenv = build_env( e->envp );
+#if WITH_SELINUX
+ if ( file_context )
+ freecon(file_context);
+#endif
log_close();
openlog(ProgramName, LOG_PID, LOG_CRON);
@@ -167,23 +193,145 @@
return 0;
}
-int cron_change_selinux_context( user *u )
+static int
+cron_authorize_context
+(
+ security_context_t scontext,
+ security_context_t file_context
+)
{
#ifdef WITH_SELINUX
- if ((is_selinux_enabled() >0) && (u->scontext != 0L)) {
- if (setexeccon(u->scontext) < 0) {
- if (security_getenforce() > 0) {
- syslog(LOG_INFO,
- "CRON (%s) ERROR:"
- "Could not set exec context to %s for user\n",
- u->name, u->scontext
- );
+ struct av_decision avd;
+ int retval;
+ /*
+ * Since crontab files are not directly executed,
+ * crond must ensure that the crontab file has
+ * a context that is appropriate for the context of
+ * 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);
+
+ if (retval || ((FILE__ENTRYPOINT & avd.allowed) != FILE__ENTRYPOINT))
+ return 0;
+#endif
+ return 1;
+}
+
+int cron_get_job_context( user *u, void *scontextp, void *file_contextp, char **jobenv )
+{
+#if WITH_SELINUX
+ char *sroletype;
+
+ if ( is_selinux_enabled() <= 0 )
+ return 0;
+ if ( (file_contextp == 0) || (scontextp == 0L) )
return -1;
- }
+
+ *((security_context_t*)scontextp) = u->scontext;
+ *((void **)file_contextp) = 0L;
+
+ if ( (sroletype = env_get("SELINUX_ROLE_TYPE",jobenv)) != 0L )
+ {
+ *((security_context_t*)scontextp) = (security_context_t) sroletype;
+
+ char crontab[MAX_FNAME];
+ if ( strcmp(u->name,"*system*") == 0 )
+ strncpy(crontab, u->tabname, MAX_FNAME);
+ else
+ snprintf(crontab, MAX_FNAME, "%s/%s", CRONDIR, u->tabname);
+
+ if ( getfilecon( crontab, file_contextp ) == -1 )
+ {
+ if ( security_getenforce() > 0 )
+ {
+ log_it(u->name,
+ getpid(), "getfilecon FAILED for SELINUX_ROLE_TYPE",
+ sroletype
+ );
+ return -1;
+ } else
+ if ( access( crontab, F_OK ) )
+ log_it(u->name,
+ getpid(),
+ "getfilecon FAILED but SELinux in permissive mode, continuing "
+ "- SELINUX_ROLE_TYPE=", sroletype
+ );
+ }
}
- }
#endif
- return 0;
+ return 0;
+}
+
+int cron_change_selinux_context( user *u, void *scontext, void *file_context )
+{
+#ifdef WITH_SELINUX
+ if ( is_selinux_enabled() <= 0 )
+ return 0;
+
+ if ( 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 ( file_context )
+ {
+ if ( ! cron_authorize_context( scontext, file_context ) )
+ {
+ if ( security_getenforce() > 0 )
+ {
+ syslog(LOG_ERR,
+ "CRON (%s) ERROR:"
+ "Unauthorized exec context to SELINUX_ROLE_TYPE %s for user",
+ u->name, (char*)scontext
+ );
+ return -1;
+ } else
+ {
+ syslog(LOG_INFO,
+ "CRON (%s) WARNING:"
+ "Unauthorized exec context to SELINUX_ROLE_TYPE %s for user,"
+ " but SELinux in permissive mode, continuing",
+ u->name, (char*)scontext
+ );
+ }
+ }
+ }
+
+ if ( setexeccon(scontext) < 0 )
+ {
+ if (security_getenforce() > 0)
+ {
+ syslog(LOG_ERR,
+ "CRON (%s) ERROR:"
+ "Could not set exec context to %s for user",
+ u->name, (char*)scontext
+ );
+
+ return -1;
+ }
+ }
+#endif
+ return 0;
}
int get_security_context( const char *name,
@@ -192,8 +340,7 @@
const char *tabname) {
#ifdef WITH_SELINUX
security_context_t scontext=NULL;
- security_context_t file_context=NULL;
- struct av_decision avd;
+ security_context_t file_context=NULL;
int retval=0;
char *seuser=NULL;
char *level=NULL;
@@ -233,28 +380,24 @@
}
}
- /*
- * Since crontab files are not directly executed,
- * crond must ensure that the crontab file has
- * a context that is appropriate for the context of
- * 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);
- freecon(file_context);
- if (retval || ((FILE__ENTRYPOINT & avd.allowed) != FILE__ENTRYPOINT)) {
+ if ( ! cron_authorize_context( scontext, file_context ) )
+ {
+ freecon(scontext);
+ freecon(file_context);
if (security_getenforce() > 0) {
- log_it(name, getpid(), "ENTRYPOINT FAILED", tabname);
- freecon(scontext);
+ log_it(name, getpid(), "Unauthorized SELinux context", tabname);
return -1;
- } else {
- log_it(name, getpid(), "ENTRYPOINT FAILED but SELinux in permissive mode, continuing", tabname);
+ } else
+ {
+ log_it(name, getpid(),
+ "Unauthorized SELinux context, but SELinux in permissive mode, continuing",
+ tabname
+ );
+ return 0;
}
}
+ freecon(file_context);
+
*rcontext=scontext;
#endif
return 0;
vixie-cron-4.1-_54_bz198019_database_changes.patch:
database.c | 129 ++++++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 86 insertions(+), 43 deletions(-)
--- NEW FILE vixie-cron-4.1-_54_bz198019_database_changes.patch ---
--- vixie-cron-4.1/database.c.bz198019 2006-07-20 21:42:01.000000000 -0400
+++ vixie-cron-4.1/database.c 2006-07-20 21:46:51.000000000 -0400
@@ -34,6 +34,12 @@
const char *, struct stat *,
cron_db *, cron_db *);
+static int not_a_crontab( DIR_T *dp );
+/* return 1 if we should skip this file */
+
+static void max_mtime( char *dir_name, struct stat *max_st );
+/* record max mtime of any file under dir_name in max_st */
+
void
load_database(cron_db *old_db) {
struct stat statbuf, syscron_stat, crond_stat;
@@ -52,12 +58,20 @@
log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
(void) exit(ERROR_EXIT);
}
+
+ /* As pointed out in Red Hat bugzilla 198019, with modern Linux it
+ * is possible to modify a file without modifying the mtime of the
+ * containing directory. Hence, we must check the mtime of each file:
+ */
+ max_mtime(SPOOL_DIR, &statbuf);
if (stat(RH_CROND_DIR, &crond_stat) < OK) {
log_it("CRON", getpid(), "STAT FAILED", RH_CROND_DIR);
(void) exit(ERROR_EXIT);
}
+ max_mtime(RH_CROND_DIR, &crond_stat);
+
/* track system crontab file
*/
if (stat(SYSCRONTAB, &syscron_stat) < OK)
@@ -97,40 +111,12 @@
}
while (NULL != (dp = readdir(dir))) {
- char fname[MAXNAMLEN+1],
- tabname[MAXNAMLEN+1];
- size_t len;
-
- /* avoid file names beginning with ".". this is good
- * because we would otherwise waste two guaranteed calls
- * to getpwnam() for . and .., and there shouldn't be
- * hidden files in here anyway
- */
- if (dp->d_name[0] == '.')
- continue;
-
- /* ignore files starting with # and ending with ~ */
- if (dp->d_name[0] == '#')
- continue;
-
- len = strlen(dp->d_name);
-
- if (len >= sizeof fname)
- continue; /* XXX log? */
-
- if ((len > 0) && (dp->d_name[len - 1] == '~'))
- continue;
+ char tabname[MAXNAMLEN+1];
- (void) strcpy(fname, dp->d_name);
-
- if ((len > 8) && (strncmp(fname + len - 8, ".rpmsave", 8) == 0))
- continue;
- if ((len > 8) && (strncmp(fname + len - 8, ".rpmorig", 8) == 0))
- continue;
- if ((len > 7) && (strncmp(fname + len - 7, ".rpmnew", 7) == 0))
+ if ( not_a_crontab( dp ) )
continue;
- if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, fname, '/'))
+ if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/'))
continue; /* XXX log? */
process_crontab("root", NULL, tabname,
@@ -142,6 +128,7 @@
* efficiency. however, we need to close it in every fork, and
* we fork a lot more often than the mtime of the dir changes.
*/
+
if (!(dir = opendir(SPOOL_DIR))) {
log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
(void) exit(ERROR_EXIT);
@@ -150,20 +137,12 @@
while (NULL != (dp = readdir(dir))) {
char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1];
- /* avoid file names beginning with ".". this is good
- * because we would otherwise waste two guaranteed calls
- * to getpwnam() for . and .., and also because user names
- * starting with a period are just too nasty to consider.
- */
- if (dp->d_name[0] == '.')
+ if ( not_a_crontab( dp ) )
continue;
- if (strlen(dp->d_name) >= sizeof fname)
- continue; /* XXX log? */
- (void) strcpy(fname, dp->d_name);
-
- if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR,
- fname, '/'))
+ strncpy(fname, dp->d_name, MAXNAMLEN);
+
+ if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/'))
continue; /* XXX log? */
process_crontab(fname, fname, tabname,
@@ -322,3 +301,67 @@
close(crontab_fd);
}
}
+
+static int not_a_crontab( DIR_T *dp )
+{
+ int len;
+
+ /* avoid file names beginning with ".". this is good
+ * because we would otherwise waste two guaranteed calls
+ * to getpwnam() for . and .., and there shouldn't be
+ * hidden files in here anyway
+ */
+ if (dp->d_name[0] == '.')
+ return(1);
+
+ /* ignore files starting with # and ending with ~ */
+ if (dp->d_name[0] == '#')
+ return(1);
+
+ len = strlen(dp->d_name);
+
+ if (len >= MAXNAMLEN)
+ return(1); /* XXX log? */
+
+ if ((len > 0) && (dp->d_name[len - 1] == '~'))
+ return(1);
+
+ if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmsave", 8) == 0))
+ return(1);
+ if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmorig", 8) == 0))
+ return(1);
+ if ((len > 7) && (strncmp(dp->d_name + len - 7, ".rpmnew", 7) == 0))
+ return(1);
+
+ return(0);
+}
+
+static void max_mtime( char *dir_name, struct stat *max_st )
+{
+ DIR * dir;
+ DIR_T *dp;
+ struct stat st;
+
+ if (!(dir = opendir(dir_name))) {
+ log_it("CRON", getpid(), "OPENDIR FAILED", dir_name);
+ (void) exit(ERROR_EXIT);
+ }
+
+ while (NULL != (dp = readdir(dir)))
+ {
+ char tabname[MAXNAMLEN+1];
+
+ if ( not_a_crontab ( dp ) )
+ continue;
+
+ if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, dp->d_name, '/'))
+ continue; /* XXX log? */
+
+ if ( stat( tabname, &st ) < OK )
+ continue; /* XXX log? */
+
+ if ( st.st_mtime > max_st->st_mtime )
+ max_st->st_mtime = st.st_mtime;
+ }
+ closedir(dir);
+}
Index: vixie-cron.spec
===================================================================
RCS file: /cvs/dist/rpms/vixie-cron/devel/vixie-cron.spec,v
retrieving revision 1.71
retrieving revision 1.72
diff -u -r1.71 -r1.72
--- vixie-cron.spec 15 Jul 2006 00:27:50 -0000 1.71
+++ vixie-cron.spec 21 Jul 2006 02:01:22 -0000 1.72
@@ -10,7 +10,7 @@
Summary: The Vixie cron daemon for executing specified programs at set times.
Name: vixie-cron
Version: 4.1
-Release: 56.FC6
+Release: 58%{?dist}
Epoch: 4
License: distributable
Group: System Environment/Base
@@ -70,6 +70,8 @@
Patch50: vixie-cron-4.1-_50-bz178931.patch
Patch51: vixie-cron-4.1-_51-bz180145-mail_i18n.patch
Patch52: vixie-cron-4.1-_52-bz181439.patch
+Patch53: vixie-cron-4.1-_53_bz199294_selinux_mls.patch
+Patch54: vixie-cron-4.1-_54_bz198019_database_changes.patch
Buildroot: %{_tmppath}/%{name}-%{version}-root
Requires: syslog, bash >= 2.0
Conflicts: sysklogd < 1.4.1
@@ -158,6 +160,8 @@
%patch50 -p1 -b .bz178931
%patch51 -p1 -b .mail_i18n
%patch52 -p1 -b .bz181439
+%patch53 -p1 -b .selinux_mls
+%patch54 -p1 -b .bz198019
%build
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS -g -DLINT -Dlint -Werror" \
@@ -225,6 +229,11 @@
%config(noreplace) /etc/sysconfig/crond
%changelog
+* Thu Jul 20 2006 Jason Vas Dias <jvdias at redhat.com> - 4:4.1-58
+- fix bug 199294: support for LSPP multiple per-job SELinux contexts
+- fix bug 198019: make database.c correct if crontab mtime changes
+ while spool dir mtime does not.
+
* Fri Jul 14 2006 Jason Vas Dias <jvdias at redhat.com> - 4:4.1-56.FC6
- fix bug 198893 - change permissions of cron spool directories to 0700
More information about the fedora-cvs-commits
mailing list