Репозитории ALT
S: | 4.1.20060426-alt10.3 |
5.1: | 4.1.20060426-alt6 |
4.1: | 4.1.20060426-alt4 |
4.0: | 4.1.20060426-alt4 |
3.0: | 4.1.20040916-alt2 |
Группа :: Система/Серверы
Пакет: vixie-cron
Главная Изменения Спек Патчи Sources Загрузить Gear Bugs and FR Repocop
Патч: vixie-cron-4.1.20060426-alt-selinux.patch
Скачать
Скачать
diff --git a/usr.bin/crontab/Makefile b/usr.bin/crontab/Makefile
index d6da601..c08d580 100644
--- a/usr.bin/crontab/Makefile
+++ b/usr.bin/crontab/Makefile
@@ -1,10 +1,11 @@
# $OpenBSD: Makefile,v 1.5 2005/12/19 19:12:17 millert Exp $
PROG= crontab
-SRCS= crontab.c misc.c entry.c env.c closeall.c ../../lib/libc/gen/pw_dup.c
-CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../usr.sbin/cron -DDEBUGGING=0
+SRCS= crontab.c misc.c entry.c env.c closeall.c selinux.c ../../lib/libc/gen/pw_dup.c
+CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../../usr.sbin/cron -DDEBUGGING=0 -DWITH_SELINUX=1
BINGRP =crontab
BINMODE=2555
+LDLIBS+=-lselinux
MAN= crontab.1 crontab.5
#.PATH: ${.CURDIR}/../../usr.sbin/cron
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
index 9f4d50e..ee7e7f2 100644
--- a/usr.sbin/cron/Makefile
+++ b/usr.sbin/cron/Makefile
@@ -2,9 +2,9 @@
PROG= crond
SRCS= cron.c database.c user.c entry.c job.c do_command.c \
- misc.c env.c popen.c atrun.c closeall.c pam_auth.c ../../lib/libc/gen/pw_dup.c
-CFLAGS+=-I${.CURDIR} -DHAVE_SETPROCTITLE=1
-LDLIBS+=-lpam -lsetproctitle
+ misc.c env.c popen.c atrun.c closeall.c pam_auth.c selinux.c ../../lib/libc/gen/pw_dup.c
+CFLAGS+=-I${.CURDIR} -DHAVE_SETPROCTITLE=1 -DWITH_SELINUX=1
+LDLIBS+=-lpam -lsetproctitle -lselinux
MAN= cron.8
#.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/atrun.c b/usr.sbin/cron/atrun.c
index 328b729..8a53d71 100644
--- a/usr.sbin/cron/atrun.c
+++ b/usr.sbin/cron/atrun.c
@@ -529,6 +529,17 @@ run_job(atjob *job, char *atfile)
if (job->queue > 'b')
(void)setpriority(PRIO_PROCESS, 0, job->queue - 'b');
+#ifdef WITH_SELINUX
+ if (is_selinux_enabled() > 0) {
+ char *err_msg = NULL;
+ if (set_at_selinux_context(pw->pw_name, STDIN, &err_msg) < 0) {
+ fprintf(stderr, "at job %s: %s\n", atfile, err_msg);
+ free(err_msg);
+ if (security_getenforce() > 0)
+ _exit(ERROR_EXIT);
+ }
+ }
+#endif /* WITH_SELINUX */
#if DEBUGGING
if (DebugFlags & DTEST) {
fprintf(stderr,
diff --git a/usr.sbin/cron/crontab.1 b/usr.sbin/cron/crontab.1
index 26f19c9..603f1ff 100644
--- a/usr.sbin/cron/crontab.1
+++ b/usr.sbin/cron/crontab.1
@@ -117,6 +117,11 @@ environment variables.
After you exit from the editor, the modified
.Xr crontab 5
will be installed automatically.
+.It Fl s
+It will append the current SELinux security context string as an
+MLS_LEVEL setting to the crontab file before editing / replacement
+occurs - see the documentation of MLS_LEVEL in
+.Xr crontab 5 .
.El
.Sh FILES
.Bl -tag -width "/etc/cron.allow" -compact
diff --git a/usr.sbin/cron/crontab.5 b/usr.sbin/cron/crontab.5
index 838a330..2cba75d 100644
--- a/usr.sbin/cron/crontab.5
+++ b/usr.sbin/cron/crontab.5
@@ -167,6 +167,24 @@ Otherwise mail is sent to the owner of the
This option is useful for pseudo-users that lack an alias
that would otherwise redirect the mail to a real person.
.Pp
+The
+.Ev MLS_LEVEL
+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.
+For more about roles and SELinux MLS/MCS see
+.Xr selinux 8
+and undermentioned crontab example.
+You can set MLS_LEVEL 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
+.Xr crontab 1 -s option.
+.Pp
.Em cron Commands
.Pp
The format of a
@@ -368,6 +386,27 @@ MAILTO=paul
23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
5 4 * * sun echo "run at 5 after 4 every sunday"
.Ed
+.Sh SELinux with multi level security (MLS)
+In crontab is important specified security level by \fIcrontab\ -s\fR or specifying
+the required level on the first line of the crontab. Each level is specified
+in \fI/etc/selinux/targeted/seusers\fR. For using crontab in MLS mode is really important:
+.br
+- check/change actual role,
+.br
+- set correct \fIrole for directory\fR, which is used for input/output.
+.Sh EXAMPLE FOR SELINUX MLS
+.nf
+# login as root
+newrole -r sysadm_r
+mkdir /tmp/SystemHigh
+chcon -l SystemHigh /tmp/SystemHigh
+crontab -e
+# write in crontab file
+MLS_LEVEL=SystemHigh
+0-59 * * * * id -Z > /tmp/SystemHigh/crontest
+When I log in as a normal user, it can't work, because \fI/tmp/SystemHigh\fR is
+higher than my level.
+.fi
.Sh SEE ALSO
.Xr crontab 1 ,
.Xr cron 8
diff --git a/usr.sbin/cron/crontab.c b/usr.sbin/cron/crontab.c
index 31889af..4335ef4 100644
--- a/usr.sbin/cron/crontab.c
+++ b/usr.sbin/cron/crontab.c
@@ -35,13 +35,20 @@ static char const rcsid[] = "$OpenBSD: crontab.c,v 1.49 2005/11/29 20:43:31 mill
#define NHEADER_LINES 3
#define CRONTAB_TEMPLATE "/etc/crontab.template"
+#ifdef WITH_SELINUX
+static int set_mls_level;
+#define OPT_S "s"
+#else
+#define OPT_S
+#endif /* WITH_SELINUX */
+
enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
#if DEBUGGING
static char *Options[] = { "???", "list", "delete", "edit", "replace" };
-static char *getoptargs = "u:lerx:";
+static char *getoptargs = "u:lerx:" OPT_S;
#else
-static char *getoptargs = "u:ler";
+static char *getoptargs = "u:ler" OPT_S;
#endif
static PID_T Pid;
@@ -71,6 +78,9 @@ usage(const char *msg) {
fprintf(stderr, "\t-e\t(edit user's crontab)\n");
fprintf(stderr, "\t-l\t(list user's crontab)\n");
fprintf(stderr, "\t-r\t(delete user's crontab)\n");
+#ifdef WITH_SELINUX
+ fprintf(stderr, "\t-s\t(selinux context)\n");
+#endif /* WITH_SELINUX */
exit(ERROR_EXIT);
}
@@ -154,6 +164,13 @@ parse_args(int argc, char *argv[]) {
"must be privileged to use -u\n");
exit(ERROR_EXIT);
}
+#ifdef WITH_SELINUX
+ if (selinux_crontab_access_denied()) {
+ fprintf(stderr,
+ "access denied by SELinux, must be privileged to use -u\n");
+ exit(ERROR_EXIT);
+ }
+#endif /* WITH_SELINUX */
if (!(pw = getpwnam(optarg))) {
fprintf(stderr, "%s: user `%s' unknown\n",
ProgramName, optarg);
@@ -177,6 +194,12 @@ parse_args(int argc, char *argv[]) {
usage("only one operation permitted");
Option = opt_edit;
break;
+#ifdef WITH_SELINUX
+ case 's':
+ set_mls_level = 1;
+ break;
+#endif /* WITH_SELINUX */
+
default:
usage("unrecognized option");
}
@@ -363,6 +386,15 @@ edit_cmd(void) {
Set_LineNum(1)
copy_crontab(f, NewCrontab);
fclose(f);
+#ifdef WITH_SELINUX
+ if (set_mls_level) {
+ /* Do not write MLS_LEVEL again in replace_cmd() */
+ set_mls_level = 0;
+ if (write_crontab_mls_level(NewCrontab) < 0)
+ goto fatal;
+ }
+#endif /* WITH_SELINUX */
+
if (fflush(NewCrontab) < OK) {
perror(Filename);
exit(ERROR_EXIT);
@@ -592,6 +624,14 @@ replace_cmd(void) {
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 (set_mls_level && write_crontab_mls_level(NewCrontab) < 0) {
+ fclose(tmp);
+ error = -2;
+ goto done;
+ }
+#endif /* WITH_SELINUX */
+
/* copy the crontab to the tmp
*/
rewind(NewCrontab);
diff --git a/usr.sbin/cron/database.c b/usr.sbin/cron/database.c
index 28b0dd6..d0cbf84 100644
--- a/usr.sbin/cron/database.c
+++ b/usr.sbin/cron/database.c
@@ -40,7 +40,7 @@ struct spooldir {
static struct spooldir spools[] = {
{SPOOL_DIR, NULL, NULL},
- {"/etc/cron.d", "root", "*system*"},
+ {"/etc/cron.d", "root", SYSUSERNAME},
{NULL, NULL, NULL}
};
@@ -99,7 +99,7 @@ load_database(cron_db *old_db) {
new_db.head = new_db.tail = NULL;
if (syscron_stat.st_mtime) {
- process_crontab(ROOT_USER, "*system*", SYSCRONTAB, &syscron_stat,
+ process_crontab(ROOT_USER, SYSUSERNAME, SYSCRONTAB, &syscron_stat,
&new_db, old_db);
}
@@ -210,7 +210,7 @@ process_crontab(const char *uname, const char *fname, const char *tabname,
struct stat lstatbuf;
user *u;
- if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
+ if (strcmp(fname, SYSUSERNAME) && !(pw = getpwnam(uname))) {
/* file doesn't have a user in passwd file.
* */
log_it(fname, getpid(), "ORPHAN", "no passwd entry");
@@ -295,7 +295,8 @@ process_crontab(const char *uname, const char *fname, const char *tabname,
free_user(u);
log_it(fname, getpid(), "RELOAD", tabname);
}
- u = load_user(crontab_fd, pw, fname);
+
+ u = load_user(crontab_fd, pw, fname, tabname);
if (u != NULL) {
u->mtime = statbuf->st_mtime;
link_user(new_db, u);
diff --git a/usr.sbin/cron/do_command.c b/usr.sbin/cron/do_command.c
index d84fc0d..ab4961a 100644
--- a/usr.sbin/cron/do_command.c
+++ b/usr.sbin/cron/do_command.c
@@ -313,6 +313,13 @@ child_process(entry *e, user *u) {
_exit(OK_EXIT);
}
# endif /*DEBUGGING*/
+#ifdef WITH_SELINUX
+ if (is_selinux_enabled() > 0 &&
+ set_job_selinux_context(u, envp) &&
+ security_getenforce() > 0) {
+ _exit(ERROR_EXIT);
+ }
+#endif /* WITH_SELINUX */
execle(shell, shell, "-c", e->cmd, (char *)NULL, envp);
fprintf(stderr, "execle: couldn't exec `%s'\n", shell);
perror("execle");
diff --git a/usr.sbin/cron/externs.h b/usr.sbin/cron/externs.h
index 20b1dec..8af57eb 100644
--- a/usr.sbin/cron/externs.h
+++ b/usr.sbin/cron/externs.h
@@ -68,6 +68,10 @@
# include <bsd_auth.h>
#endif /*BSD_AUTH*/
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif /* WITH_SELINUX */
+
#define DIR_T struct dirent
#define WAIT_T int
#define SIG_T sig_t
diff --git a/usr.sbin/cron/funcs.h b/usr.sbin/cron/funcs.h
index 49f9c4d..2460abe 100644
--- a/usr.sbin/cron/funcs.h
+++ b/usr.sbin/cron/funcs.h
@@ -69,7 +69,7 @@ char *env_get(char *, char **),
struct passwd *pw_dup(const struct passwd *);
void mkprint(char *, unsigned char *, int);
-user *load_user(int, struct passwd *, const char *),
+user *load_user(int, struct passwd *, const char *, const char *),
*find_user(cron_db *, const char *);
entry *load_entry(FILE *,
@@ -89,3 +89,11 @@ extern void cron_pam_finish (void);
extern void cron_pam_child_close (void);
extern char **cron_pam_getenvlist (char **envp);
#endif
+
+#ifdef WITH_SELINUX
+char* get_selinux_context(const char *name, int fd, char **err_msg);
+int set_job_selinux_context(const user *u, char **envp);
+int set_at_selinux_context(const char *name, int fd, char **err_msg);
+int selinux_crontab_access_denied(void);
+int write_crontab_mls_level(FILE *crontab);
+#endif /* WITH_SELINUX */
diff --git a/usr.sbin/cron/macros.h b/usr.sbin/cron/macros.h
index e96c342..1b856ab 100644
--- a/usr.sbin/cron/macros.h
+++ b/usr.sbin/cron/macros.h
@@ -70,6 +70,8 @@
#define MAXHOSTNAMELEN 64
#endif
+#define SYSUSERNAME "*system*"
+
#define Skip_Blanks(c, f) \
while (c == '\t' || c == ' ') \
c = get_char(f);
diff --git a/usr.sbin/cron/selinux.c b/usr.sbin/cron/selinux.c
new file mode 100644
index 0000000..07dd291
--- /dev/null
+++ b/usr.sbin/cron/selinux.c
@@ -0,0 +1,275 @@
+#include "cron.h"
+#include <selinux/context.h>
+#include <selinux/get_context_list.h>
+
+static int
+selinux_authorize(const char *scon, const char *tcon,
+ security_class_t tclass, access_vector_t requested) {
+ struct av_decision avd;
+ int rc;
+
+ rc = security_compute_av(scon, tcon, tclass, requested, &avd);
+ if (rc || ((requested & avd.allowed) != requested))
+ return 0;
+ return 1;
+}
+
+static int
+selinux_authorize_context(const char *scontext,
+ const char *file_context) {
+ /* 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.
+ */
+ security_class_t tclass;
+ access_vector_t bit;
+
+ tclass = string_to_security_class("file");
+ if (tclass <= 0) {
+ log_it("CRON", getpid(),
+ "Failed to translate security class file", strerror(errno));
+ return 0;
+ }
+
+ bit = string_to_av_perm(tclass, "entrypoint");
+ if (bit <= 0) {
+ log_it("CRON", getpid(),
+ "Failed to translate av perm entrypoint", strerror(errno));
+ return 0;
+ }
+
+ return selinux_authorize(scontext, file_context, tclass, bit);
+}
+
+static int
+selinux_authorize_range(const char *scontext,
+ const char *ucontext) {
+ /* 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.
+ */
+ security_class_t tclass;
+ access_vector_t bit;
+
+ tclass = string_to_security_class("context");
+ if (tclass <= 0) {
+ log_it("CRON", getpid(),
+ "Failed to translate security class context", strerror(errno));
+ return 0;
+ }
+
+ bit = string_to_av_perm(tclass, "contains");
+ if (bit <= 0) {
+ log_it("CRON", getpid(),
+ "Failed to translate av perm contains", strerror(errno));
+ return 0;
+ }
+
+ return selinux_authorize(scontext, ucontext, tclass, bit);
+}
+
+static char*
+get_job_context(const char *u_name, const char *u_scontext, const char *range)
+{
+ char *scontext = NULL;
+ context_t ccon = NULL;
+
+ if (range) {
+ if (!(ccon = context_new(u_scontext))) {
+ log_it(u_name, getpid(),
+ "context_new FAILED for MLS_LEVEL", range);
+ return NULL;
+ }
+
+ if (context_range_set(ccon, range)) {
+ log_it(u_name, getpid(),
+ "context_range_set FAILED for MLS_LEVEL", range);
+ goto out;
+ }
+
+ if (!(scontext = context_str(ccon))) {
+ log_it(u_name, getpid(),
+ "context_str FAILED for MLS_LEVEL", range);
+ goto out;
+ }
+ }
+
+ if (!(scontext = strdup(scontext ? scontext : u_scontext))) {
+ log_it(u_name, getpid(), "strdup FAILED for MLS_LEVEL", range);
+ }
+
+out:
+ context_free(ccon);
+ return scontext;
+}
+
+char*
+get_selinux_context(const char *name, int fd, char **err_msg) {
+ char *user_context = NULL;
+ char *file_context = NULL;
+ char *seuser = NULL;
+ char *level = NULL;
+
+ if (fgetfilecon(fd, &file_context) < OK) {
+ *err_msg = strdup("getfilecon FAILED");
+ return NULL;
+ }
+
+ if (name && getseuserbyname(name, &seuser, &level) < OK) {
+ *err_msg = strdup("NO SEUSER");
+ goto out;
+ }
+
+ if (get_default_context_with_level(name ? seuser : "system_u",
+ level, NULL, &user_context) < OK) {
+ *err_msg = strdup("No SELinux security context");
+ goto out;
+ }
+
+ if (!selinux_authorize_context(user_context, file_context)) {
+ freecon(user_context);
+ user_context = NULL;
+ *err_msg = strdup("Unauthorized SELinux context");
+ }
+
+out:
+ free(seuser);
+ free(level);
+ freecon(file_context);
+
+ return user_context;
+}
+
+int
+set_job_selinux_context(const user *u, char **envp)
+{
+ char *scontext = NULL;
+ int rc = -1;
+
+ if (u->scontext == NULL) {
+ log_it(u->name, getpid(), "NULL security context for user", "");
+ return -1;
+ }
+
+ scontext = get_job_context(u->name, u->scontext, env_get("MLS_LEVEL", envp));
+ if (!scontext)
+ return -1;
+ if (strcmp(u->scontext, scontext) &&
+ !selinux_authorize_range(u->scontext, scontext)) {
+ char *msg = NULL;
+ if (asprintf(&msg, "Unauthorized range in %s for user range in %s",
+ (char *) scontext, (const char *)u->scontext) >= 0) {
+ log_it(u->name, getpid(), "ERROR", msg);
+ free(msg);
+ }
+ goto out;
+ }
+
+ if (setexeccon(scontext) < 0 || setkeycreatecon(scontext) < 0) {
+ char *msg = NULL;
+ if (asprintf(&msg, "Could not set exec or keycreate context to %s for user",
+ (char *) scontext) >= 0) {
+ log_it(u->name, getpid(), "ERROR", msg);
+ free(msg);
+ }
+ goto out;
+ }
+
+ rc = 0;
+out:
+ freecon(scontext);
+
+ return rc;
+}
+
+int
+set_at_selinux_context(const char *name, int fd, char **err_msg)
+{
+ char *scontext = NULL;
+ int rc = 0;
+
+ scontext = get_selinux_context(name, fd, err_msg);
+ if (scontext == NULL)
+ return -1;
+
+ if (setexeccon(scontext) < 0 || setkeycreatecon(scontext) < 0) {
+ asprintf(err_msg, "Could not set exec or keycreate context to %s",
+ (char *) scontext);
+ rc = -1;
+ }
+
+ freecon(scontext);
+
+ return rc;
+}
+
+int
+selinux_crontab_access_denied(void) {
+ security_class_t passwd_class;
+ access_vector_t bit;
+ char *scontext;
+ int rc = 1;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ if (getprevcon(&scontext)) {
+ perror("Cannot obtain SELinux process context");
+ return security_getenforce() > 0;
+ }
+
+ passwd_class = string_to_security_class("passwd");
+ if (passwd_class <= 0) {
+ fprintf(stderr, "Security class \"passwd\" is not defined in the SELinux policy.\n");
+ goto out;
+ }
+
+ bit = string_to_av_perm(passwd_class, "crontab");
+ if (bit <= 0) {
+ fprintf(stderr, "Failed to translate av perm \"crontab\" for security class \"passwd\".\n");
+ goto out;
+ }
+
+ rc = !selinux_authorize(scontext, scontext, passwd_class, bit);
+
+out:
+ freecon(scontext);
+
+ return rc ? (security_getenforce() > 0) : 0;
+}
+
+int
+write_crontab_mls_level(FILE *crontab) {
+ char *scontext = NULL;
+ context_t ccon = NULL;
+ const char *level = NULL;
+ int rc = -1;
+
+ if (getprevcon(&scontext)) {
+ perror("Cannot obtain SELinux process context");
+ return -1;
+ }
+
+ if (!(ccon = context_new(scontext))) {
+ perror("context_new failed");
+ goto out;
+ }
+
+ if (!(level = context_range_get(ccon))) {
+ perror("context_range failed");
+ goto out;
+ }
+
+ rc = fprintf(crontab, "MLS_LEVEL=%s\n", level);
+ if (rc < 0)
+ perror("Cannot write MLS level");
+
+out:
+ context_free(ccon);
+ freecon(scontext);
+
+ return rc;
+}
+
diff --git a/usr.sbin/cron/structs.h b/usr.sbin/cron/structs.h
index 1b72d79..8ecc5e7 100644
--- a/usr.sbin/cron/structs.h
+++ b/usr.sbin/cron/structs.h
@@ -48,6 +48,9 @@ typedef struct _user {
char *name;
time_t mtime; /* last modtime of crontab */
entry *crontab; /* this person's crontab */
+#ifdef WITH_SELINUX
+ char *scontext; /* SELinux security context */
+#endif /* WITH_SELINUX */
} user;
typedef struct _cron_db {
diff --git a/usr.sbin/cron/user.c b/usr.sbin/cron/user.c
index facc0b1..efa7394 100644
--- a/usr.sbin/cron/user.c
+++ b/usr.sbin/cron/user.c
@@ -39,11 +39,15 @@ free_user(user *u) {
ne = e->next;
free_entry(e);
}
+#ifdef WITH_SELINUX
+ freecon(u->scontext);
+#endif /* WITH_SELINUX */
free(u);
}
user *
-load_user(int crontab_fd, struct passwd *pw, const char *name) {
+load_user(int crontab_fd, struct passwd *pw,
+ const char *name, const char *tabname) {
char envstr[MAX_ENVSTR];
FILE *file;
user *u;
@@ -80,6 +84,24 @@ load_user(int crontab_fd, struct passwd *pw, const char *name) {
return (NULL);
}
+#ifdef WITH_SELINUX
+ u->scontext = NULL;
+ if (is_selinux_enabled() > 0) {
+ char *err_msg = NULL;
+ u->scontext = get_selinux_context(strcmp(name, SYSUSERNAME) ? name : NULL,
+ crontab_fd, &err_msg);
+ if (!u->scontext) {
+ log_it(name, getpid(), err_msg, tabname);
+ free(err_msg);
+ if(security_getenforce() > 0) {
+ free_user(u);
+ u = NULL;
+ goto done;
+ }
+ }
+ }
+#endif /* WITH_SELINUX */
+
/* load the crontab
*/
while ((status = load_env(envstr, file)) >= OK) {