From aa74292e24a20f8f3f8ab49bc338233b2bc7542f Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Jul 01 2016 08:43:48 +0000 Subject: new upstream release - properly lock the lock files to be able to safely remove stale ones --- diff --git a/.gitignore b/.gitignore index f2cb27c..89de2f8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ at_3.1.12.orig.tar.gz /at_3.1.14.orig.tar.gz /at_3.1.16.orig.tar.gz /at_3.1.18.orig.tar.gz +/at_3.1.20.orig.tar.gz diff --git a/at-3.1.14-shell.patch b/at-3.1.14-shell.patch deleted file mode 100644 index 49f66f1..0000000 --- a/at-3.1.14-shell.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff -up at-3.1.14/at.c.shell at-3.1.14/at.c ---- at-3.1.14/at.c.shell 2014-01-06 17:58:17.555564746 +0100 -+++ at-3.1.14/at.c 2014-01-06 17:59:17.699720002 +0100 -@@ -62,11 +62,8 @@ - #include - #include - --#ifdef TM_IN_SYS_TIME - #include --#else - #include --#endif - - #ifdef HAVE_UNISTD_H - #include -@@ -239,6 +236,12 @@ writefile(time_t runtimer, char queue) - int kill_errno; - int rc; - int mailsize = 128; -+ struct timeval tv; -+ struct timezone tz; -+ long int i; -+ -+ gettimeofday(&tv, &tz); -+ srandom(getpid()+tv.tv_usec); - - /* Install the signal handler for SIGINT; terminate after removing the - * spool file if necessary -@@ -449,6 +452,9 @@ writefile(time_t runtimer, char queue) - fprintf(fp, " || {\n\t echo 'Execution directory " - "inaccessible' >&2\n\t exit 1\n}\n"); - -+ i = random(); -+ fprintf(fp, "${SHELL:-/bin/sh} << \'marcinDELIMITER%08lx\'\n", i); -+ - istty = isatty(fileno(stdin)); - if (istty) { - fprintf(stderr, "at> "); -@@ -464,7 +470,7 @@ writefile(time_t runtimer, char queue) - if (istty) { - fprintf(stderr, "\n"); - } -- fprintf(fp, "\n"); -+ fprintf(fp, "marcinDELIMITER%08lx\n", i); - if (ferror(fp)) - panic("Output error"); - diff --git a/at-3.1.18-pam.patch b/at-3.1.18-pam.patch deleted file mode 100644 index 7658767..0000000 --- a/at-3.1.18-pam.patch +++ /dev/null @@ -1,436 +0,0 @@ -diff -up at-3.1.18/at.c.pam at-3.1.18/at.c ---- at-3.1.18/at.c.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/at.c 2016-03-23 12:40:10.694447117 +0100 -@@ -144,18 +144,13 @@ sigc(int signo) - /* If the user presses ^C, remove the spool file and exit - */ - if (fcreated) { -- /* - PRIV_START -- -+ /* - We need the unprivileged uid here since the file is owned by the real - (not effective) uid. - */ -- setregid(real_gid, effective_gid); -- unlink(atfile); -- setregid(effective_gid, real_gid); -- /* -+ unlink(atfile); - PRIV_END -- */ - } - exit(EXIT_FAILURE); - } -@@ -315,26 +310,19 @@ writefile(time_t runtimer, char queue) - * bit. Yes, this is a kluge. - */ - cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); -- seteuid(real_uid); -+ if ((seteuid(effective_uid)) < 0) -+ perr("Error in seteuid: %s", errno); - if ((fd = open(atfile, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, S_IRUSR)) == -1) - perr("Cannot create atjob file %.500s", atfile); -- seteuid(effective_uid); - - if ((fd2 = dup(fd)) < 0) - perr("Error in dup() of job file"); - -- /* - if (fchown(fd2, real_uid, real_gid) != 0) -- perr("Cannot give away file"); -- */ -+ perr("Cannot give real_uid and real_gid the file"); - - PRIV_END - -- /* We no longer need suid root; now we just need to be able to write -- * to the directory, if necessary. -- */ -- -- REDUCE_PRIV(daemon_uid, daemon_gid) - /* We've successfully created the file; let's set the flag so it - * gets removed in case of an interrupt or error. - */ -@@ -670,7 +658,7 @@ process_jobs(int argc, char **argv, int - We need the unprivileged uid here since the file is owned by the real - (not effective) uid. - */ -- setregid(real_gid, effective_gid); -+ PRIV_START - - if (queue == '=') { - fprintf(stderr, "Warning: deleting running job\n"); -@@ -679,8 +667,8 @@ process_jobs(int argc, char **argv, int - perr("Cannot unlink %.500s", dirent->d_name); - rc = EXIT_FAILURE; - } -+ PRIV_END - -- setregid(effective_gid, real_gid); - done = 1; - - break; -@@ -690,7 +678,7 @@ process_jobs(int argc, char **argv, int - FILE *fp; - int ch; - -- setregid(real_gid, effective_gid); -+ PRIV_START - fp = fopen(dirent->d_name, "r"); - - if (fp) { -@@ -703,7 +691,7 @@ process_jobs(int argc, char **argv, int - perr("Cannot open %.500s", dirent->d_name); - rc = EXIT_FAILURE; - } -- setregid(effective_gid, real_gid); -+ PRIV_END - } - break; - -diff -up at-3.1.18/atd.c.pam at-3.1.18/atd.c ---- at-3.1.18/atd.c.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/atd.c 2016-03-23 12:43:31.990906478 +0100 -@@ -91,6 +91,10 @@ int selinux_enabled = 0; - - /* Macros */ - -+#ifndef LOG_ATD -+#define LOG_ATD LOG_DAEMON -+#endif -+ - #define BATCH_INTERVAL_DEFAULT 60 - #define CHECK_INTERVAL 3600 - -@@ -114,7 +118,7 @@ static int run_as_daemon = 0; - - static volatile sig_atomic_t term_signal = 0; - --#ifdef HAVE_PAM -+#ifdef WITH_PAM - #include - - static pam_handle_t *pamh = NULL; -@@ -123,15 +127,7 @@ static const struct pam_conv conv = { - NULL - }; - --#define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ -- fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \ -- syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ -- pam_end(pamh, retcode); exit(1); \ -- } --#define PAM_END { retcode = pam_close_session(pamh,0); \ -- pam_end(pamh,retcode); } -- --#endif /* HAVE_PAM */ -+#endif /* WITH_PAM */ - - /* Signal handlers */ - RETSIGTYPE -@@ -292,7 +288,7 @@ run_file(const char *filename, uid_t uid - char fmt[64]; - unsigned long jobno; - int rc; --#ifdef HAVE_PAM -+#ifdef WITH_PAM - int retcode; - #endif - -@@ -449,17 +445,11 @@ run_file(const char *filename, uid_t uid - fstat(fd_out, &buf); - size = buf.st_size; - --#ifdef HAVE_PAM -- PRIV_START -- retcode = pam_start("atd", pentry->pw_name, &conv, &pamh); -- PAM_FAIL_CHECK; -- retcode = pam_acct_mgmt(pamh, PAM_SILENT); -- PAM_FAIL_CHECK; -- retcode = pam_open_session(pamh, PAM_SILENT); -- PAM_FAIL_CHECK; -- retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); -- PAM_FAIL_CHECK; -- PRIV_END -+#ifdef WITH_PAM -+ AT_START_PAM; -+ AT_OPEN_PAM_SESSION; -+ closelog(); -+ openlog("atd", LOG_PID, LOG_ATD); - #endif - - close(STDIN_FILENO); -@@ -473,7 +463,14 @@ run_file(const char *filename, uid_t uid - else if (pid == 0) { - char *nul = NULL; - char **nenvp = &nul; -+ char **pam_envp=0L; - -+ PRIV_START -+#ifdef WITH_PAM -+ pam_envp = pam_getenvlist(pamh); -+ if ( ( pam_envp != 0L ) && (pam_envp[0] != 0L) ) -+ nenvp = pam_envp; -+#endif - /* Set up things for the child; we want standard input from the - * input file, and standard output and error sent to our output file. - */ -@@ -492,8 +489,6 @@ run_file(const char *filename, uid_t uid - close(fd_in); - close(fd_out); - -- PRIV_START -- - nice((tolower((int) queue) - 'a' + 1) * 2); - - #ifdef WITH_SELINUX -@@ -514,9 +509,9 @@ run_file(const char *filename, uid_t uid - - chdir("/"); - -- if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) -- perr("Exec failed for /bin/sh"); -- -+ execle("/bin/sh", "sh", (char *) NULL, nenvp); -+ perr("Exec failed for /bin/sh"); -+ /* perr exits, the PRIV_END is just for nice form */ - PRIV_END - } - /* We're the parent. Let's wait. -@@ -529,14 +524,6 @@ run_file(const char *filename, uid_t uid - */ - waitpid(pid, (int *) NULL, 0); - --#ifdef HAVE_PAM -- PRIV_START -- pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); -- retcode = pam_close_session(pamh, PAM_SILENT); -- pam_end(pamh, retcode); -- PRIV_END --#endif -- - /* Send mail. Unlink the output file after opening it, so it - * doesn't hang around after the run. - */ -@@ -567,8 +554,13 @@ run_file(const char *filename, uid_t uid - unlink(newname); - free(newname); - -+#ifdef ATD_MAIL_PROGRAM - if (((send_mail != -1) && (buf.st_size != size)) || (send_mail == 1)) { -+ int mail_pid = -1; - -+ mail_pid = fork(); -+ -+ if ( mail_pid == 0 ) { - PRIV_START - - if (initgroups(pentry->pw_name, pentry->pw_gid)) -@@ -590,7 +582,20 @@ run_file(const char *filename, uid_t uid - perr("Exec failed for mail command"); - - PRIV_END -+ } -+ else if ( mail_pid == -1 ) { -+ syslog(LOG_ERR, "fork of mailer failed: %m"); -+ } -+ /* Parent */ -+ waitpid(mail_pid, (int *) NULL, 0); - } -+ -+#ifdef WITH_PAM -+ AT_CLOSE_PAM; -+ closelog(); -+ openlog("atd", LOG_PID, LOG_ATD); -+#endif -+#endif - exit(EXIT_SUCCESS); - } - -diff -up at-3.1.18/config.h.in.pam at-3.1.18/config.h.in ---- at-3.1.18/config.h.in.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/config.h.in 2016-03-23 12:40:10.695447139 +0100 -@@ -68,8 +68,8 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_NLIST_H - --/* Define to 1 for PAM support */ --#undef HAVE_PAM -+/* Define if you are building with_pam */ -+#undef WITH_PAM - - /* Define to 1 if you have the `pstat_getdynamic' function. */ - #undef HAVE_PSTAT_GETDYNAMIC -diff -up at-3.1.18/configure.ac.pam at-3.1.18/configure.ac ---- at-3.1.18/configure.ac.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/configure.ac 2016-03-23 12:45:27.885473913 +0100 -@@ -78,7 +78,7 @@ AC_FUNC_GETLOADAVG - AC_CHECK_FUNCS(getcwd mktime strftime setreuid setresuid sigaction waitpid) - AC_CHECK_HEADERS(security/pam_appl.h, [ - PAMLIB="-lpam" -- AC_DEFINE(HAVE_PAM, 1, [Define to 1 for PAM support]) -+ AC_DEFINE(WITH_PAM, 1, [Define to 1 for PAM support]) - ]) - - dnl Checking for programs -@@ -239,6 +239,13 @@ AC_ARG_WITH(daemon_username, - ) - AC_SUBST(DAEMON_USERNAME) - -+AC_ARG_WITH(pam, -+[ --with-pam Define to enable pam support ], -+AC_DEFINE(WITH_PAM), -+) -+AC_CHECK_LIB(pam, pam_start, PAMLIB='-lpam -lpam_misc') -+AC_SUBST(PAMLIB) -+ - AC_ARG_WITH(selinux, - [ --with-selinux Define to run with selinux], - AC_DEFINE(WITH_SELINUX, 1, [Define if you are building with_selinux]), -diff -up at-3.1.18/Makefile.in.pam at-3.1.18/Makefile.in ---- at-3.1.18/Makefile.in.pam 2016-03-23 12:55:30.000000000 +0100 -+++ at-3.1.18/Makefile.in 2016-03-23 12:57:58.347145148 +0100 -@@ -68,7 +68,7 @@ LIST = Filelist Filelist.asc - all: at atd atd.service atrun - - at: $(ATOBJECTS) -- $(CC) $(LDFLAGS) -pie -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) -+ $(CC) $(LDFLAGS) -pie -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) $(PAMLIB) - rm -f $(CLONES) - $(LN_S) -f at atq - $(LN_S) -f at atrm -diff -up at-3.1.18/perm.c.pam at-3.1.18/perm.c ---- at-3.1.18/perm.c.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/perm.c 2016-03-23 12:40:10.695447139 +0100 -@@ -51,6 +51,14 @@ - #define PRIV_END while(0) - #endif - -+#ifdef WITH_PAM -+#include -+static pam_handle_t *pamh = NULL; -+static const struct pam_conv conv = { -+ NULL -+}; -+#endif -+ - /* Structures and unions */ - - -@@ -108,18 +116,45 @@ user_in_file(const char *path, const cha - int - check_permission() - { -- uid_t uid = geteuid(); -+ uid_t euid = geteuid(), uid=getuid(), egid=getegid(), gid=getgid(); - struct passwd *pentry; - int allow = 0, deny = 1; - -- if (uid == 0) -+ int retcode = 0; -+ if (euid == 0) - return 1; - -- if ((pentry = getpwuid(uid)) == NULL) { -+ if ((pentry = getpwuid(euid)) == NULL) { - perror("Cannot access user database"); - exit(EXIT_FAILURE); - } - -+#ifdef WITH_PAM -+/* -+ * We must check if the atd daemon userid will be allowed to gain the job owner user's -+ * credentials with PAM . If not, the user has been denied at(1) usage, eg. with pam_access. -+ */ -+ if (setreuid(daemon_uid, daemon_uid) != 0) { -+ fprintf(stderr, "cannot set egid: %s", strerror(errno)); -+ exit(1); -+ } -+ if (setregid(daemon_gid, daemon_gid) != 0) { -+ fprintf(stderr, "cannot set euid: %s", strerror(errno)); -+ exit(1); -+ } -+ -+ AT_START_PAM; -+ AT_CLOSE_PAM; -+ if (setregid(gid,egid) != 0) { -+ fprintf(stderr, "cannot set egid: %s", strerror(errno)); -+ exit(1); -+ } -+ if (setreuid(uid,euid) != 0) { -+ fprintf(stderr, "cannot set euid: %s", strerror(errno)); -+ exit(1); -+ } -+#endif -+ - allow = user_in_file(ETCDIR "/at.allow", pentry->pw_name); - if (allow==0 || allow==1) - return allow; -diff -up at-3.1.18/privs.h.pam at-3.1.18/privs.h ---- at-3.1.18/privs.h.pam 2015-12-06 16:45:10.000000000 +0100 -+++ at-3.1.18/privs.h 2016-03-23 12:40:10.695447139 +0100 -@@ -144,3 +144,63 @@ extern gid_t real_gid, effective_gid, da - #error "Cannot implement user ID swapping without setreuid or setresuid" - #endif - #endif -+ -+#ifdef WITH_PAM -+/* PAM failed after session was open. */ -+#define PAM_SESSION_FAIL if (retcode != PAM_SUCCESS) \ -+ pam_close_session(pamh,PAM_SILENT); -+ -+/* syslog will be logging error messages */ -+#ifdef HAVE_UNISTD_H -+#include -+#endif -+ -+/* PAM fail even before opening the session */ -+#define PAM_FAIL_CHECK \ -+ do { if (retcode != PAM_SUCCESS) { \ -+ fprintf(stderr,"PAM failure: %s\n",pam_strerror(pamh, retcode)); \ -+ syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ -+ if (pamh) \ -+ pam_end(pamh, retcode); \ -+ if (setregid(getgid(),getegid()) != 0) { \ -+ fprintf(stderr, "cannot set egid: %s", strerror(errno)); \ -+ exit(1); \ -+ } \ -+ if (setreuid(getuid(),geteuid()) != 0) { \ -+ fprintf(stderr, "cannot set euid: %s", strerror(errno)); \ -+ exit(1); \ -+ } \ -+ exit(1); \ -+ } \ -+ } while (0) \ -+ -+static int pam_session_opened = 0; //global for open session -+ -+#define AT_START_PAM { \ -+ retcode = pam_start("atd", pentry->pw_name, &conv, &pamh); \ -+ PAM_FAIL_CHECK; \ -+ retcode = pam_set_item(pamh, PAM_TTY, "atd"); \ -+ PAM_FAIL_CHECK; \ -+ retcode = pam_acct_mgmt(pamh, PAM_SILENT); \ -+ PAM_FAIL_CHECK; \ -+} -+ -+#define AT_OPEN_PAM_SESSION { \ -+ retcode = pam_open_session(pamh, PAM_SILENT); \ -+ PAM_FAIL_CHECK; \ -+ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); \ -+ PAM_FAIL_CHECK; \ -+ if (retcode == PAM_SUCCESS) \ -+ pam_session_opened = 1; \ -+} -+ -+#define AT_CLOSE_PAM { \ -+ if (pam_session_opened != 0) { \ -+ pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); \ -+ pam_close_session(pamh, PAM_SILENT); \ -+ } \ -+ pam_end(pamh, PAM_SUCCESS); \ -+} -+ -+#endif -+ diff --git a/at-3.1.20-lock-locks.patch b/at-3.1.20-lock-locks.patch new file mode 100644 index 0000000..17d6ad4 --- /dev/null +++ b/at-3.1.20-lock-locks.patch @@ -0,0 +1,108 @@ +diff -up at-3.1.20/atd.c.lock-locks at-3.1.20/atd.c +--- at-3.1.20/atd.c.lock-locks 2016-07-01 10:41:50.640867692 +0200 ++++ at-3.1.20/atd.c 2016-07-01 10:42:32.345844967 +0200 +@@ -74,6 +74,9 @@ + #include + #endif + ++#include ++#include ++ + /* Local headers */ + + #include "privs.h" +@@ -288,7 +291,7 @@ run_file(const char *filename, uid_t uid + * mail to the user. + */ + pid_t pid; +- int fd_out, fd_in; ++ int fd_out, fd_in, fd_std; + char jobbuf[9]; + char *mailname = NULL; + int mailsize = 128; +@@ -410,6 +413,10 @@ run_file(const char *filename, uid_t uid + + fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); + ++ if (flock(fd_in, LOCK_EX | LOCK_NB) != 0) ++ perr("Somebody already locked the job %8lu (%.500s) - " ++ "aborting", jobno, filename); ++ + /* + * If the spool directory is mounted via NFS `atd' isn't able to + * read from the job file and will bump out here. The file is +@@ -553,10 +560,7 @@ run_file(const char *filename, uid_t uid + PRIV_END + } + /* We're the parent. Let's wait. +- */ +- close(fd_in); +- +- /* We inherited the master's SIGCHLD handler, which does a ++ We inherited the master's SIGCHLD handler, which does a + non-blocking waitpid. So this blocking one will eventually + return with an ECHILD error. + */ +@@ -573,14 +577,14 @@ run_file(const char *filename, uid_t uid + /* some sendmail implementations are confused if stdout, stderr are + * not available, so let them point to /dev/null + */ +- if ((fd_in = open("/dev/null", O_WRONLY)) < 0) ++ if ((fd_std = open("/dev/null", O_WRONLY)) < 0) + perr("Could not open /dev/null."); +- if (dup2(fd_in, STDOUT_FILENO) < 0) ++ if (dup2(fd_std, STDOUT_FILENO) < 0) + perr("Could not use /dev/null as standard output."); +- if (dup2(fd_in, STDERR_FILENO) < 0) ++ if (dup2(fd_std, STDERR_FILENO) < 0) + perr("Could not use /dev/null as standard error."); +- if (fd_in != STDOUT_FILENO && fd_in != STDERR_FILENO) +- close(fd_in); ++ if (fd_std != STDOUT_FILENO && fd_std != STDERR_FILENO) ++ close(fd_std); + + if (unlink(filename) == -1) + syslog(LOG_WARNING, "Warning: removing output file for job %li failed: %s", +@@ -588,7 +592,12 @@ run_file(const char *filename, uid_t uid + + /* The job is now finished. We can delete its input file. + */ +- chdir(ATJOB_DIR); ++ if (chdir(ATJOB_DIR) != 0) ++ perr("Somebody removed %s directory from under us.", ATJOB_DIR); ++ ++ /* This also removes the flock */ ++ (void)close(fd_in); ++ + unlink(newname); + free(newname); + +@@ -723,16 +732,18 @@ run_loop() + + /* Skip lock files */ + if (queue == '=') { +- /* FIXME: calhariz */ +- /* I think the following code is broken, but commenting +- may haven unknow side effects. Make a release and see +- in the wild how it works. For more information see: +- https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=818508/* +- +- /* if ((buf.st_nlink == 1) && (run_time + CHECK_INTERVAL <= now)) { */ +- /* /\* Remove stale lockfile FIXME: lock the lockfile, if you fail, it's still in use. *\/ */ +- /* unlink(dirent->d_name); */ +- /* } */ ++ if ((buf.st_nlink == 1) && (run_time + CHECK_INTERVAL <= now)) { ++ int fd; ++ ++ fd = open(dirent->d_name, O_RDONLY); ++ if (fd != -1) { ++ if (flock(fd, LOCK_EX | LOCK_NB) == 0) { ++ unlink(dirent->d_name); ++ syslog(LOG_NOTICE, "removing stale lock file %s\n", dirent->d_name); ++ } ++ (void)close(fd); ++ } ++ } + continue; + } + /* Skip any other file types which may have been invented in diff --git a/at-3.1.20-pam.patch b/at-3.1.20-pam.patch new file mode 100644 index 0000000..418fa84 --- /dev/null +++ b/at-3.1.20-pam.patch @@ -0,0 +1,436 @@ +diff -up at-3.1.20/at.c.pam at-3.1.20/at.c +--- at-3.1.20/at.c.pam 2016-06-28 22:18:00.000000000 +0200 ++++ at-3.1.20/at.c 2016-07-01 09:44:22.251683924 +0200 +@@ -144,18 +144,13 @@ sigc(int signo) + /* If the user presses ^C, remove the spool file and exit + */ + if (fcreated) { +- /* + PRIV_START +- ++ /* + We need the unprivileged uid here since the file is owned by the real + (not effective) uid. + */ +- setregid(real_gid, effective_gid); +- unlink(atfile); +- setregid(effective_gid, real_gid); +- /* ++ unlink(atfile); + PRIV_END +- */ + } + exit(EXIT_FAILURE); + } +@@ -315,26 +310,19 @@ writefile(time_t runtimer, char queue) + * bit. Yes, this is a kluge. + */ + cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); +- seteuid(real_uid); ++ if ((seteuid(effective_uid)) < 0) ++ perr("Error in seteuid: %s", errno); + if ((fd = open(atfile, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, S_IRUSR)) == -1) + perr("Cannot create atjob file %.500s", atfile); +- seteuid(effective_uid); + + if ((fd2 = dup(fd)) < 0) + perr("Error in dup() of job file"); + +- /* + if (fchown(fd2, real_uid, real_gid) != 0) +- perr("Cannot give away file"); +- */ ++ perr("Cannot give real_uid and real_gid the file"); + + PRIV_END + +- /* We no longer need suid root; now we just need to be able to write +- * to the directory, if necessary. +- */ +- +- REDUCE_PRIV(daemon_uid, daemon_gid) + /* We've successfully created the file; let's set the flag so it + * gets removed in case of an interrupt or error. + */ +@@ -673,7 +661,7 @@ process_jobs(int argc, char **argv, int + We need the unprivileged uid here since the file is owned by the real + (not effective) uid. + */ +- setregid(real_gid, effective_gid); ++ PRIV_START + + if (queue == '=') { + fprintf(stderr, "Warning: deleting running job\n"); +@@ -682,8 +670,8 @@ process_jobs(int argc, char **argv, int + perr("Cannot unlink %.500s", dirent->d_name); + rc = EXIT_FAILURE; + } ++ PRIV_END + +- setregid(effective_gid, real_gid); + done = 1; + + break; +@@ -693,7 +681,7 @@ process_jobs(int argc, char **argv, int + FILE *fp; + int ch; + +- setregid(real_gid, effective_gid); ++ PRIV_START + fp = fopen(dirent->d_name, "r"); + + if (fp) { +@@ -706,7 +694,7 @@ process_jobs(int argc, char **argv, int + perr("Cannot open %.500s", dirent->d_name); + rc = EXIT_FAILURE; + } +- setregid(effective_gid, real_gid); ++ PRIV_END + } + break; + +diff -up at-3.1.20/atd.c.pam at-3.1.20/atd.c +--- at-3.1.20/atd.c.pam 2016-06-28 22:14:39.000000000 +0200 ++++ at-3.1.20/atd.c 2016-07-01 09:44:22.251683924 +0200 +@@ -91,6 +91,10 @@ int selinux_enabled = 0; + + /* Macros */ + ++#ifndef LOG_ATD ++#define LOG_ATD LOG_DAEMON ++#endif ++ + #define BATCH_INTERVAL_DEFAULT 60 + #define CHECK_INTERVAL 3600 + +@@ -114,7 +118,7 @@ static int run_as_daemon = 0; + + static volatile sig_atomic_t term_signal = 0; + +-#ifdef HAVE_PAM ++#ifdef WITH_PAM + #include + + static pam_handle_t *pamh = NULL; +@@ -123,15 +127,7 @@ static const struct pam_conv conv = { + NULL + }; + +-#define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ +- fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \ +- syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ +- pam_end(pamh, retcode); exit(1); \ +- } +-#define PAM_END { retcode = pam_close_session(pamh,0); \ +- pam_end(pamh,retcode); } +- +-#endif /* HAVE_PAM */ ++#endif /* WITH_PAM */ + + /* Signal handlers */ + RETSIGTYPE +@@ -292,7 +288,7 @@ run_file(const char *filename, uid_t uid + char fmt[64]; + unsigned long jobno; + int rc; +-#ifdef HAVE_PAM ++#ifdef WITH_PAM + int retcode; + #endif + +@@ -449,17 +445,11 @@ run_file(const char *filename, uid_t uid + fstat(fd_out, &buf); + size = buf.st_size; + +-#ifdef HAVE_PAM +- PRIV_START +- retcode = pam_start("atd", pentry->pw_name, &conv, &pamh); +- PAM_FAIL_CHECK; +- retcode = pam_acct_mgmt(pamh, PAM_SILENT); +- PAM_FAIL_CHECK; +- retcode = pam_open_session(pamh, PAM_SILENT); +- PAM_FAIL_CHECK; +- retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); +- PAM_FAIL_CHECK; +- PRIV_END ++#ifdef WITH_PAM ++ AT_START_PAM; ++ AT_OPEN_PAM_SESSION; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); + #endif + + close(STDIN_FILENO); +@@ -473,7 +463,14 @@ run_file(const char *filename, uid_t uid + else if (pid == 0) { + char *nul = NULL; + char **nenvp = &nul; ++ char **pam_envp=0L; + ++ PRIV_START ++#ifdef WITH_PAM ++ pam_envp = pam_getenvlist(pamh); ++ if ( ( pam_envp != 0L ) && (pam_envp[0] != 0L) ) ++ nenvp = pam_envp; ++#endif + /* Set up things for the child; we want standard input from the + * input file, and standard output and error sent to our output file. + */ +@@ -492,8 +489,6 @@ run_file(const char *filename, uid_t uid + close(fd_in); + close(fd_out); + +- PRIV_START +- + nice((tolower((int) queue) - 'a' + 1) * 2); + + #ifdef WITH_SELINUX +@@ -514,9 +509,9 @@ run_file(const char *filename, uid_t uid + + chdir("/"); + +- if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) +- perr("Exec failed for /bin/sh"); +- ++ execle("/bin/sh", "sh", (char *) NULL, nenvp); ++ perr("Exec failed for /bin/sh"); ++ /* perr exits, the PRIV_END is just for nice form */ + PRIV_END + } + /* We're the parent. Let's wait. +@@ -529,14 +524,6 @@ run_file(const char *filename, uid_t uid + */ + waitpid(pid, (int *) NULL, 0); + +-#ifdef HAVE_PAM +- PRIV_START +- pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); +- retcode = pam_close_session(pamh, PAM_SILENT); +- pam_end(pamh, retcode); +- PRIV_END +-#endif +- + /* Send mail. Unlink the output file after opening it, so it + * doesn't hang around after the run. + */ +@@ -567,8 +554,13 @@ run_file(const char *filename, uid_t uid + unlink(newname); + free(newname); + ++#ifdef ATD_MAIL_PROGRAM + if (((send_mail != -1) && (buf.st_size != size)) || (send_mail == 1)) { ++ int mail_pid = -1; + ++ mail_pid = fork(); ++ ++ if ( mail_pid == 0 ) { + PRIV_START + + if (initgroups(pentry->pw_name, pentry->pw_gid)) +@@ -590,7 +582,20 @@ run_file(const char *filename, uid_t uid + perr("Exec failed for mail command"); + + PRIV_END ++ } ++ else if ( mail_pid == -1 ) { ++ syslog(LOG_ERR, "fork of mailer failed: %m"); ++ } ++ /* Parent */ ++ waitpid(mail_pid, (int *) NULL, 0); + } ++ ++#ifdef WITH_PAM ++ AT_CLOSE_PAM; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); ++#endif ++#endif + exit(EXIT_SUCCESS); + } + +diff -up at-3.1.20/config.h.in.pam at-3.1.20/config.h.in +--- at-3.1.20/config.h.in.pam 2015-12-18 21:29:24.000000000 +0100 ++++ at-3.1.20/config.h.in 2016-07-01 09:44:22.251683924 +0200 +@@ -68,8 +68,8 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_NLIST_H + +-/* Define to 1 for PAM support */ +-#undef HAVE_PAM ++/* Define if you are building with_pam */ ++#undef WITH_PAM + + /* Define to 1 if you have the `pstat_getdynamic' function. */ + #undef HAVE_PSTAT_GETDYNAMIC +diff -up at-3.1.20/configure.ac.pam at-3.1.20/configure.ac +--- at-3.1.20/configure.ac.pam 2016-06-28 22:55:52.000000000 +0200 ++++ at-3.1.20/configure.ac 2016-07-01 09:45:23.268092527 +0200 +@@ -78,7 +78,7 @@ AC_FUNC_GETLOADAVG + AC_CHECK_FUNCS(getcwd mktime strftime setreuid setresuid sigaction waitpid) + AC_CHECK_HEADERS(security/pam_appl.h, [ + PAMLIB="-lpam" +- AC_DEFINE(HAVE_PAM, 1, [Define to 1 for PAM support]) ++ AC_DEFINE(WITH_PAM, 1, [Define to 1 for PAM support]) + ]) + + dnl Checking for programs +@@ -239,6 +239,13 @@ AC_ARG_WITH(daemon_username, + ) + AC_SUBST(DAEMON_USERNAME) + ++AC_ARG_WITH(pam, ++[ --with-pam Define to enable pam support ], ++AC_DEFINE(WITH_PAM), ++) ++AC_CHECK_LIB(pam, pam_start, PAMLIB='-lpam -lpam_misc') ++AC_SUBST(PAMLIB) ++ + AC_ARG_WITH(selinux, + [ --with-selinux Define to run with selinux (default=check)], + [], +diff -up at-3.1.20/Makefile.in.pam at-3.1.20/Makefile.in +--- at-3.1.20/Makefile.in.pam 2016-07-01 09:44:22.250683901 +0200 ++++ at-3.1.20/Makefile.in 2016-07-01 09:44:22.252683947 +0200 +@@ -68,7 +68,7 @@ LIST = Filelist Filelist.asc + all: at atd atd.service atrun + + at: $(ATOBJECTS) +- $(CC) $(LDFLAGS) -pie -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) ++ $(CC) $(LDFLAGS) -pie -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) $(PAMLIB) + rm -f $(CLONES) + $(LN_S) -f at atq + $(LN_S) -f at atrm +diff -up at-3.1.20/perm.c.pam at-3.1.20/perm.c +--- at-3.1.20/perm.c.pam 2015-08-22 00:09:22.000000000 +0200 ++++ at-3.1.20/perm.c 2016-07-01 09:44:22.252683947 +0200 +@@ -51,6 +51,14 @@ + #define PRIV_END while(0) + #endif + ++#ifdef WITH_PAM ++#include ++static pam_handle_t *pamh = NULL; ++static const struct pam_conv conv = { ++ NULL ++}; ++#endif ++ + /* Structures and unions */ + + +@@ -108,18 +116,45 @@ user_in_file(const char *path, const cha + int + check_permission() + { +- uid_t uid = geteuid(); ++ uid_t euid = geteuid(), uid=getuid(), egid=getegid(), gid=getgid(); + struct passwd *pentry; + int allow = 0, deny = 1; + +- if (uid == 0) ++ int retcode = 0; ++ if (euid == 0) + return 1; + +- if ((pentry = getpwuid(uid)) == NULL) { ++ if ((pentry = getpwuid(euid)) == NULL) { + perror("Cannot access user database"); + exit(EXIT_FAILURE); + } + ++#ifdef WITH_PAM ++/* ++ * We must check if the atd daemon userid will be allowed to gain the job owner user's ++ * credentials with PAM . If not, the user has been denied at(1) usage, eg. with pam_access. ++ */ ++ if (setreuid(daemon_uid, daemon_uid) != 0) { ++ fprintf(stderr, "cannot set egid: %s", strerror(errno)); ++ exit(1); ++ } ++ if (setregid(daemon_gid, daemon_gid) != 0) { ++ fprintf(stderr, "cannot set euid: %s", strerror(errno)); ++ exit(1); ++ } ++ ++ AT_START_PAM; ++ AT_CLOSE_PAM; ++ if (setregid(gid,egid) != 0) { ++ fprintf(stderr, "cannot set egid: %s", strerror(errno)); ++ exit(1); ++ } ++ if (setreuid(uid,euid) != 0) { ++ fprintf(stderr, "cannot set euid: %s", strerror(errno)); ++ exit(1); ++ } ++#endif ++ + allow = user_in_file(ETCDIR "/at.allow", pentry->pw_name); + if (allow==0 || allow==1) + return allow; +diff -up at-3.1.20/privs.h.pam at-3.1.20/privs.h +--- at-3.1.20/privs.h.pam 2015-08-22 00:09:22.000000000 +0200 ++++ at-3.1.20/privs.h 2016-07-01 09:44:22.252683947 +0200 +@@ -144,3 +144,63 @@ extern gid_t real_gid, effective_gid, da + #error "Cannot implement user ID swapping without setreuid or setresuid" + #endif + #endif ++ ++#ifdef WITH_PAM ++/* PAM failed after session was open. */ ++#define PAM_SESSION_FAIL if (retcode != PAM_SUCCESS) \ ++ pam_close_session(pamh,PAM_SILENT); ++ ++/* syslog will be logging error messages */ ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++ ++/* PAM fail even before opening the session */ ++#define PAM_FAIL_CHECK \ ++ do { if (retcode != PAM_SUCCESS) { \ ++ fprintf(stderr,"PAM failure: %s\n",pam_strerror(pamh, retcode)); \ ++ syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ ++ if (pamh) \ ++ pam_end(pamh, retcode); \ ++ if (setregid(getgid(),getegid()) != 0) { \ ++ fprintf(stderr, "cannot set egid: %s", strerror(errno)); \ ++ exit(1); \ ++ } \ ++ if (setreuid(getuid(),geteuid()) != 0) { \ ++ fprintf(stderr, "cannot set euid: %s", strerror(errno)); \ ++ exit(1); \ ++ } \ ++ exit(1); \ ++ } \ ++ } while (0) \ ++ ++static int pam_session_opened = 0; //global for open session ++ ++#define AT_START_PAM { \ ++ retcode = pam_start("atd", pentry->pw_name, &conv, &pamh); \ ++ PAM_FAIL_CHECK; \ ++ retcode = pam_set_item(pamh, PAM_TTY, "atd"); \ ++ PAM_FAIL_CHECK; \ ++ retcode = pam_acct_mgmt(pamh, PAM_SILENT); \ ++ PAM_FAIL_CHECK; \ ++} ++ ++#define AT_OPEN_PAM_SESSION { \ ++ retcode = pam_open_session(pamh, PAM_SILENT); \ ++ PAM_FAIL_CHECK; \ ++ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); \ ++ PAM_FAIL_CHECK; \ ++ if (retcode == PAM_SUCCESS) \ ++ pam_session_opened = 1; \ ++} ++ ++#define AT_CLOSE_PAM { \ ++ if (pam_session_opened != 0) { \ ++ pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); \ ++ pam_close_session(pamh, PAM_SILENT); \ ++ } \ ++ pam_end(pamh, PAM_SUCCESS); \ ++} ++ ++#endif ++ diff --git a/at-3.1.20-shell.patch b/at-3.1.20-shell.patch new file mode 100644 index 0000000..ac9586c --- /dev/null +++ b/at-3.1.20-shell.patch @@ -0,0 +1,47 @@ +diff -up at-3.1.20/at.c.shell at-3.1.20/at.c +--- at-3.1.20/at.c.shell 2016-07-01 09:47:13.392684445 +0200 ++++ at-3.1.20/at.c 2016-07-01 09:48:47.679931959 +0200 +@@ -62,11 +62,8 @@ + #include + #include + +-#ifdef TM_IN_SYS_TIME + #include +-#else + #include +-#endif + + #ifdef HAVE_UNISTD_H + #include +@@ -239,6 +236,12 @@ writefile(time_t runtimer, char queue) + int kill_errno; + int rc; + int mailsize = 128; ++ struct timeval tv; ++ struct timezone tz; ++ long int i; ++ ++ gettimeofday(&tv, &tz); ++ srandom(getpid()+tv.tv_usec); + + /* Install the signal handler for SIGINT; terminate after removing the + * spool file if necessary +@@ -465,6 +468,9 @@ writefile(time_t runtimer, char queue) + fprintf(fp, " || {\n\t echo 'Execution directory " + "inaccessible' >&2\n\t exit 1\n}\n"); + ++ i = random(); ++ fprintf(fp, "${SHELL:-/bin/sh} << \'marcinDELIMITER%08lx\'\n", i); ++ + istty = isatty(fileno(stdin)); + if (istty) { + fprintf(stderr, "at> "); +@@ -480,7 +486,7 @@ writefile(time_t runtimer, char queue) + if (istty) { + fprintf(stderr, "\n"); + } +- fprintf(fp, "\n"); ++ fprintf(fp, "marcinDELIMITER%08lx\n", i); + if (ferror(fp)) + panic("Output error"); + fflush(fp); diff --git a/at.spec b/at.spec index a0dcb74..560bea0 100644 --- a/at.spec +++ b/at.spec @@ -2,8 +2,8 @@ Summary: Job spooling tools Name: at -Version: 3.1.18 -Release: 2%{?dist} +Version: 3.1.20 +Release: 1%{?dist} # http://packages.debian.org/changelogs/pool/main/a/at/current/copyright # + install-sh is MIT license with changes under Public Domain License: GPLv3+ and GPLv2+ and ISC and MIT and Public Domain @@ -18,9 +18,9 @@ Source5: atd.systemd Patch0: at-aarch64.patch Patch1: at-3.1.18-make.patch -Patch2: at-3.1.18-pam.patch +Patch2: at-3.1.20-pam.patch Patch4: at-3.1.14-opt_V.patch -Patch5: at-3.1.14-shell.patch +Patch5: at-3.1.20-shell.patch Patch6: at-3.1.18-nitpicks.patch Patch8: at-3.1.14-fix_no_export.patch Patch9: at-3.1.14-mailwithhostname.patch @@ -30,6 +30,7 @@ Patch13: at-3.1.18-noabort.patch Patch14: at-3.1.16-fclose-error.patch Patch15: at-3.1.16-clear-nonjobs.patch Patch16: at-3.1.18-utc-dst.patch +Patch17: at-3.1.20-lock-locks.patch BuildRequires: fileutils /etc/init.d BuildRequires: flex flex-static bison autoconf @@ -63,7 +64,7 @@ need to be repeated at the same time every day/week, etc. you should use crontab instead. %prep -%setup -c -q +%setup -q cp %{SOURCE1} . %patch0 -p1 -b .arm %patch1 -p1 -b .make @@ -79,6 +80,7 @@ cp %{SOURCE1} . %patch14 -p1 -b .fclose %patch15 -p1 -b .clear-nojobs %patch16 -p1 -b .dst +%patch17 -p1 -b .lock-locks %build # patch9 touches configure.in @@ -176,6 +178,11 @@ chown daemon:daemon %{_localstatedir}/spool/at/.SEQ %attr(0644,root,root) /%{_unitdir}/atd.service %changelog +* Fri Jul 1 2016 Tomáš Mráz - 3.1.20-1 +- new upstream release +- properly lock the lock files to be able to safely remove + stale ones + * Mon May 23 2016 Tomáš Mráz - 3.1.18-2 - SIGPIPE should not be ignored in atd (#1338039) diff --git a/sources b/sources index 16baf64..67f3292 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -f67a7aab557cd5b4a1311079a08acebe at_3.1.18.orig.tar.gz +e7bd7b785b2cbb17e133d6bdc0fb099e at_3.1.20.orig.tar.gz