X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fcrontab.c;h=4787fa08f5dee66b84fac6e2eece54e3ed5cfac2;hb=4c9455f967e21d30db0de2e13b6e1115ab8f36ce;hp=a1fe2c5a5a6cf268a6fe42c7fa357f20a27b50ad;hpb=7fc294cdfe1e7f4a12c44f984a698b0c0f609075;p=oweals%2Fbusybox.git diff --git a/miscutils/crontab.c b/miscutils/crontab.c index a1fe2c5a5..4787fa08f 100644 --- a/miscutils/crontab.c +++ b/miscutils/crontab.c @@ -7,95 +7,69 @@ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) * Vladimir Oleynik (C) 2002 * - * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ +//config:config CRONTAB +//config: bool "crontab (9.7 kb)" +//config: default y +//config: help +//config: Crontab manipulates the crontab for a particular user. Only +//config: the superuser may specify a different user and/or crontab directory. +//config: Note that busybox binary must be setuid root for this applet to +//config: work properly. + +/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */ +//applet:IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) + +//kbuild:lib-$(CONFIG_CRONTAB) += crontab.o + +//usage:#define crontab_trivial_usage +//usage: "[-c DIR] [-u USER] [-ler]|[FILE]" +//usage:#define crontab_full_usage "\n\n" +//usage: " -c Crontab directory" +//usage: "\n -u User" +//usage: "\n -l List crontab" +//usage: "\n -e Edit crontab" +//usage: "\n -r Delete crontab" +//usage: "\n FILE Replace crontab by FILE ('-': stdin)" #include "libbb.h" -#ifndef CRONTABS -#define CRONTABS "/var/spool/cron/crontabs" -#endif +#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" #ifndef CRONUPDATE #define CRONUPDATE "cron.update" #endif -#ifndef PATH_VI -#define PATH_VI "/bin/vi" /* location of vi */ -#endif - -static void change_user(const struct passwd *pas) -{ - setenv("USER", pas->pw_name, 1); - setenv("HOME", pas->pw_dir, 1); - setenv("SHELL", DEFAULT_SHELL, 1); - - /* initgroups, setgid, setuid */ - change_identity(pas); - - if (chdir(pas->pw_dir) < 0) { - bb_perror_msg("chdir(%s) by %s failed", - pas->pw_dir, pas->pw_name); - xchdir("/tmp"); - } -} static void edit_file(const struct passwd *pas, const char *file) { const char *ptr; - int pid = vfork(); + pid_t pid; - if (pid < 0) /* failure */ - bb_perror_msg_and_die("vfork"); + pid = xvfork(); if (pid) { /* parent */ wait4pid(pid); return; } /* CHILD - change user and run editor */ - change_user(pas); + /* initgroups, setgid, setuid */ + change_identity(pas); + setup_environment(pas->pw_shell, + SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP, + pas); ptr = getenv("VISUAL"); if (!ptr) { ptr = getenv("EDITOR"); if (!ptr) - ptr = PATH_VI; + ptr = "vi"; } BB_EXECLP(ptr, ptr, file, NULL); - bb_perror_msg_and_die("exec %s", ptr); -} - -static int open_as_user(const struct passwd *pas, const char *file) -{ - struct fd_pair filedes; - pid_t pid; - char c; - - xpiped_pair(filedes); - pid = vfork(); - if (pid < 0) /* ERROR */ - bb_perror_msg_and_die("vfork"); - if (pid) { /* PARENT */ - int n = safe_read(filedes.rd, &c, 1); - close(filedes.rd); - close(filedes.wr); - if (n > 0) /* child says it can read */ - return open(file, O_RDONLY); - return -1; - } - - /* CHILD */ - - /* initgroups, setgid, setuid */ - change_identity(pas); - - /* We just try to read one byte. If that works, file is readable - * under this user. We signal that by sending one byte to parent. */ - if (safe_read(xopen(file, O_RDONLY), &c, 1) == 1) - safe_write(filedes.wr, &c, 1); /* "papa, I can read!" */ - _exit(0); + bb_perror_msg_and_die("can't execute '%s'", ptr); } int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int crontab_main(int argc, char **argv) +int crontab_main(int argc UNUSED_PARAM, char **argv) { const struct passwd *pas; const char *crontab_dir = CRONTABS; @@ -103,8 +77,8 @@ int crontab_main(int argc, char **argv) char *new_fname; char *user_name; /* -u USER */ int fd; + int src_fd; int opt_ler; - uid_t my_uid; /* file [opts] Replace crontab from file * - [opts] Replace crontab from stdin @@ -125,33 +99,24 @@ int crontab_main(int argc, char **argv) OPT_ler = OPT_l + OPT_e + OPT_r, }; - my_uid = getuid(); - - opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ - opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); + opt_ler = getopt32(argv, "^" "u:c:lerd" "\0" "?1:dr"/*max one arg; -d implies -r*/, + &user_name, &crontab_dir + ); argv += optind; - if (my_uid != geteuid()) { /* run by non-root? */ + if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ + /* Run by non-root */ if (opt_ler & (OPT_u|OPT_c)) - bb_error_msg_and_die("only root can use -c or -u"); - /* Clear dangerous stuff, set PATH */ - sanitize_env_for_suid(); + bb_error_msg_and_die(bb_msg_you_must_be_root); } if (opt_ler & OPT_u) { - pas = getpwnam(user_name); - if (!pas) - bb_error_msg_and_die("user %s is not known", user_name); - my_uid = pas->pw_uid; + pas = xgetpwnam(user_name); } else { - pas = getpwuid(my_uid); - if (!pas) - bb_perror_msg_and_die("no user record for UID %u", - (unsigned)my_uid); + pas = xgetpwuid(getuid()); } #define user_name DONT_USE_ME_BEYOND_THIS_POINT -#define my_uid DONT_USE_ME_BEYOND_THIS_POINT /* From now on, keep only -l, -e, -r bits */ opt_ler &= OPT_ler; @@ -159,15 +124,12 @@ int crontab_main(int argc, char **argv) bb_show_usage(); /* Read replacement file under user's UID/GID/group vector */ + src_fd = STDIN_FILENO; if (!opt_ler) { /* Replace? */ if (!argv[0]) bb_show_usage(); if (NOT_LONE_DASH(argv[0])) { - fd = open_as_user(pas, argv[0]); - if (fd < 0) - bb_error_msg_and_die("user %s cannot read %s", - pas->pw_name, argv[0]); - xmove_fd(fd, STDIN_FILENO); + src_fd = xopen_as_uid_gid(argv[0], O_RDONLY, pas->pw_uid, pas->pw_gid); } } @@ -180,7 +142,7 @@ int crontab_main(int argc, char **argv) switch (opt_ler) { default: /* case OPT_r: Delete */ - remove(pas->pw_name); + unlink(pas->pw_name); break; case OPT_l: /* List */ @@ -193,40 +155,41 @@ int crontab_main(int argc, char **argv) case OPT_e: /* Edit */ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid()); - fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600); - xmove_fd(fd, STDIN_FILENO); + /* No O_EXCL: we don't want to be stuck if earlier crontabs + * were killed, leaving stale temp file behind */ + src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); + fchown(src_fd, pas->pw_uid, pas->pw_gid); fd = open(pas->pw_name, O_RDONLY); if (fd >= 0) { - bb_copyfd_eof(fd, STDIN_FILENO); + bb_copyfd_eof(fd, src_fd); close(fd); + xlseek(src_fd, 0, SEEK_SET); } - fchown(STDIN_FILENO, pas->pw_uid, pas->pw_gid); + close_on_exec_on(src_fd); /* don't want editor to see this fd */ edit_file(pas, tmp_fname); - xlseek(STDIN_FILENO, 0, SEEK_SET); /* fall through */ case 0: /* Replace (no -l, -e, or -r were given) */ new_fname = xasprintf("%s.new", pas->pw_name); fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600); if (fd >= 0) { - bb_copyfd_eof(STDIN_FILENO, fd); + bb_copyfd_eof(src_fd, fd); close(fd); - rename(new_fname, pas->pw_name); + xrename(new_fname, pas->pw_name); } else { - bb_error_msg("cannot create %s/%s", + bb_error_msg("can't create %s/%s", crontab_dir, new_fname); } if (tmp_fname) - remove(tmp_fname); + unlink(tmp_fname); /*free(tmp_fname);*/ /*free(new_fname);*/ - } /* switch */ /* Bump notification file. Handle window where crond picks file up * before we can write our entry out. */ - while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND)) >= 0) { + while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) { struct stat st; fdprintf(fd, "%s\n", pas->pw_name); @@ -240,7 +203,7 @@ int crontab_main(int argc, char **argv) /* loop */ } if (fd < 0) { - bb_error_msg("cannot append to %s/%s", + bb_error_msg("can't append to %s/%s", crontab_dir, CRONUPDATE); } return 0;