This source file includes following definitions.
- cron_conv
- cron_restore_default_security_context
- cron_set_job_security_context
- cron_start_pam
- cron_open_pam_session
- cron_close_pam
- cron_change_groups
- cron_change_user_permanently
- cron_authorize_context
- cron_authorize_range
- cron_get_job_range
- cron_change_selinux_range
- get_security_context
- free_security_context
- crontab_security_access
- build_env
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #include "config.h"
23
24 #include <errno.h>
25 #include <grp.h>
26 #include <pwd.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "cronie_common.h"
33 #include "funcs.h"
34 #include "globals.h"
35 #include "macros.h"
36
37 #ifdef WITH_PAM
38 # include <security/pam_appl.h>
39 #endif
40
41 #ifdef WITH_SELINUX
42 # include <selinux/selinux.h>
43 # include <selinux/context.h>
44 # include <selinux/get_context_list.h>
45 #endif
46
47 #ifdef WITH_AUDIT
48 # include <libaudit.h>
49 #endif
50
51 #ifdef WITH_PAM
52 static pam_handle_t *pamh = NULL;
53 static int pam_session_opened = 0;
54
55 static int
56 cron_conv(int num_msg, const struct pam_message **msgm,
57 struct pam_response **response ATTRIBUTE_UNUSED,
58 void *appdata_ptr ATTRIBUTE_UNUSED)
59 {
60 int i;
61
62 for (i = 0; i < num_msg; i++) {
63 switch (msgm[i]->msg_style) {
64 case PAM_ERROR_MSG:
65 case PAM_TEXT_INFO:
66 if (msgm[i]->msg != NULL) {
67 log_it("CRON", getpid(), "pam_message", msgm[i]->msg, 0);
68 }
69 break;
70 default:
71 break;
72 }
73 }
74 return (0);
75 }
76
77 static const struct pam_conv conv = {
78 cron_conv, NULL
79 };
80
81 static int cron_open_pam_session(struct passwd *pw);
82
83 # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
84 log_it(pw->pw_name, getpid(), "PAM ERROR", pam_strerror(pamh, retcode), 0); \
85 if (pamh != NULL) { \
86 if (pam_session_opened != 0) \
87 pam_close_session(pamh, PAM_SILENT); \
88 pam_end(pamh, retcode); \
89 pamh = NULL; \
90 } \
91 return(retcode); }
92 #endif
93
94 static char **build_env(char **cronenv);
95
96 #ifdef WITH_SELINUX
97 static int cron_change_selinux_range(user * u, security_context_t ucontext);
98 static int cron_get_job_range(user * u, security_context_t * ucontextp,
99 char **jobenv);
100 #endif
101
102 void cron_restore_default_security_context(void) {
103 #ifdef WITH_SELINUX
104 if (is_selinux_enabled() <= 0)
105 return;
106 if (setexeccon(NULL) < 0)
107 log_it("CRON", getpid(), "ERROR",
108 "failed to restore SELinux context", 0);
109 #endif
110 }
111
112 int cron_set_job_security_context(entry *e, user *u ATTRIBUTE_UNUSED,
113 char ***jobenv) {
114 time_t minutely_time = 0;
115 #ifdef WITH_PAM
116 int ret;
117 #endif
118
119 if ((e->flags & MIN_STAR) == MIN_STAR) {
120
121
122
123 minutely_time = time(0);
124 Debug(DSCH, ("Minute-ly job. Recording time %lu\n", minutely_time));
125 }
126
127 #ifdef WITH_PAM
128
129 if ((!u->system || e->pwd->pw_uid != 0) && (ret = cron_start_pam(e->pwd)) != 0) {
130 log_it(e->pwd->pw_name, getpid(), "FAILED to authorize user with PAM",
131 pam_strerror(pamh, ret), 0);
132 return -1;
133 }
134 #endif
135
136 #ifdef WITH_SELINUX
137
138
139
140 security_context_t ucontext = 0;
141
142 if (cron_get_job_range(u, &ucontext, e->envp) < OK) {
143 log_it(e->pwd->pw_name, getpid(), "ERROR",
144 "failed to get SELinux context", 0);
145 return -1;
146 }
147
148 if (cron_change_selinux_range(u, ucontext) != 0) {
149 log_it(e->pwd->pw_name, getpid(), "ERROR",
150 "failed to change SELinux context", 0);
151 if (ucontext)
152 freecon(ucontext);
153 return -1;
154 }
155 if (ucontext)
156 freecon(ucontext);
157 #endif
158 #ifdef WITH_PAM
159 if (pamh != NULL && (ret = cron_open_pam_session(e->pwd)) != 0) {
160 log_it(e->pwd->pw_name, getpid(),
161 "FAILED to open PAM security session", pam_strerror(pamh, ret), 0);
162 return -1;
163 }
164 #endif
165
166 if (cron_change_groups(e->pwd) != 0) {
167 return -1;
168 }
169
170 *jobenv = build_env(e->envp);
171
172 time_t job_run_time = time(0L);
173
174 if ((minutely_time > 0) && ((job_run_time / 60) != (minutely_time / 60))) {
175
176
177
178 struct tm tmS, tmN;
179 char buf[256];
180
181 localtime_r(&job_run_time, &tmN);
182 localtime_r(&minutely_time, &tmS);
183
184 snprintf(buf, sizeof (buf),
185 "Job execution of per-minute job scheduled for "
186 "%.2u:%.2u delayed into subsequent minute %.2u:%.2u. Skipping job run.",
187 tmS.tm_hour, tmS.tm_min, tmN.tm_hour, tmN.tm_min);
188 log_it(e->pwd->pw_name, getpid(), "INFO", buf, 0);
189 return -1;
190 }
191 return 0;
192 }
193
194 #if defined(WITH_PAM)
195 int cron_start_pam(struct passwd *pw) {
196 int retcode = 0;
197
198 retcode = pam_start("crond", pw->pw_name, &conv, &pamh);
199 PAM_FAIL_CHECK;
200 retcode = pam_set_item(pamh, PAM_TTY, "cron");
201 PAM_FAIL_CHECK;
202 retcode = pam_acct_mgmt(pamh, PAM_SILENT);
203 PAM_FAIL_CHECK;
204 retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT);
205 PAM_FAIL_CHECK;
206
207 return retcode;
208 }
209 #endif
210
211 #if defined(WITH_PAM)
212 static int cron_open_pam_session(struct passwd *pw) {
213 int retcode;
214
215 retcode = pam_open_session(pamh, PAM_SILENT);
216 PAM_FAIL_CHECK;
217 if (retcode == PAM_SUCCESS)
218 pam_session_opened = 1;
219
220 return retcode;
221 }
222 #endif
223
224 void cron_close_pam(void) {
225 #if defined(WITH_PAM)
226 if (pam_session_opened != 0) {
227 pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
228 pam_close_session(pamh, PAM_SILENT);
229 }
230 if (pamh != NULL) {
231 pam_end(pamh, PAM_SUCCESS);
232 pamh = NULL;
233 }
234 #endif
235 }
236
237 int cron_change_groups(struct passwd *pw) {
238 pid_t pid = getpid();
239
240 if (setgid(pw->pw_gid) != 0) {
241 log_it("CRON", pid, "ERROR", "setgid failed", errno);
242 return -1;
243 }
244
245 if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
246 log_it("CRON", pid, "ERROR", "initgroups failed", errno);
247 return -1;
248 }
249
250 #if defined(WITH_PAM)
251
252
253 if (pamh != NULL) {
254 pam_setcred(pamh, PAM_REINITIALIZE_CRED | PAM_SILENT);
255 }
256 #endif
257
258 return 0;
259 }
260
261 int cron_change_user_permanently(struct passwd *pw, char *homedir) {
262 if (setreuid(pw->pw_uid, pw->pw_uid) != 0) {
263 log_it("CRON", getpid(), "ERROR", "setreuid failed", errno);
264 return -1;
265 }
266
267 if (chdir(homedir) == -1) {
268 log_it("CRON", getpid(), "ERROR chdir failed", homedir, errno);
269 return -1;
270 }
271
272 log_close();
273
274 return 0;
275 }
276
277
278 #ifdef WITH_SELINUX
279 static int cron_authorize_context(security_context_t scontext,
280 security_context_t file_context) {
281 struct av_decision avd;
282 int retval;
283 security_class_t tclass;
284 access_vector_t bit;
285
286 tclass = string_to_security_class("file");
287 if (!tclass) {
288 log_it("CRON", getpid(), "ERROR", "Failed to translate security class file", errno);
289 return 0;
290 }
291 bit = string_to_av_perm(tclass, "entrypoint");
292 if (!bit) {
293 log_it("CRON", getpid(), "ERROR", "Failed to translate av perm entrypoint", errno);
294 return 0;
295 }
296
297
298
299
300
301
302
303 retval = security_compute_av(scontext, file_context,
304 tclass, bit, &avd);
305 if (retval || ((bit & avd.allowed) != bit))
306 return 0;
307 return 1;
308 }
309 #endif
310
311 #ifdef WITH_SELINUX
312 static int cron_authorize_range(security_context_t scontext,
313 security_context_t ucontext) {
314 struct av_decision avd;
315 int retval;
316 security_class_t tclass;
317 access_vector_t bit;
318
319 tclass = string_to_security_class("context");
320 if (!tclass) {
321 log_it("CRON", getpid(), "ERROR", "Failed to translate security class context", errno);
322 return 0;
323 }
324 bit = string_to_av_perm(tclass, "contains");
325 if (!bit) {
326 log_it("CRON", getpid(), "ERROR", "Failed to translate av perm contains", errno);
327 return 0;
328 }
329
330
331
332
333
334
335 retval = security_compute_av(scontext, ucontext,
336 tclass, bit, &avd);
337
338 if (retval || ((bit & avd.allowed) != bit))
339 return 0;
340 return 1;
341 }
342 #endif
343
344 #if WITH_SELINUX
345
346
347 static int
348 cron_get_job_range(user * u, security_context_t * ucontextp, char **jobenv) {
349 char *range;
350
351 if (is_selinux_enabled() <= 0)
352 return 0;
353 if (ucontextp == 0L)
354 return -1;
355
356 *ucontextp = 0L;
357
358 if ((range = env_get("MLS_LEVEL", jobenv)) != 0L) {
359 context_t ccon;
360 if (!(ccon = context_new(u->scontext))) {
361 log_it(u->name, getpid(), "context_new FAILED for MLS_LEVEL",
362 range, 0);
363 context_free(ccon);
364 return -1;
365 }
366
367 if (context_range_set(ccon, range)) {
368 log_it(u->name, getpid(),
369 "context_range_set FAILED for MLS_LEVEL", range, 0);
370 context_free(ccon);
371 return -1;
372 }
373
374 if (!(*ucontextp = context_str(ccon))) {
375 log_it(u->name, getpid(), "context_str FAILED for MLS_LEVEL",
376 range, 0);
377 context_free(ccon);
378 return -1;
379 }
380
381 if (!(*ucontextp = strdup(*ucontextp))) {
382 log_it(u->name, getpid(), "strdup FAILED for MLS_LEVEL", range, 0);
383 return -1;
384 }
385 context_free(ccon);
386 }
387 else if (!u->scontext) {
388
389 return 0;
390 }
391 else if (!(*ucontextp = strdup(u->scontext))) {
392 log_it(u->name, getpid(), "strdup FAILED for MLS_LEVEL", range, 0);
393 return -1;
394 }
395
396 return 0;
397 }
398 #endif
399
400 #ifdef WITH_SELINUX
401 static int cron_change_selinux_range(user * u, security_context_t ucontext) {
402 char *msg = NULL;
403
404 if (is_selinux_enabled() <= 0)
405 return 0;
406
407 if (u->scontext == 0L) {
408 if (security_getenforce() > 0) {
409 log_it(u->name, getpid(), "NULL security context for user", "", 0);
410 return -1;
411 }
412 else {
413 log_it(u->name, getpid(),
414 "NULL security context for user, "
415 "but SELinux in permissive mode, continuing", "", 0);
416 return 0;
417 }
418 }
419
420 if (!ucontext || strcmp(u->scontext, ucontext)) {
421 if (!cron_authorize_range(u->scontext, ucontext)) {
422 if (security_getenforce() > 0) {
423 # ifdef WITH_AUDIT
424 if (asprintf(&msg,
425 "cron: Unauthorized MLS range acct=%s new_scontext=%s old_scontext=%s",
426 u->name, (char *) ucontext, u->scontext) >= 0) {
427 int audit_fd = audit_open();
428 audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
429 msg, NULL, NULL, NULL, 0);
430 close(audit_fd);
431 free(msg);
432 }
433 # endif
434 if (asprintf
435 (&msg, "Unauthorized range in %s for user range in %s",
436 (char *) ucontext, u->scontext) >= 0) {
437 log_it(u->name, getpid(), "ERROR", msg, 0);
438 free(msg);
439 }
440 return -1;
441 }
442 else {
443 if (asprintf
444 (&msg,
445 "Unauthorized range in %s for user range in %s,"
446 " but SELinux in permissive mod, continuing",
447 (char *) ucontext, u->scontext) >= 0) {
448 log_it(u->name, getpid(), "WARNING", msg, 0);
449 free(msg);
450 }
451 }
452 }
453 }
454
455 if (setexeccon(ucontext) < 0) {
456 if (security_getenforce() > 0) {
457 if (asprintf
458 (&msg, "Could not set exec or keycreate context to %s for user",
459 (char *) ucontext) >= 0) {
460 log_it(u->name, getpid(), "ERROR", msg, 0);
461 free(msg);
462 }
463 return -1;
464 }
465 else {
466 if (asprintf
467 (&msg,
468 "Could not set exec context to %s for user,"
469 " but SELinux in permissive mode, continuing",
470 (char *) ucontext) >= 0) {
471 log_it(u->name, getpid(), "WARNING", msg, 0);
472 free(msg);
473 }
474 return 0;
475 }
476 }
477 return 0;
478 }
479 #endif
480
481 #ifdef WITH_SELINUX
482 int
483 get_security_context(const char *name, int crontab_fd,
484 security_context_t * rcontext, const char *tabname) {
485 security_context_t scontext = NULL;
486 security_context_t file_context = NULL;
487 security_context_t rawcontext=NULL;
488 context_t current_context = NULL;
489 int retval;
490 char *current_context_str = NULL;
491 char *seuser = NULL;
492 char *level = NULL;
493
494 *rcontext = NULL;
495
496 if (is_selinux_enabled() <= 0)
497 return 0;
498
499 if (name != NULL) {
500 if (getseuserbyname(name, &seuser, &level) < 0) {
501 log_it(name, getpid(), "getseuserbyname FAILED", name, 0);
502 return security_getenforce() > 0 ? -1 : 0;
503 }
504
505 retval = get_default_context_with_level(seuser, level, NULL, &scontext);
506 }
507 else {
508 const char *current_user, *current_role;
509 if (getcon(¤t_context_str) < 0) {
510 log_it(name, getpid(), "getcon FAILED", "", 0);
511 return (security_getenforce() > 0);
512 }
513
514 current_context = context_new(current_context_str);
515 if (current_context == NULL) {
516 log_it(name, getpid(), "context_new FAILED", current_context_str, 0);
517 freecon(current_context_str);
518 return (security_getenforce() > 0);
519 }
520
521 current_user = context_user_get(current_context);
522 current_role = context_role_get(current_context);
523 retval = get_default_context_with_rolelevel(current_user, current_role, level, NULL, &scontext);
524
525 freecon(current_context_str);
526 context_free(current_context);
527 }
528
529 if (selinux_trans_to_raw_context(scontext, &rawcontext) == 0) {
530 freecon(scontext);
531 scontext = rawcontext;
532 }
533 free(seuser);
534 free(level);
535 if (retval) {
536 if (security_getenforce() > 0) {
537 log_it(name, getpid(), "No SELinux security context", tabname, 0);
538 return -1;
539 }
540 else {
541 log_it(name, getpid(),
542 "No security context but SELinux in permissive mode, continuing",
543 tabname, 0);
544 return 0;
545 }
546 }
547
548 if (fgetfilecon(crontab_fd, &file_context) < OK) {
549 if (security_getenforce() > 0) {
550 log_it(name, getpid(), "getfilecon FAILED", tabname, 0);
551 freecon(scontext);
552 return -1;
553 }
554 else {
555 log_it(name, getpid(),
556 "getfilecon FAILED but SELinux in permissive mode, continuing",
557 tabname, 0);
558 *rcontext = scontext;
559 return 0;
560 }
561 }
562
563 if (!cron_authorize_context(scontext, file_context)) {
564 char *msg=NULL;
565 if (asprintf(&msg,
566 "Unauthorized SELinux context=%s file_context=%s", (char *) scontext, file_context) >= 0) {
567 log_it(name, getpid(), msg, tabname, 0);
568 free(msg);
569 } else {
570 log_it(name, getpid(), "Unauthorized SELinux context", tabname, 0);
571 }
572 freecon(scontext);
573 freecon(file_context);
574 if (security_getenforce() > 0) {
575 return -1;
576 }
577 else {
578 log_it(name, getpid(),
579 "SELinux in permissive mode, continuing",
580 tabname, 0);
581 return 0;
582 }
583 }
584 freecon(file_context);
585
586 *rcontext = scontext;
587 return 0;
588 }
589 #endif
590
591 #ifdef WITH_SELINUX
592 void free_security_context(security_context_t * scontext) {
593 if (*scontext != NULL) {
594 freecon(*scontext);
595 *scontext = 0L;
596 }
597 }
598 #endif
599
600 #ifdef WITH_SELINUX
601 int crontab_security_access(void) {
602 int selinux_check_passwd_access = -1;
603 if (is_selinux_enabled() > 0) {
604 security_context_t user_context;
605 if (getprevcon_raw(&user_context) == 0) {
606 security_class_t passwd_class;
607 access_vector_t crontab_bit;
608 struct av_decision avd;
609 int retval = 0;
610
611 passwd_class = string_to_security_class("passwd");
612 if (passwd_class == 0) {
613 fprintf(stderr, "Security class \"passwd\" is not defined in the SELinux policy.\n");
614 retval = -1;
615 }
616
617 if (retval == 0) {
618 crontab_bit = string_to_av_perm(passwd_class, "crontab");
619 if (crontab_bit == 0) {
620 fprintf(stderr, "Security av permission \"crontab\" is not defined in the SELinux policy.\n");
621 retval = -1;
622 }
623 }
624
625 if (retval == 0)
626 retval = security_compute_av_raw(user_context,
627 user_context, passwd_class,
628 crontab_bit, &avd);
629
630 if ((retval == 0) && ((crontab_bit & avd.allowed) == crontab_bit)) {
631 selinux_check_passwd_access = 0;
632 }
633 freecon(user_context);
634 }
635
636 if (selinux_check_passwd_access != 0 && security_getenforce() == 0)
637 selinux_check_passwd_access = 0;
638
639 return selinux_check_passwd_access;
640 }
641 return 0;
642 }
643 #endif
644
645
646
647
648 static char **build_env(char **cronenv) {
649 char **jobenv;
650 #ifdef WITH_PAM
651 char *cronvar;
652 int count = 0;
653
654 if (pamh == NULL || (jobenv=pam_getenvlist(pamh)) == NULL) {
655 #endif
656 jobenv = env_copy(cronenv);
657 if (jobenv == NULL)
658 log_it("CRON", getpid(),
659 "ERROR", "Initialization of cron environment variables failed", 0);
660 return jobenv;
661 #ifdef WITH_PAM
662 }
663
664
665
666
667
668 while ((cronvar = cronenv[count++])) {
669 if (!(jobenv = env_set(jobenv, cronvar))) {
670 log_it("CRON", getpid(),
671 "Setting Cron environment variable failed", cronvar, 0);
672 return NULL;
673 }
674 }
675 return jobenv;
676 #endif
677 }