4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 #include <sys/types.h>
31 #include <sys/types.h>
46 #include <security/pam_appl.h>
49 #include "getresponse.h"
53 #define TMPFILE "_cron" /* prefix for tmp file */
54 #define CRMODE 0600 /* mode for creating crontabs */
57 "can't create your crontab file in the crontab directory."
58 #define BADOPEN "can't open your crontab file."
60 "because your login shell isn't /usr/bin/sh, you can't use cron."
61 #define WARNSHELL "warning: commands will be executed using /usr/bin/sh\n"
64 "\tcrontab [file]\n" \
65 "\tcrontab -e [username]\n" \
66 "\tcrontab -l [username]\n" \
67 "\tcrontab -r [username]"
68 #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)."
69 #define NOTALLOWED "you are not authorized to use cron. Sorry."
71 "you must be super-user to access another user's crontab file"
72 #define AUDITREJECT "The audit context for your shell has not been set."
73 #define EOLN "unexpected end of line."
74 #define UNEXPECT "unexpected character found in line."
75 #define OUTOFBOUND "number out of bounds."
76 #define ERRSFND "errors detected in input, no crontab file generated."
78 " The editor indicates that an error occurred while you were\n"\
79 " editing the crontab data - usually a minor typing error.\n\n"
80 #define BADREAD "error reading your crontab file"
82 " Edit again, to ensure crontab information is intact? "
83 #define NAMETOOLONG "login name too long"
84 #define BAD_SHELL "Invalid shell specified: %s"
85 #define BAD_HOME "Unable to access directory: %s\t%s\n"
89 extern int audit_crontab_modify(char *, char *, int);
90 extern int audit_crontab_delete(char *, int);
91 extern int audit_crontab_not_allowed(uid_t
, char *);
98 char line
[CTLINESIZE
];
99 static char login
[UNAMESIZE
];
101 static int next_field(int, int);
102 static void catch(int);
103 static void crabort(char *);
104 static void cerror(char *);
105 static void copycron(FILE *);
108 main(int argc
, char **argv
)
125 char real_login
[UNAMESIZE
];
132 (void) setlocale(LC_ALL
, "");
133 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
134 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
136 (void) textdomain(TEXT_DOMAIN
);
138 if (init_yes() < 0) {
139 (void) fprintf(stderr
, gettext(ERR_MSG_INIT_YES
),
144 while ((c
= getopt(argc
, argv
, "elr")) != EOF
)
160 if (eflag
+ lflag
+ rflag
> 1)
165 if (errflg
|| argc
> 1)
169 if ((pwp
= getpwuid(ruid
)) == NULL
)
170 crabort(INVALIDUSER
);
172 if (strlcpy(real_login
, pwp
->pw_name
, sizeof (real_login
))
173 >= sizeof (real_login
))
174 crabort(NAMETOOLONG
);
176 if ((eflag
|| lflag
|| rflag
) && argc
== 1) {
177 if ((pwp
= getpwnam(*argv
)) == NULL
)
178 crabort(INVALIDUSER
);
180 if (!cron_admin(real_login
)) {
181 if (pwp
->pw_uid
!= ruid
)
195 crabort(INVALIDUSER
);
197 if (strlcpy(login
, pp
, sizeof (login
)) >= sizeof (login
))
198 crabort(NAMETOOLONG
);
199 if (!allowed(login
, CRONALLOW
, CRONDENY
))
202 /* Do account validation check */
203 pam_error
= pam_start("cron", pp
, NULL
, &pamh
);
204 if (pam_error
!= PAM_SUCCESS
) {
205 crabort((char *)pam_strerror(pamh
, pam_error
));
207 pam_error
= pam_acct_mgmt(pamh
, PAM_SILENT
);
208 if (pam_error
!= PAM_SUCCESS
) {
209 (void) fprintf(stderr
, gettext("Warning - Invalid account: "
210 "'%s' not allowed to execute cronjobs\n"), pp
);
212 (void) pam_end(pamh
, PAM_SUCCESS
);
215 /* check for unaudited shell */
216 if (audit_crontab_not_allowed(ruid
, pp
))
217 crabort(AUDITREJECT
);
219 cf
= xmalloc(strlen(CRONDIR
)+strlen(login
)+2);
220 strcat(strcat(strcpy(cf
, CRONDIR
), "/"), login
);
224 cron_sendmsg(DELETE
, login
, login
, CRON
);
225 audit_crontab_delete(cf
, r
);
229 if ((fp
= fopen(cf
, "r")) == NULL
)
231 while (fgets(line
, CTLINESIZE
, fp
) != NULL
)
237 if ((fp
= fopen(cf
, "r")) == NULL
) {
241 (void) strcpy(edtemp
, "/tmp/crontabXXXXXX");
242 tmpfd
= mkstemp(edtemp
);
243 if (fchown(tmpfd
, ruid
, -1) == -1) {
245 crabort("fchown of temporary file failed");
249 * Fork off a child with user's permissions,
250 * to edit the crontab file
252 if ((pid
= fork()) == (pid_t
)-1)
253 crabort("fork failed");
254 if (pid
== 0) { /* child process */
255 /* give up super-user privileges. */
257 if ((tmpfp
= fopen(edtemp
, "w")) == NULL
)
258 crabort("can't create temporary file");
261 * Copy user's crontab file to temporary file.
263 while (fgets(line
, CTLINESIZE
, fp
) != NULL
) {
268 crabort("write error on"
279 if (fclose(tmpfp
) == EOF
)
280 crabort("write error on temporary file");
281 if (stat(edtemp
, &stbuf
) < 0)
282 crabort("can't stat temporary file");
283 omodtime
= stbuf
.st_mtime
;
284 editor
= getenv("EDITOR");
287 buflen
= strlen(editor
) + strlen(edtemp
) + 2;
288 buf
= xmalloc(buflen
);
289 (void) snprintf(buf
, buflen
, "%s %s", editor
, edtemp
);
297 if ((tmpfp
= fopen(edtemp
, "r")) == NULL
)
298 crabort("can't open temporary file");
299 if (fstat(fileno(tmpfp
), &stbuf
) < 0)
300 crabort("can't stat temporary file");
301 if (stbuf
.st_size
== 0)
302 crabort("temporary file empty");
303 if (omodtime
== stbuf
.st_mtime
) {
304 (void) unlink(edtemp
);
305 fprintf(stderr
, gettext(
306 "The crontab file was not"
310 if ((ret
) && (errno
!= EINTR
)) {
312 * Some editors (like 'vi') can return
313 * a non-zero exit status even though
314 * everything is okay. Need to check.
316 fprintf(stderr
, gettext(ED_ERROR
));
318 if (isatty(fileno(stdin
))) {
329 (void) unlink(edtemp
);
334 * Non-interactive, dump changes
336 (void) unlink(edtemp
);
344 /* fix for 1125555 - ignore common signals while waiting */
345 (void) signal(SIGINT
, SIG_IGN
);
346 (void) signal(SIGHUP
, SIG_IGN
);
347 (void) signal(SIGQUIT
, SIG_IGN
);
348 (void) signal(SIGTERM
, SIG_IGN
);
350 if ((stat_loc
& 0xFF00) != 0)
354 * unlink edtemp as 'ruid'. The file contents will be held
355 * since we open the file descriptor 'tmpfp' before calling
358 if (((ret
= seteuid(ruid
)) < 0) ||
359 ((tmpfp
= fopen(edtemp
, "r")) == NULL
) ||
360 (unlink(edtemp
) == -1)) {
361 fprintf(stderr
, "crontab: %s: %s\n",
362 edtemp
, errmsg(errno
));
363 if ((ret
< 0) || (tmpfp
== NULL
))
364 (void) unlink(edtemp
);
373 else if (seteuid(getuid()) != 0 || (fp
= fopen(argv
[0], "r"))
381 cron_sendmsg(ADD
, login
, login
, CRON
);
383 * if (per_errno == 2)
384 * fprintf(stderr, gettext(WARNSHELL));
394 char pid
[6], *tnam_end
;
398 sprintf(pid
, "%-5d", getpid());
399 tnam
= xmalloc(strlen(CRONDIR
)+strlen(TMPFILE
)+7);
400 strcat(strcat(strcat(strcpy(tnam
, CRONDIR
), "/"), TMPFILE
), pid
);
401 /* cut trailing blanks */
402 tnam_end
= strchr(tnam
, ' ');
403 if (tnam_end
!= NULL
)
405 /* catch SIGINT, SIGHUP, SIGQUIT signals */
406 if (signal(SIGINT
, catch) == SIG_IGN
)
407 signal(SIGINT
, SIG_IGN
);
408 if (signal(SIGHUP
, catch) == SIG_IGN
) signal(SIGHUP
, SIG_IGN
);
409 if (signal(SIGQUIT
, catch) == SIG_IGN
) signal(SIGQUIT
, SIG_IGN
);
410 if (signal(SIGTERM
, catch) == SIG_IGN
) signal(SIGTERM
, SIG_IGN
);
411 if ((t
= creat(tnam
, CRMODE
)) == -1) crabort(BADCREATE
);
412 if ((tfp
= fdopen(t
, "w")) == NULL
) {
416 err
= 0; /* if errors found, err set to 1 */
417 while (fgets(line
, CTLINESIZE
, fp
) != NULL
) {
419 while (line
[cursor
] == ' ' || line
[cursor
] == '\t')
421 /* fix for 1039689 - treat blank line like a comment */
422 if (line
[cursor
] == '#' || line
[cursor
] == '\n')
425 if (strncmp(&line
[cursor
], ENV_TZ
, strlen(ENV_TZ
)) == 0) {
428 strncpy(buf
, &line
[cursor
+ strlen(ENV_TZ
)],
430 if ((x
= strchr(buf
, '\n')) != NULL
)
434 } else if (strncmp(&line
[cursor
], ENV_SHELL
,
435 strlen(ENV_SHELL
)) == 0) {
438 strncpy(buf
, &line
[cursor
+ strlen(ENV_SHELL
)],
440 if ((x
= strchr(buf
, '\n')) != NULL
)
443 if (isvalid_shell(buf
)) {
447 fprintf(stderr
, BAD_SHELL
, &line
[cursor
]);
450 } else if (strncmp(&line
[cursor
], ENV_HOME
,
451 strlen(ENV_HOME
)) == 0) {
454 strncpy(buf
, &line
[cursor
+ strlen(ENV_HOME
)],
456 if ((x
= strchr(buf
, '\n')) != NULL
)
458 if (chdir(buf
) == 0) {
462 fprintf(stderr
, BAD_HOME
, &line
[cursor
],
468 if (next_field(0, 59)) continue;
469 if (next_field(0, 23)) continue;
470 if (next_field(1, 31)) continue;
471 if (next_field(1, 12)) continue;
472 if (next_field(0, 06)) continue;
473 if (line
[++cursor
] == '\0') {
478 if (fputs(line
, tfp
) == EOF
) {
486 /* audit differences between old and new crontabs */
487 audit_crontab_modify(cf
, tnam
, err
);
490 /* make file tfp the new crontab */
492 if (link(tnam
, cf
) == -1) {
503 next_field(lower
, upper
)
508 while ((line
[cursor
] == ' ') || (line
[cursor
] == '\t')) cursor
++;
509 if (line
[cursor
] == '\0') {
513 if (line
[cursor
] == '*') {
515 if ((line
[cursor
] != ' ') && (line
[cursor
] != '\t')) {
522 if (!isdigit(line
[cursor
])) {
528 num
= num
*10 + (line
[cursor
]-'0');
529 } while (isdigit(line
[++cursor
]));
530 if ((num
< lower
) || (num
> upper
)) {
534 if (line
[cursor
] == '-') {
535 if (!isdigit(line
[++cursor
])) {
541 num2
= num2
*10 + (line
[cursor
]-'0');
542 } while (isdigit(line
[++cursor
]));
543 if ((num2
< lower
) || (num2
> upper
)) {
548 if ((line
[cursor
] == ' ') || (line
[cursor
] == '\t')) break;
549 if (line
[cursor
] == '\0') {
553 if (line
[cursor
++] != ',') {
565 fprintf(stderr
, gettext("%scrontab: error on previous line; %s\n"),
584 if (strcmp(edtemp
, "") != 0) {
586 (void) unlink(edtemp
);
594 fprintf(stderr
, "crontab: %s\n", gettext(msg
));