diff --git a/README.md b/README.md index f16856d..c99135d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # at +At and batch read commands from standard input or from a specified file. +At allows you to specify that a command will be run at a particular time (now or a specified time in the future). +Batch will execute commands when the system load levels drop to a particular level. +Both commands use /bin/sh to run the commands. + diff --git a/at-3.1.10-daylight.patch b/at-3.1.10-daylight.patch new file mode 100644 index 0000000..62b19f2 --- /dev/null +++ b/at-3.1.10-daylight.patch @@ -0,0 +1,14 @@ +--- at-3.1.10/at.c.daylight 2007-03-31 00:03:47.000000000 +0200 ++++ at-3.1.10/at.c 2007-03-31 00:06:23.000000000 +0200 +@@ -840,8 +840,9 @@ + + if( tm.tm_mday ) + { +- tm.tm_isdst = tm_now.tm_isdst; +- return mktime(&tm); ++ tm.tm_isdst = -1; ++ t = mktime(&tm); ++ return t; + } else + return 0L; + } diff --git a/at-3.1.10-dont_fork.patch b/at-3.1.10-dont_fork.patch new file mode 100644 index 0000000..1a23686 --- /dev/null +++ b/at-3.1.10-dont_fork.patch @@ -0,0 +1,59 @@ +--- at-3.1.10/daemon.c.dontfork 2005-08-05 05:16:01.000000000 +0200 ++++ at-3.1.10/daemon.c 2006-09-12 13:53:10.000000000 +0200 +@@ -50,7 +50,8 @@ + + static const char *svnid = "$Id$"; + +-int daemon_debug; ++int daemon_debug = 0; ++int daemon_nofork = 0; + + static int + lock_fd(int fd) +@@ -119,15 +120,18 @@ + (open("/dev/null", O_RDWR) != 2)) { + perr("Error redirecting I/O"); + } ++ } ++ if (daemon_nofork) pid = getpid(); ++ else { + pid = fork(); + if (pid == -1) { + perr("Cannot fork"); + } else if (pid != 0) { + exit(0); + } ++ (void) setsid(); + } + old_umask = umask(S_IWGRP | S_IWOTH); +- (void) setsid(); + + PRIV_START + +--- at-3.1.10/atd.8.in.dontfork 2005-08-29 10:08:51.000000000 +0200 ++++ at-3.1.10/atd.8.in 2006-09-12 13:53:10.000000000 +0200 +@@ -10,6 +10,7 @@ + .IR batch_interval ] + .RB [ -d ] + .RB [ -s ] ++.RB [ -n ] + .SH DESCRIPTION + .B atd + runs jobs queued by +@@ -46,6 +47,9 @@ + is installed as + .B @prefix@/sbin/atrun + for backward compatibility. ++.TP 8 ++.B -n ++Don't fork option. + .SH WARNING + .B atd + won't work if its spool directory is mounted via NFS even if +--- at-3.1.10/daemon.h.dontfork 2005-08-05 05:16:01.000000000 +0200 ++++ at-3.1.10/daemon.h 2006-09-12 13:54:43.000000000 +0200 +@@ -14,3 +14,4 @@ + perr (const char *fmt, ...); + + extern int daemon_debug; ++extern int daemon_nofork; diff --git a/at-3.1.10-fix_no_export.patch b/at-3.1.10-fix_no_export.patch new file mode 100644 index 0000000..5a31416 --- /dev/null +++ b/at-3.1.10-fix_no_export.patch @@ -0,0 +1,84 @@ +--- at-3.1.10/at.c.fix 2006-09-12 13:21:16.000000000 +0200 ++++ at-3.1.10/at.c 2006-09-12 13:20:08.000000000 +0200 +@@ -396,8 +396,9 @@ + unsigned int i; + for (i = 0; i < sizeof(no_export) / sizeof(no_export[0]); i++) { + export = export +- && (strncmp(*atenv, no_export[i], +- (size_t) (eqp - *atenv)) != 0); ++ && ( (((size_t) (eqp - *atenv)) != strlen(no_export[i])) ++ ||(strncmp(*atenv, no_export[i],(size_t) (eqp - *atenv)) != 0) ++ ); + } + eqp++; + } +--- at-3.1.10/at.1.in.fix 2006-09-12 13:21:16.000000000 +0200 ++++ at-3.1.10/at.1.in 2006-09-12 13:10:28.000000000 +0200 +@@ -42,8 +42,7 @@ + and + .B batch + read commands from standard input or a specified file which are to +-be executed at a later time, using +-.BR /bin/sh . ++be executed at a later time. + .TP 8 + .BR at + executes commands at a specified time. +@@ -244,8 +243,56 @@ + option argument, which must have the same format as specified for the + .BR touch(1) + utility's +-.B -t ++.B \-t + time option argument ([[CC]YY]MMDDhhmm). ++.SH ENVIRONMENT ++.P ++.TP 8 ++.B SHELL ++The value of the SHELL environment variable at the time of ++.B at ++invocation will determine which shell is used to execute the ++.B at ++job commands. If SHELL is unset when ++.B at ++is invoked, the user's login shell will be used; otherwise, ++if SHELL is set when ++.B at ++is invoked, it must contain the path of a shell interpreter ++executable that will be used to run the commands at the specified time. ++.P ++.B at ++will record the values of ++environment variables present at time of ++.B at ++invocation. When the commands are run at the specified time, ++.B at ++will restore these variables to their recorded values . ++These variables are excluded from this processing and are never ++set by ++.B at ++when the commands are run : ++.br ++.BI TERM, ++.BI DISPLAY, ++.BI SHELLOPTS, ++.BI _, ++.BI PPID, ++.BI BASH_VERSINFO, ++.BI EUID, ++.BI UID, ++.BI GROUPS. ++.br ++If the user submitting the ++.B at ++job is not the super-user, variables that alter the behaviour of the ++loader ++.BR ld.so(8), ++such as ++.B LD_LIBRARY_PATH ++, cannot be recorded and restored by ++.B at . ++.P + .SH FILES + .I @ATJBD@ + .br diff --git a/at-3.1.10-instinet.patch b/at-3.1.10-instinet.patch new file mode 100644 index 0000000..fc480cb --- /dev/null +++ b/at-3.1.10-instinet.patch @@ -0,0 +1,245 @@ +--- at-3.1.10/atd.c.instinet 2006-09-12 11:01:10.000000000 +0200 ++++ at-3.1.10/atd.c 2006-09-12 11:24:49.000000000 +0200 +@@ -102,7 +102,7 @@ + static const char *svnid = "$Id$"; + static double load_avg = LOADAVG_MX; + static time_t now; +-static time_t last_chg; ++//static time_t last_chg; + static int nothing_to_do; + unsigned int batch_interval; + static int run_as_daemon = 0; +@@ -197,7 +197,7 @@ + #endif + + static void +-run_file(const char *filename, uid_t uid, gid_t gid) ++run_file(char *filename, uid_t uid, gid_t gid) + { + /* Run a file by by spawning off a process which redirects I/O, + * spawns a subshell, then waits for it to complete and sends +@@ -208,7 +208,7 @@ + char jobbuf[9]; + char *mailname = NULL; + int mailsize = 128; +- char *newname; ++ char newname[256]; + FILE *stream; + int send_mail = 0; + struct stat buf, lbuf; +@@ -242,11 +242,17 @@ + + sprintf(jobbuf, "%8lu", jobno); + +- if ((newname = malloc(strlen(filename) + 1)) == NULL) +- pabort("Job %8lu : out of virtual memory", jobno); ++ if( strlen( filename ) >= sizeof( newname ) - 1 ) ++ pabort("File name too long: %s", filename ); + + strcpy(newname, filename); + ++ newname[0] = '!'; ++ ++ if( rename( filename, newname ) < 0 ) ++ perr( "Error renaming job file." ); ++ ++ filename[0] = '!'; + newname[0] = '='; + + /* We try to make a hard link to lock the file. If we fail, then +@@ -264,14 +270,15 @@ + } + } + /* If something goes wrong between here and the unlink() call, +- * the job gets restarted as soon as the "=" entry is cleared +- * by the main atd loop. +- */ ++ * the job will remain in the "!" queue. ++ * no point in retrying, and need glaring proof that something went wrong ++ */ + + pid = fork(); +- if (pid == -1) ++ if (pid == -1) { ++ unlink(newname); + perr("Cannot fork"); +- ++ } + else if (pid != 0) { + free(mailname); + free(newname); +@@ -284,6 +291,7 @@ + + pentry = getpwuid(uid); + if (pentry == NULL) { ++ unlink(newname); + pabort("Userid %lu not found - aborting job %8lu (%.500s)", + (unsigned long) uid, jobno, filename); + } +@@ -293,35 +301,43 @@ + + PRIV_END + +- if (stream == NULL) ++ if (stream == NULL) { ++ unlink( newname ); + perr("Cannot open input file"); +- +- if ((fd_in = dup(fileno(stream))) < 0) ++ } ++ if ((fd_in = dup(fileno(stream))) < 0) { ++ unlink( newname ); + perr("Error duplicating input file descriptor"); +- +- if (fstat(fd_in, &buf) == -1) ++ } ++ if (fstat(fd_in, &buf) == -1) { ++ unlink( newname ); + perr("Error in fstat of input file descriptor"); +- +- if (lstat(filename, &lbuf) == -1) ++ } ++ if (lstat(filename, &lbuf) == -1) { ++ unlink( newname ); + perr("Error in fstat of input file"); +- +- if (S_ISLNK(lbuf.st_mode)) ++ } ++ if (S_ISLNK(lbuf.st_mode)) { ++ unlink( newname ); + perr("Symbolic link encountered in job %8lu (%.500s) - aborting", + jobno, filename); +- ++ } + if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || + (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || +- (lbuf.st_size != buf.st_size)) ++ (lbuf.st_size != buf.st_size)) { ++ unlink( newname ); + perr("Somebody changed files from under us for job %8lu (%.500s) - " + "aborting", jobno, filename); +- ++ } + if (buf.st_nlink > 2) { ++ unlink( newname ); + perr("Somebody is trying to run a linked script for job %8lu (%.500s)", + jobno, filename); + } +- if ((fflags = fcntl(fd_in, F_GETFD)) < 0) ++ if ((fflags = fcntl(fd_in, F_GETFD)) < 0) { ++ unlink( newname ); + perr("Error in fcntl"); +- ++ } + fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); + + /* +@@ -335,28 +351,44 @@ + mailsize ); + + if (fscanf(stream, fmt, +- &nuid, &ngid, mailname, &send_mail) != 4) ++ &nuid, &ngid, mailname, &send_mail) != 4) { ++ unlink( newname ); + pabort("File %.500s is in wrong format - aborting", + filename); ++ } + +- if (mailname[0] == '-') ++ if (mailname[0] == '-') { ++ unlink( newname ); + pabort("illegal mail name %.300s in job %8lu (%.300s)", mailname, + jobno, filename); +- +- if (nuid != uid) ++ } ++ if (nuid != uid) { ++ unlink( newname ); + pabort("Job %8lu (%.500s) - userid %d does not match file uid %d", + jobno, filename, nuid, uid); +- ++ } ++ if (ngid != gid) { ++ unlink( newname ); ++ pabort("Job %8lu %.500s - groupid %d does not match file gid %d", ++ jobno, filename, ngid, gid); ++ } + /* We are now committed to executing this script. Unlink the + * original. + */ + + unlink(filename); + ++ /* If we bail out from now on, the job gets stuck in "=" ++ * The main loop should take care of that. ++ */ ++ + fclose(stream); ++ + if (chdir(ATSPOOL_DIR) < 0) + perr("Cannot chdir to " ATSPOOL_DIR); + ++ filename[0] = queue; ++ + /* Create a file to hold the output of the job we are about to run. + * Write the mail header. Complain in case + */ +@@ -466,19 +498,19 @@ + #endif + + /* Send mail. Unlink the output file after opening it, so it +- * doesn't hang around after the run. ++ * doesn't hang around after the run (if we are to send mail). + */ +- stat(filename, &buf); +- if (open(filename, O_RDONLY) != STDIN_FILENO) +- perr("Open of jobfile failed"); +- +- unlink(filename); ++ if( send_mail != -1 ) { ++ stat(filename, &buf); ++ if (open(filename, O_RDONLY) != STDIN_FILENO) ++ perr("Open of jobfile failed"); ++ unlink(filename); ++ } + + /* The job is now finished. We can delete its input file. + */ + chdir(ATJOB_DIR); + unlink(newname); +- free(newname); + + if (((send_mail != -1) && (buf.st_size != size)) || (send_mail == 1)) { + +@@ -508,6 +540,8 @@ + exit(EXIT_SUCCESS); + } + ++#define CHECK_INTERVAL_5MIN 300 ++ + static time_t + run_loop() + { +@@ -537,7 +571,7 @@ + * atrun. + */ + +- next_job = now + CHECK_INTERVAL; ++ next_job = now + CHECK_INTERVAL_5MIN; + if (next_batch == 0) + next_batch = now; + +@@ -548,11 +582,11 @@ + + if (stat(".", &buf) == -1) + perr("Cannot stat " ATJOB_DIR); +- ++/* + if (nothing_to_do && buf.st_mtime <= last_chg) + return next_job; + last_chg = buf.st_mtime; +- ++*/ + if ((spool = opendir(".")) == NULL) + perr("Cannot read " ATJOB_DIR); + diff --git a/at-3.1.10-lexer-parser.patch b/at-3.1.10-lexer-parser.patch new file mode 100644 index 0000000..9280a2e --- /dev/null +++ b/at-3.1.10-lexer-parser.patch @@ -0,0 +1,14 @@ +--- at-3.1.10/parsetime.y.parser 2005-08-05 05:31:04.000000000 +0200 ++++ at-3.1.10/parsetime.y 2006-09-07 12:47:45.000000000 +0200 +@@ -55,8 +55,10 @@ + %% + timespec : spec_base + | spec_base inc_or_dec ++ { ++ time_only = 0; ++ } + ; +- + spec_base : date + | time + { diff --git a/at-3.1.10-man-timespec-path.patch b/at-3.1.10-man-timespec-path.patch new file mode 100644 index 0000000..53b0425 --- /dev/null +++ b/at-3.1.10-man-timespec-path.patch @@ -0,0 +1,11 @@ +--- at-3.1.10/at.1.in.path 2005-08-29 10:09:24.000000000 +0200 ++++ at-3.1.10/at.1.in 2006-09-07 11:07:21.000000000 +0200 +@@ -110,7 +110,7 @@ + .B at 1am tomorrow. + .PP + The exact definition of the time specification can be found in +-.IR @prefix@/share/doc/at/timespec . ++.IR @prefix@/share/doc/at-@VERSION@/timespec. + .PP + For both + .BR at " and " batch , diff --git a/at-3.1.10-perm.patch b/at-3.1.10-perm.patch new file mode 100644 index 0000000..57ea7cf --- /dev/null +++ b/at-3.1.10-perm.patch @@ -0,0 +1,83 @@ +--- at-3.1.10/at.c.perm 2006-11-14 12:26:27.000000000 +0100 ++++ at-3.1.10/at.c 2006-11-14 12:28:15.000000000 +0100 +@@ -144,17 +144,12 @@ + */ + 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); +- /* +- PRIV_END +- */ + } + exit(EXIT_FAILURE); + } +@@ -314,18 +309,18 @@ + * bit. Yes, this is a kluge. + */ + cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); +- seteuid(real_uid); ++ seteuid(effective_uid); + 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); ++ //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"); +- */ ++ + + PRIV_END + +@@ -656,6 +651,7 @@ + We need the unprivileged uid here since the file is owned by the real + (not effective) uid. + */ ++// PRIV_START + setregid(real_gid, effective_gid); + + if (queue == '=') { +@@ -668,17 +664,17 @@ + + setregid(effective_gid, real_gid); + done = 1; +- ++// PRIV_END + break; + + case CAT: + { + FILE *fp; + int ch; +- ++ // PRIV_START + setregid(real_gid, effective_gid); + fp = fopen(dirent->d_name, "r"); +- ++ // PRIV_END + if (fp) { + while ((ch = getc(fp)) != EOF) { + putchar(ch); +--- at-3.1.10/Makefile.in.perm 2006-11-14 12:26:27.000000000 +0100 ++++ at-3.1.10/Makefile.in 2006-11-14 12:26:27.000000000 +0100 +@@ -97,7 +97,7 @@ + $(INSTALL) -m 755 -d $(IROOT)$(atdocdir) + $(INSTALL) -m 755 -d $(IROOT)$(ATJOB_DIR) + $(INSTALL) -m 755 -d $(IROOT)$(etcdir)/pam.d +- $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 755 -d $(IROOT) $(ATSPOOL_DIR) ++ $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 755 -d $(IROOT)$(ATSPOOL_DIR) + chmod 700 $(IROOT)$(ATSPOOL_DIR) $(IROOT)$(ATJOB_DIR) + touch $(IROOT)$(LFILE) + chmod 600 $(IROOT)$(LFILE) diff --git a/at-3.1.10-pie.patch b/at-3.1.10-pie.patch new file mode 100644 index 0000000..4087aca --- /dev/null +++ b/at-3.1.10-pie.patch @@ -0,0 +1,27 @@ +--- at-3.1.10/Makefile.in.pie 2006-09-12 08:28:13.000000000 +0200 ++++ at-3.1.10/Makefile.in 2006-09-12 08:30:47.000000000 +0200 +@@ -69,13 +69,13 @@ + all: at atd atrun + + at: $(ATOBJECTS) +- $(CC) $(CFLAGS) -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) ++ $(CC) $(CFLAGS) -o at -pie $(ATOBJECTS) $(LIBS) $(LEXLIB) + rm -f $(CLONES) + $(LN_S) -f at atq + $(LN_S) -f at atrm + + atd: $(RUNOBJECTS) +- $(CC) $(CFLAGS) -o atd $(RUNOBJECTS) $(LIBS) $(PAMLIB) ++ $(CC) $(CFLAGS) -o atd -pie $(RUNOBJECTS) $(LIBS) $(PAMLIB) + + y.tab.c y.tab.h: parsetime.y + $(YACC) -d parsetime.y +@@ -87,7 +87,7 @@ + configure + + .c.o: +- $(CC) -c $(CFLAGS) $(DEFS) $*.c ++ $(CC) -c $(CFLAGS) -fpie $(DEFS) $*.c + + install: all + $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(etcdir) diff --git a/at-3.1.10-typo.patch b/at-3.1.10-typo.patch new file mode 100644 index 0000000..79d171e --- /dev/null +++ b/at-3.1.10-typo.patch @@ -0,0 +1,56 @@ +--- at-3.1.10/atrun.8.in.typo 2005-08-29 10:08:41.000000000 +0200 ++++ at-3.1.10/atrun.8.in 2006-09-12 10:00:57.000000000 +0200 +@@ -11,7 +11,7 @@ + .B atrun + runs jobs queued by + .BR at(1) . +-It is a shell script containing invoking ++It is a shell script invoking + .B @sbindir@/atd + with the + .I -s +--- at-3.1.10/atd.c.typo 2006-09-12 10:00:57.000000000 +0200 ++++ at-3.1.10/atd.c 2006-09-12 10:07:19.000000000 +0200 +@@ -316,7 +316,7 @@ + "aborting", jobno, filename); + + if (buf.st_nlink > 2) { +- perr("Someboy is trying to run a linked script for job %8lu (%.500s)", ++ perr("Somebody is trying to run a linked script for job %8lu (%.500s)", + jobno, filename); + } + if ((fflags = fcntl(fd_in, F_GETFD)) < 0) +@@ -607,6 +607,7 @@ + * Let's remove the lockfile and reschedule. + */ + strncpy(lock_name, dirent->d_name, sizeof(lock_name)); ++ lock_name[sizeof(lock_name)-1] = '\0'; + lock_name[0] = '='; + unlink(lock_name); + next_job = now; +@@ -623,7 +624,7 @@ + nothing_to_do = 0; + + /* There's a job for later. Note its execution time if it's +- * the earlierst so far. ++ * the earliest so far. + */ + if (run_time > now) { + if (next_job > run_time) { +@@ -641,6 +642,7 @@ + run_batch++; + if (strcmp(batch_name, dirent->d_name) > 0) { + strncpy(batch_name, dirent->d_name, sizeof(batch_name)); ++ batch_name[sizeof(batch_name)-1] = '\0'; + batch_uid = buf.st_uid; + batch_gid = buf.st_gid; + batch_queue = queue; +@@ -683,7 +685,7 @@ + int + main(int argc, char *argv[]) + { +-/* Browse through ATJOB_DIR, checking all the jobfiles wether they should ++/* Browse through ATJOB_DIR, checking all the jobfiles whether they should + * be executed and or deleted. The queue is coded into the first byte of + * the job filename, the date (in minutes since Eon) as a hex number in the + * following eight bytes, followed by a dot and a serial number. A file diff --git a/at-3.1.10-usage.patch b/at-3.1.10-usage.patch new file mode 100644 index 0000000..ec80818 --- /dev/null +++ b/at-3.1.10-usage.patch @@ -0,0 +1,12 @@ +--- at-3.1.10/panic.c.usage 2006-09-07 13:21:23.000000000 +0200 ++++ at-3.1.10/panic.c 2006-09-07 13:24:00.000000000 +0200 +@@ -92,7 +92,8 @@ + /* Print usage and exit. + */ + fprintf(stderr, "Usage: at [-V] [-q x] [-f file] [-mldbv] time\n" +- " at -c job ...\n" ++ " at [-V] [-q x] [-f file] [-m] -t [[CC]YY]MMDDhhmm\n" ++ " at -c job [job...]\n" + " atq [-V] [-q x]\n" + " atrm [-V] job ...\n" + " batch\n"); diff --git a/at-3.1.12-opt_V.patch b/at-3.1.12-opt_V.patch new file mode 100644 index 0000000..d25148d --- /dev/null +++ b/at-3.1.12-opt_V.patch @@ -0,0 +1,17 @@ +diff -up at-3.1.12/at.c.opt_V at-3.1.12/at.c +--- at-3.1.12/at.c.opt_V 2009-11-23 16:11:52.000000000 +0100 ++++ at-3.1.12/at.c 2009-12-02 13:20:29.770215516 +0100 +@@ -853,10 +853,9 @@ main(int argc, char **argv) + */ + + if (disp_version) { +- fprintf(stderr, "at version " VERSION "\n" +- "Please report bugs to the Debian bug tracking system (http://bugs.debian.org/)\n" +- "or contact the maintainers (at@packages.debian.org).\n"); +- exit(EXIT_SUCCESS); ++ fprintf(stderr, "at version " VERSION "\n"); ++ if (argc == 2) ++ exit(EXIT_SUCCESS); + } + + /* select our program diff --git a/at-3.1.12-selinux.patch b/at-3.1.12-selinux.patch new file mode 100644 index 0000000..8290bc1 --- /dev/null +++ b/at-3.1.12-selinux.patch @@ -0,0 +1,152 @@ +diff -up at-3.1.12/config.h.in.selinux at-3.1.12/config.h.in +--- at-3.1.12/config.h.in.selinux 2009-12-02 16:32:19.469228959 +0100 ++++ at-3.1.12/config.h.in 2009-12-02 16:32:57.706966488 +0100 +@@ -71,6 +71,9 @@ + /* Define if you are building with_pam */ + #undef WITH_PAM + ++/* Define if you are building with_selinux */ ++#undef WITH_SELINUX ++ + /* Define to 1 if you have the `pstat_getdynamic' function. */ + #undef HAVE_PSTAT_GETDYNAMIC + +diff -up at-3.1.12/configure.ac.selinux at-3.1.12/configure.ac +--- at-3.1.12/configure.ac.selinux 2009-12-02 16:31:15.323246019 +0100 ++++ at-3.1.12/configure.ac 2009-12-02 16:32:01.425966844 +0100 +@@ -266,5 +266,13 @@ AC_ARG_WITH(daemon_groupname, + ) + AC_SUBST(DAEMON_GROUPNAME) + ++AC_ARG_WITH(selinux, ++[ --with-selinux Define to run with selinux], ++AC_DEFINE(WITH_SELINUX), ++) ++AC_CHECK_LIB(selinux, is_selinux_enabled, SELINUXLIB=-lselinux) ++AC_SUBST(SELINUXLIB) ++AC_SUBST(WITH_SELINUX) ++ + AC_CONFIG_FILES(Makefile atrun atd.8 atrun.8 at.1 batch) + AC_OUTPUT +diff -up at-3.1.12/Makefile.in.selinux at-3.1.12/Makefile.in +--- at-3.1.12/Makefile.in.selinux 2009-12-02 16:30:11.923216529 +0100 ++++ at-3.1.12/Makefile.in 2009-12-02 16:30:57.949215706 +0100 +@@ -39,6 +39,7 @@ LIBS = @LIBS@ + LIBOBJS = @LIBOBJS@ + INSTALL = @INSTALL@ + PAMLIB = @PAMLIB@ ++SELINUXLIB = @SELINUXLIB@ + + CLONES = atq atrm + ATOBJECTS = at.o panic.o perm.o posixtm.o y.tab.o lex.yy.o +diff -up at-3.1.12/atd.c.selinux at-3.1.12/atd.c +--- at-3.1.12/atd.c.selinux 2009-12-03 13:03:57.182284669 +0100 ++++ at-3.1.12/atd.c 2009-12-03 13:07:20.542272874 +0100 +@@ -83,6 +83,14 @@ + #include "getloadavg.h" + #endif + ++#ifdef WITH_SELINUX ++#include ++#include ++int selinux_enabled=0; ++#include ++#include ++#endif ++ + #ifndef LOG_ATD + #define LOG_ATD LOG_DAEMON + #endif +@@ -202,6 +210,68 @@ myfork() + #define ATD_MAIL_NAME "mailx" + #endif + ++#ifdef WITH_SELINUX ++static int set_selinux_context(const char *name, const char *filename) { ++ security_context_t user_context=NULL; ++ security_context_t file_context=NULL; ++ struct av_decision avd; ++ int retval=-1; ++ char *seuser=NULL; ++ char *level=NULL; ++ ++ if (getseuserbyname(name, &seuser, &level) == 0) { ++ retval=get_default_context_with_level(seuser, level, NULL, &user_context); ++ free(seuser); ++ free(level); ++ if (retval) { ++ if (security_getenforce()==1) { ++ perr("execle: couldn't get security context for user %s\n", name); ++ } else { ++ syslog(LOG_ERR, "execle: couldn't get security context for user %s\n", name); ++ return -1; ++ } ++ } ++ } ++ ++ /* ++ * 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. ++ */ ++ if (fgetfilecon(STDIN_FILENO, &file_context) < 0) ++ perr("fgetfilecon FAILED %s", filename); ++ ++ retval = security_compute_av(user_context, ++ file_context, ++ SECCLASS_FILE, ++ FILE__ENTRYPOINT, ++ &avd); ++ freecon(file_context); ++ if (retval || ((FILE__ENTRYPOINT & avd.allowed) != FILE__ENTRYPOINT)) { ++ if (security_getenforce()==1) { ++ perr("Not allowed to set exec context to %s for user %s\n", user_context,name); ++ } else { ++ syslog(LOG_ERR, "Not allowed to set exec context to %s for user %s\n", user_context,name); ++ retval = -1; ++ goto err; ++ } ++ } ++ if (setexeccon(user_context) < 0) { ++ if (security_getenforce()==1) { ++ perr("Could not set exec context to %s for user %s\n", user_context,name); ++ retval = -1; ++ } else { ++ syslog(LOG_ERR, "Could not set exec context to %s for user %s\n", user_context,name); ++ } ++ } ++ err: ++ freecon(user_context); ++ return 0; ++} ++#endif ++ + static void + run_file(const char *filename, uid_t uid, gid_t gid) + { +@@ -452,6 +522,12 @@ run_file(const char *filename, uid_t uid + perr("Cannot reset signal handler to default"); + + chdir("/"); ++#ifdef WITH_SELINUX ++ if (selinux_enabled > 0) { ++ if (set_selinux_context(pentry->pw_name, filename) < 0) ++ perr("SELinux Failed to set context\n"); ++ } ++#endif + + if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) + perr("Exec failed for /bin/sh"); +@@ -774,6 +850,10 @@ main(int argc, char *argv[]) + struct passwd *pwe; + struct group *ge; + ++#ifdef WITH_SELINUX ++ selinux_enabled=is_selinux_enabled(); ++#endif ++ + /* We don't need root privileges all the time; running under uid and gid + * daemon is fine. + */ diff --git a/at-3.1.12-shell.patch b/at-3.1.12-shell.patch new file mode 100644 index 0000000..891fa97 --- /dev/null +++ b/at-3.1.12-shell.patch @@ -0,0 +1,55 @@ +diff -up at-3.1.12/at.c.shell at-3.1.12/at.c +--- at-3.1.12/at.c.shell 2009-12-02 13:25:12.706989310 +0100 ++++ at-3.1.12/at.c 2009-12-02 13:26:01.991966200 +0100 +@@ -62,11 +62,8 @@ + #include + #include + +-#ifdef TM_IN_SYS_TIME + #include +-#else + #include +-#endif + + #ifdef HAVE_UNISTD_H + #include +@@ -244,6 +241,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 +@@ -461,6 +464,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> "); +@@ -477,6 +483,7 @@ writefile(time_t runtimer, char queue) + fprintf(stderr, "\n"); + } + fprintf(fp, "\n"); ++ fprintf(fp, "marcinDELIMITER%08lx\n", i); + if (ferror(fp)) + panic("Output error"); + +@@ -926,7 +933,7 @@ main(int argc, char **argv) + It also alows a warning diagnostic to be printed. Because of the + possible variance, we always output the diagnostic. */ + +- fprintf(stderr, "warning: commands will be executed using /bin/sh\n"); ++ //fprintf(stderr, "warning: commands will be executed using /bin/sh\n"); + + writefile(timer, queue); + break; diff --git a/at-3.1.13-makefile.patch b/at-3.1.13-makefile.patch new file mode 100644 index 0000000..e429719 --- /dev/null +++ b/at-3.1.13-makefile.patch @@ -0,0 +1,82 @@ +diff -up at-3.1.13/Makefile.in.make at-3.1.13/Makefile.in +--- at-3.1.13/Makefile.in.make 2011-06-25 14:43:14.000000000 +0200 ++++ at-3.1.13/Makefile.in 2011-07-29 08:06:28.317600053 +0200 +@@ -65,13 +65,13 @@ LIST = Filelist Filelist.asc + all: at atd atrun + + at: $(ATOBJECTS) +- $(CC) $(CFLAGS) -o at $(ATOBJECTS) $(LIBS) $(LEXLIB) ++ $(CC) $(CFLAGS) -o at -pie $(ATOBJECTS) $(LIBS) $(LEXLIB) $(SELINUXLIB) $(PAMLIB) + rm -f $(CLONES) + $(LN_S) -f at atq + $(LN_S) -f at atrm + + atd: $(RUNOBJECTS) +- $(CC) $(CFLAGS) -o atd $(RUNOBJECTS) $(LIBS) $(PAMLIB) ++ $(CC) $(CFLAGS) -o atd -pie $(RUNOBJECTS) $(LIBS) $(SELINUXLIB) $(PAMLIB) + + y.tab.c y.tab.h: parsetime.y + $(YACC) -d parsetime.y +@@ -83,38 +83,41 @@ atrun: atrun.in + configure + + .c.o: +- $(CC) -c $(CFLAGS) $(DEFS) $*.c ++ $(CC) -c $(CFLAGS) -fPIE $(DEFS) $*.c + + install: all +- $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(etcdir) +- $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(bindir) +- $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(sbindir) +- $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(docdir) +- $(INSTALL) -g root -o root -m 755 -d $(IROOT)$(atdocdir) +- $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 755 -d $(IROOT)$(ATSPOOL_DIR) $(IROOT)$(ATJOB_DIR) +- chmod 1770 $(IROOT)$(ATSPOOL_DIR) $(IROOT)$(ATJOB_DIR) ++ $(INSTALL) -m 755 -d $(IROOT)$(etcdir) ++ $(INSTALL) -m 755 -d $(IROOT)$(bindir) ++ $(INSTALL) -m 755 -d $(IROOT)$(sbindir) ++ $(INSTALL) -m 755 -d $(IROOT)$(docdir) ++ $(INSTALL) -m 755 -d $(IROOT)$(atdocdir) ++ $(INSTALL) -m 755 -d $(IROOT)$(etcdir)/pam.d/ ++ $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 755 -d $(IROOT)$(ATSPOOL_DIR) ++ chmod 700 $(IROOT)$(ATJOB_DIR) $(IROOT)$(ATSPOOL_DIR) ++ chown $(DAEMON_USERNAME):$(DAEMON_GROUPNAME) $(IROOT)$(ATJOB_DIR) $(IROOT)$(ATSPOOL_DIR) + touch $(IROOT)$(LFILE) + chmod 600 $(IROOT)$(LFILE) + chown $(DAEMON_USERNAME):$(DAEMON_GROUPNAME) $(IROOT)$(LFILE) +- test -f $(IROOT)$(etcdir)/at.allow || test -f $(IROOT)$(etcdir)/at.deny || $(INSTALL) -o root -g $(DAEMON_GROUPNAME) -m 640 at.deny $(IROOT)$(etcdir)/ +- $(INSTALL) -g $(DAEMON_GROUPNAME) -o $(DAEMON_USERNAME) -m 6755 at $(IROOT)$(bindir) ++ test -f $(IROOT)$(etcdir)/at.allow || test -f $(IROOT)$(etcdir)/at.deny || $(INSTALL) -m 600 at.deny $(IROOT)$(etcdir)/ ++ $(INSTALL) -o $(INSTALL_ROOT_USER) -g $(DAEMON_GROUPNAME) pam_atd $(IROOT)$(etcdir)/pam.d/atd ++ $(INSTALL) -m 4755 at $(IROOT)$(bindir) + $(LN_S) -f at $(IROOT)$(bindir)/atq + $(LN_S) -f at $(IROOT)$(bindir)/atrm +- $(INSTALL) -g root -o root -m 755 batch $(IROOT)$(bindir) +- $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man1dir) +- $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man5dir) +- $(INSTALL) -d -o root -g root -m 755 $(IROOT)$(man8dir) +- $(INSTALL) -g root -o root -m 755 atd $(IROOT)$(sbindir) +- $(INSTALL) -g root -o root -m 755 atrun $(IROOT)$(sbindir) +- $(INSTALL) -g root -o root -m 644 at.1 $(IROOT)$(man1dir)/ ++ $(INSTALL) -m 755 batch $(IROOT)$(bindir) ++ $(INSTALL) -d -m 755 $(IROOT)$(man1dir) ++ $(INSTALL) -d -m 755 $(IROOT)$(man5dir) ++ $(INSTALL) -d -m 755 $(IROOT)$(man8dir) ++ $(INSTALL) -m 755 atd $(IROOT)$(sbindir) ++ $(INSTALL) -m 755 atrun $(IROOT)$(sbindir) ++ $(INSTALL) -m 644 at.1 $(IROOT)$(man1dir)/ + cd $(IROOT)$(man1dir) && $(LN_S) -f at.1 atq.1 && $(LN_S) -f at.1 batch.1 && $(LN_S) -f at.1 atrm.1 +- $(INSTALL) -g root -o root -m 644 atd.8 $(IROOT)$(man8dir)/ ++ $(INSTALL) -m 644 atd.8 $(IROOT)$(man8dir)/ + sed "s,\$${exec_prefix},$(exec_prefix),g" tmpman +- $(INSTALL) -g root -o root -m 644 tmpman $(IROOT)$(man8dir)/atrun.8 ++ $(INSTALL) -m 644 tmpman $(IROOT)$(man8dir)/atrun.8 + rm -f tmpman +- $(INSTALL) -g root -o root -m 644 at.allow.5 $(IROOT)$(man5dir)/ ++ $(INSTALL) -m 644 at.allow.5 $(IROOT)$(man5dir)/ + cd $(IROOT)$(man5dir) && $(LN_S) -f at.allow.5 at.deny.5 +- $(INSTALL) -g root -o root -m 644 $(DOCS) $(IROOT)$(atdocdir) ++ $(INSTALL) -m 644 $(DOCS) $(IROOT)$(atdocdir) + rm -f $(IROOT)$(mandir)/cat1/at.1* $(IROOT)$(mandir)/cat1/batch.1* \ + $(IROOT)$(mandir)/cat1/atq.1* + rm -f $(IROOT)$(mandir)/cat1/atd.8* diff --git a/at-3.1.13-pam.patch b/at-3.1.13-pam.patch new file mode 100644 index 0000000..e4cae11 --- /dev/null +++ b/at-3.1.13-pam.patch @@ -0,0 +1,430 @@ +diff -up at-3.1.13/at.c.pam at-3.1.13/at.c +--- at-3.1.13/at.c.pam 2011-07-29 13:51:50.234127938 +0200 ++++ at-3.1.13/at.c 2011-07-29 13:51:50.245127883 +0200 +@@ -141,18 +141,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); + } +@@ -318,26 +313,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. + */ +@@ -661,7 +649,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"); +@@ -670,8 +658,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; +@@ -681,7 +669,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) { +@@ -694,7 +682,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.13/atd.c.pam at-3.1.13/atd.c +--- at-3.1.13/atd.c.pam 2011-07-29 13:51:50.240127908 +0200 ++++ at-3.1.13/atd.c 2011-07-29 13:54:35.805384873 +0200 +@@ -111,7 +111,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; +@@ -120,15 +120,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 +@@ -235,7 +227,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 + +@@ -395,17 +387,10 @@ 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 ++ PAM_HANDLING; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); + #endif + + close(STDIN_FILENO); +@@ -419,7 +404,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. + */ +@@ -438,8 +430,6 @@ run_file(const char *filename, uid_t uid + close(fd_in); + close(fd_out); + +- PRIV_START +- + nice((tolower((int) queue) - 'a' + 1) * 2); + + if (initgroups(pentry->pw_name, pentry->pw_gid)) +@@ -458,7 +448,16 @@ run_file(const char *filename, uid_t uid + + if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) + perr("Exec failed for /bin/sh"); +- ++#ifdef WITH_PAM ++ if ( ( nenvp != &nul ) && (pam_envp != 0L) && (*pam_envp != 0L)) ++ { ++ for( nenvp = pam_envp; *nenvp != 0L; nenvp++) ++ free(*nenvp); ++ free( pam_envp ); ++ nenvp = &nul; ++ pam_envp=0L; ++ } ++#endif + PRIV_END + } + /* We're the parent. Let's wait. +@@ -471,14 +470,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. + */ +@@ -509,8 +500,19 @@ 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; ++#ifdef WITH_PAM ++ PAM_HANDLING; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); ++#endif ++ ++ mail_pid = fork(); + ++ if ( mail_pid == 0 ) ++ { + PRIV_START + + if (initgroups(pentry->pw_name, pentry->pw_gid)) +@@ -535,7 +537,23 @@ run_file(const char *filename, uid_t uid + perr("Exec failed for mail command"); + + PRIV_END ++ } ++ else if ( mail_pid == -1 ) { ++ perr("fork of mailer failed"); ++ } ++ else { ++ /* Parent */ ++ waitpid(mail_pid, (int *) NULL, 0); ++ } ++#ifdef WITH_PAM ++ pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT ); ++ pam_close_session(pamh, PAM_SILENT); ++ pam_end(pamh, PAM_ABORT); ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); ++#endif + } ++#endif + exit(EXIT_SUCCESS); + } + +diff -up at-3.1.13/config.h.in.pam at-3.1.13/config.h.in +--- at-3.1.13/config.h.in.pam 2011-06-25 14:43:14.000000000 +0200 ++++ at-3.1.13/config.h.in 2011-07-29 13:51:50.246127878 +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.13/configure.ac.pam at-3.1.13/configure.ac +--- at-3.1.13/configure.ac.pam 2011-06-25 14:43:14.000000000 +0200 ++++ at-3.1.13/configure.ac 2011-07-29 13:51:50.247127873 +0200 +@@ -84,7 +84,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 +@@ -238,6 +238,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_MSG_CHECKING(groupname to run under) + AC_ARG_WITH(daemon_groupname, + [ --with-daemon_groupname=DAEMON_GROUPNAME Groupname to run under (default daemon) ], +diff -up at-3.1.13/perm.c.pam at-3.1.13/perm.c +--- at-3.1.13/perm.c.pam 2011-06-25 14:43:14.000000000 +0200 ++++ at-3.1.13/perm.c 2011-07-29 13:51:50.248127868 +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,51 @@ 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); ++ } ++ ++ pam_close_session(pamh,PAM_SILENT); ++ ++ PAM_HANDLING; ++ ++ pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT ); ++ pam_close_session(pamh,PAM_SILENT); ++ pam_end(pamh, PAM_ABORT); ++ ++ 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.13/privs.h.pam at-3.1.13/privs.h +--- at-3.1.13/privs.h.pam 2011-06-25 14:43:14.000000000 +0200 ++++ at-3.1.13/privs.h 2011-07-29 13:51:50.248127868 +0200 +@@ -144,3 +144,61 @@ 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) \ ++ ++/* PAM - check after every operation whether they passed */ ++#define PAM_HANDLING \ ++ do { pamh = NULL; \ ++ 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; \ ++ retcode = pam_open_session(pamh, PAM_SILENT); \ ++ PAM_FAIL_CHECK; \ ++ retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); \ ++ PAM_SESSION_FAIL; \ ++ PAM_FAIL_CHECK; \ ++ } while (0) ++ ++/* OLD FAIL_CHECK ONLY FOR perm.c ++ * define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ ++ * fprintf(stderr,"\nPAM failure %s\n",pam_strerror(pamh, retcode)); \ ++ * syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ ++ * if (pamh) \ ++ * pam_end(pamh, retcode); \ ++ * exit(1); \ ++ * } ++ */ ++ ++#endif ++ diff --git a/at-3.1.7-lockfile.patch b/at-3.1.7-lockfile.patch new file mode 100644 index 0000000..8788a1a --- /dev/null +++ b/at-3.1.7-lockfile.patch @@ -0,0 +1,11 @@ +--- at-3.1.7/at.c.lockfile Wed Oct 22 17:56:22 1997 ++++ at-3.1.7/at.c Wed Oct 22 17:56:40 1997 +@@ -199,7 +199,7 @@ + fscanf(fid, "%5lx", &jobno); + rewind(fid); + } else { +- fid = fopen(ATJOB_DIR "/.SEQ", "w"); ++ fid = fopen(LFILE, "w"); + if (fid == NULL) + return EOF; + } diff --git a/at-3.1.7-sigchld.patch b/at-3.1.7-sigchld.patch new file mode 100644 index 0000000..5a5c0b5 --- /dev/null +++ b/at-3.1.7-sigchld.patch @@ -0,0 +1,12 @@ +--- at-3.1.7/atd.c.sigchld Mon May 24 16:56:41 1999 ++++ at-3.1.7/atd.c Mon May 24 16:58:57 1999 +@@ -348,6 +348,9 @@ + if (setuid(uid) < 0) + perr("Cannot set user id"); + ++ if (SIG_ERR == signal(SIGCHLD, SIG_DFL)) ++ perr("Cannot reset signal handler to default"); ++ + chdir("/"); + + if (execle("/bin/sh", "sh", (char *) NULL, nenvp) != 0) diff --git a/at-3.1.8-perr.patch b/at-3.1.8-perr.patch new file mode 100644 index 0000000..3ef7e23 --- /dev/null +++ b/at-3.1.8-perr.patch @@ -0,0 +1,12 @@ +--- at-3.1.8/atd.c.org 2002-07-19 16:20:24.000000000 +0900 ++++ at-3.1.8/atd.c 2002-07-19 16:22:30.000000000 +0900 +@@ -453,7 +453,8 @@ + #elif defined(MAILX) + execl(MAILX, "mailx", mailname, (char *) NULL); + #else +-#error "No mail command specified." ++/*#error "No mail command specified."*/ ++ perr("No mail command specified."); + #endif + perr("Exec failed for mail command"); + diff --git a/at-3.1.8-t_option.patch b/at-3.1.8-t_option.patch new file mode 100644 index 0000000..0bd4e8e --- /dev/null +++ b/at-3.1.8-t_option.patch @@ -0,0 +1,139 @@ +--- at-3.1.10/at.1.in.t_opti 2006-09-12 12:48:04.000000000 +0200 ++++ at-3.1.10/at.1.in 2006-09-12 12:45:40.000000000 +0200 +@@ -12,6 +12,16 @@ + .RB [ -mldbv ] + .B TIME + .br ++.B at ++.RB [ -V ] ++.RB [ -q ++.IR queue ] ++.RB [ -f ++.IR file ] ++.RB [ -mldbv ] ++.RB -t ++.IR time_arg ++.br + .B "at -c" + .I job + .RI [ job... ] +@@ -227,6 +237,15 @@ + .B + \-c + cats the jobs listed on the command line to standard output. ++.TP ++.BI \-t " time_arg" ++Submit the job to be run at the time specified by the ++.BI time_arg ++option argument, which must have the same format as specified for the ++.BR touch(1) ++utility's ++.B -t ++time option argument ([[CC]YY]MMDDhhmm). + .SH FILES + .I @ATJBD@ + .br +--- at-3.1.10/at.c.t_ 2006-09-12 10:15:56.000000000 +0200 ++++ at-3.1.10/at.c 2006-09-12 10:30:17.000000000 +0200 +@@ -750,6 +750,101 @@ + return p; + } + ++/* Handle POSIX.2 '-t' option : ++ * Parses time string in "touch(1)" format: ++ * [[CC]YY]MMDDhhmm[.ss] ++ * and returns time_t . ++ */ ++time_t ++t_option(char *s) ++{ ++ time_t t=time(0L); ++ struct tm tm, tm_now=*localtime(&t); ++ int l; ++ ++ if((s == 0L) || (*s == '\0')) ++ { ++ return 0L; ++ }; ++ memset(&tm,'\0',sizeof(tm)); ++ l = strnlen(s,15); ++ switch(l) ++ { ++ case 15: ++ /* CCYYMMDDhhmm.ss */ ++ sscanf(s, "%4d%2d%2d%2d%2d.%2d", ++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec ++ ); ++ if(tm.tm_year) ++ tm.tm_year -= 1900 ; ++ ++ break; ++ ++ case 13: ++ /* YYMMDDhhmm.ss */ ++ sscanf(s, "%2d%2d%2d%2d%2d.%2d", ++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec ++ ); ++ if(tm.tm_year) ++ tm.tm_year += 100 ; /* Y2.1K+ bug! */ ++ ++ break; ++ ++ case 11: ++ /* MMDDhhmm.ss */ ++ sscanf(s, "%2d%2d%2d%2d.%2d", ++ &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec ++ ); ++ ++ tm.tm_year = tm_now.tm_year; ++ ++ if(tm.tm_mon) ++ tm.tm_mon -= 1; ++ break; ++ ++ case 12: ++ /* CCYYMMDDhhmm */ ++ sscanf(s, "%4d%2d%2d%2d%2d", ++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min ++ ); ++ if(tm.tm_year) ++ tm.tm_year -= 1900 ; ++ break; ++ ++ case 10: ++ /* YYMMDDhhmm */ ++ sscanf(s, "%2d%2d%2d%2d%2d", ++ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min ++ ); ++ if(tm.tm_year) ++ tm.tm_year += 100 ; /* Y2.1K+ bug! */ ++ break; ++ ++ case 8: ++ /* MMDDhhmm */ ++ sscanf(s, "%2d%2d%2d%2d", ++ &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min ++ ); ++ if( tm.tm_mday ) ++ tm.tm_year = tm_now.tm_year; ++ break; ++ default: ++ break; ++ } ++ ++ if( tm.tm_mon ) ++ tm.tm_mon -= 1; ++ ++ if( tm.tm_mday ) ++ { ++ tm.tm_isdst = tm_now.tm_isdst; ++ return mktime(&tm); ++ } else ++ return 0L; ++} ++ ++ ++ + int + main(int argc, char **argv) + { diff --git a/at-3.1.8-test.patch b/at-3.1.8-test.patch new file mode 100644 index 0000000..f5bc2b8 --- /dev/null +++ b/at-3.1.8-test.patch @@ -0,0 +1,23 @@ +diff -u at-3.1.8/Makefile.in.orig at-3.1.8/Makefile.in +--- at-3.1.8/Makefile.in.orig Wed Feb 14 13:59:09 2001 ++++ at-3.1.8/Makefile.in Mon Feb 19 14:48:42 2001 +@@ -51,6 +51,8 @@ + + OTHERS = parsetime.l parsetime.y + ++TEST_VERBOSE = 0 ++ + DOCS = Problems Copyright README ChangeLog timespec + + MISC = COPYING Makefile.in configure acconfig.h install-sh \ +@@ -150,6 +152,9 @@ + + parsetest: lex.yy.c y.tab.c + $(CC) -o parsetest $(CFLAGS) $(DEFS) -DTEST_PARSER -DNEED_YYWRAP lex.yy.c y.tab.c ++ ++test: parsetest ++ PERL_DL_NONLAZY=1 perl -e 'use Test::Harness qw(&runtests $$verbose); $$verbose=$(TEST_VERBOSE); runtests @ARGV;' test.pl + + .depend: $(CSRCS) + gcc $(CFLAGS) $(DEFS) -MM $(CSRCS) > .depend +diff -u at-3.1.8/test.pl.orig at-3.1.8/test.pl diff --git a/at.spec b/at.spec new file mode 100644 index 0000000..f6bb187 --- /dev/null +++ b/at.spec @@ -0,0 +1,191 @@ +%define atdaemon_groupid 65022 +%define atdaemon_userid 65022 + +Name: at +Version: 3.1.14 +Release: 1mamba +Summary: Job spooling tools +Group: System/Servers +Vendor: openmamba +Distribution: openmamba +Packager: Silvan Calarco +URL: http://ftp.debian.org/debian/pool/main/a/at/ +Source0: http://ftp.debian.org/debian/pool/main/a/at/at_%{version}.orig.tar.gz +Source1: atd-initscript +Source2: atd-pam +# patches stolen from fedora +Patch0: %{name}-3.1.7-lockfile.patch +Patch1: %{name}-3.1.10-man-timespec-path.patch +Patch2: %{name}-3.1.7-sigchld.patch +Patch3: %{name}-3.1.10-typo.patch +Patch4: %{name}-3.1.10-lexer-parser.patch +Patch5: %{name}-3.1.8-test.patch +Patch6: %{name}-3.1.8-perr.patch +Patch7: %{name}-3.1.10-instinet.patch +Patch8: %{name}-3.1.12-shell.patch +Patch9: %{name}-3.1.10-pie.patch +Patch10: %{name}-3.1.8-t_option.patch +Patch11: %{name}-3.1.10-usage.patch +Patch12: %{name}-3.1.10-fix_no_export.patch +Patch13: %{name}-3.1.10-dont_fork.patch +Patch14: %{name}-3.1.13-pam.patch +Patch15: %{name}-3.1.13-makefile.patch +Patch16: %{name}-3.1.10-daylight.patch +Patch17: %{name}-3.1.10-perm.patch +Patch18: %{name}-3.1.12-opt_V.patch +Patch19: %{name}-3.1.12-selinux.patch +License: GPL +## AUTOBUILDREQ-BEGIN +BuildRequires: glibc-devel +BuildRequires: pam-devel +## AUTOBUILDREQ-END +BuildRequires: libselinux-devel +BuildRequires: systemd-devel +BuildRoot: %{_tmppath}/%{name}-%{version}-root + +%description +At and batch read commands from standard input or from a specified file. +At allows you to specify that a command will be run at a particular time (now or a specified time in the future). +Batch will execute commands when the system load levels drop to a particular level. +Both commands use /bin/sh to run the commands. + +%prep +%setup -q +#%patch0 -p1 -b .lockfile +#%patch1 -p1 -b .paths +%patch2 -p1 -b .sigchld +#%patch3 -p1 -b .typo +%patch4 -p1 -b .lexer +%patch5 -p1 -b .test +%patch6 -p1 -b .perr +#%patch7 -p1 -b .instinet +%patch8 -p1 -b .shell +#%patch9 -p1 -b .pie +%patch10 -p1 -b .t_option +#%patch11 -p1 -b .usage +%patch12 -p1 -b .fix_no_export +#%patch13 -p1 -b .dont_fork +%patch14 -p1 -b .pam +#%patch15 -p1 -b .makefile +%patch16 -p1 -b .daylight +#%patch17 -p1 -b .perm +%patch18 -p1 -b .opt_V +#%patch19 -p1 -b .selinux + +install %{SOURCE2} pam_atd + +sed -i "s|-g root||g" Makefile.in +sed -i "s|-o root||g" Makefile.in + +%build +# for patch2 +rm -f lex.yy.* y.tab.* +sed -i "s|/var/run|/run|g" configure + +%configure \ + --with-atspool=%{_localstatedir}/spool/at/spool \ + --with-jobdir=%{_localstatedir}/spool/at \ + --with-daemon_username=atdaemon \ + --with-daemon_groupname=atdaemon \ + --with-systemdsystemunitdir=%_unitdir + +%make -j1 +#LANG=C make test >/dev/null + +%install +[ "%{buildroot}" != / ] && rm -rf "%{buildroot}" + +%makeinstall \ + IROOT=%{buildroot} \ + DAEMON_USERNAME=`id -nu` \ + DAEMON_GROUPNAME=`id -ng` \ + INSTALL_ROOT_USER=`id -nu` \ + INSTALL_ROOT_GROUP=`id -nu` \ + docdir=%{_docdir} + +# prefix=%{buildroot}%{_prefix} \ +# bindir=%{buildroot}%{_bindir} \ +# sbindir=%{buildroot}%{_sbindir} \ +# etcdir=%{buildroot}%{_sysconfdir} \ +# mandir=%{buildroot}%{_mandir} \ +# ATJOB_DIR=%{buildroot}%{_localstatedir}/spool/at \ +# ATSPOOL_DIR=%{buildroot}%{_localstatedir}/spool/at/spool \ +#install -D -m 0755 %{S:1} %{buildroot}%{_initrddir}/atd + +%clean +[ "%{buildroot}" != / ] && rm -rf "%{buildroot}" + +%pre +groupadd -g %{atdaemon_groupid} atdaemon 2>/dev/null +useradd -u %{atdaemon_userid} -g atdaemon -c "at daemon" \ + -d /var/empty -s /bin/false atdaemon 2>/dev/null +exit 0 + +%post +touch %{_localstatedir}/spool/at/.SEQ +chmod 600 %{_localstatedir}/spool/at/.SEQ +chown atdaemon:atdaemon %{_localstatedir}/spool/at/.SEQ +#/sbin/chkconfig --add atd +exit 0 + +%preun +if [ $1 -eq 0 ]; then + service atd stop >/dev/null 2>&1 + /sbin/chkconfig --del atd + userdel atdaemon >/dev/null 2>&1 +fi +exit 0 + +%postun +if [ $1 -eq 1 ]; then + service atd condrestart >/dev/null 2>&1 +fi +exit 0 + +%files +%defattr(-,root,root) +%attr(4755,root,root) %{_bindir}/at +%config(noreplace) %{_sysconfdir}/at.deny +#%attr(0640,root,atdaemon) %config(noreplace) /etc/pam.d/atd +%{_bindir}/batch +%{_bindir}/atrm +%{_bindir}/atq +%{_sbindir}/atrun +%{_sbindir}/atd +/lib/systemd/system/atd.service +%attr(0700,atdaemon,atdaemon) %dir %{_localstatedir}/spool/at +%attr(0700,atdaemon,atdaemon) %dir %{_localstatedir}/spool/at/spool +%ghost %{_localstatedir}/spool/at/.SEQ +%{_mandir}/man1/* +%{_mandir}/man5/* +%{_mandir}/man8/* +%dir %{_docdir}/at +%{_docdir}/at + +%changelog +* Thu Oct 10 2013 Automatic Build System 3.1.14-1mamba +- automatic update by autodist + +* Sat Sep 10 2011 Automatic Build System 3.1.13-1mamba +- automatic version update by autodist + +* Sun Dec 27 2009 Silvan Calarco 3.1.12-1mamba +- update to 3.1.12 + +* Mon Aug 25 2008 Aleph0 3.1.10-3mamba +- remove broken symlink to debian copyright + +* Mon Jul 02 2007 Silvan Calarco 3.1.10-2mamba +- fix pam file for new release + +* Wed Jan 17 2007 Davide Madrisan 3.1.10-1qilnx +- update to version 3.1.10 by autospec +- added missing build requirements +- fixed permissions of at binary +- enabled support for pam +- use the service script to start/restart/stop the atd service +- fixed owner and group for /var/spool/at/.SEQ +- modified homedir for atd daemon: now is /var/empty + +* Thu Apr 29 2004 Silvan Calarco 3.1.8-1qilnx +- first build diff --git a/atd-initscript b/atd-initscript new file mode 100644 index 0000000..e8fcba6 --- /dev/null +++ b/atd-initscript @@ -0,0 +1,85 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/atd +# +# Starts the at daemon +# +# chkconfig: 345 40 60 +# description: Runs commands scheduled by the at command at the time \ +# specified when at was run, and runs batch commands when the load \ +# average is low enough. +# processname: atd + +# Source function library. +. /etc/init.d/functions + +test -x /usr/sbin/atd || exit 0 + +RETVAL=0 + +# +# See how we were called. +# + +prog="atd" + +start() { + # Check if atd is already running + if [ ! -f /var/lock/subsys/atd ]; then + echo -n $"Starting $prog: " + daemon /usr/sbin/atd + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/atd + echo + fi + return $RETVAL +} + +stop() { + echo -n $"Stopping $prog: " + killproc /usr/sbin/atd + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/atd + echo + return $RETVAL +} + + +restart() { + stop + start +} + +reload() { + restart +} + +status_at() { + status /usr/sbin/atd +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +reload|restart) + restart + ;; +condrestart) + if [ -f /var/lock/subsys/atd ]; then + restart + fi + ;; +status) + status_at + ;; +*) + echo $"Usage: $0 {start|stop|restart|condrestart|status}" + exit 1 +esac + +exit $? +exit $RETVAL diff --git a/atd-pam b/atd-pam new file mode 100644 index 0000000..3abc88e --- /dev/null +++ b/atd-pam @@ -0,0 +1,13 @@ +# +# The PAM configuration file for the at daemon +# +# +auth sufficient pam_rootok.so +auth include system-auth +auth required pam_env.so +account include system-auth +session include system-auth +# Sets up user limits, please uncomment and read /etc/security/limits.conf +# to enable this functionality. +# session required pam_limits.so +#