Sync atrun with FreeBSD:
authorPeter Avalos <pavalos@theshell.com>
Sat, 3 Jan 2009 23:58:03 +0000 (18:58 -0500)
committerPeter Avalos <pavalos@theshell.com>
Sun, 4 Jan 2009 03:12:25 +0000 (22:12 -0500)
-Add PAM support.

-Remove non-Dragonfly code.

-Use strlcpy().

-Use setusercontext to get login.conf settings.

etc/pam.d/Makefile
etc/pam.d/atrun [new file with mode: 0644]
libexec/atrun/Makefile
libexec/atrun/atrun.c
libexec/atrun/atrun.man
libexec/atrun/gloadavg.c

index 9eb738a..6905d0b 100644 (file)
@@ -1,6 +1,7 @@
 NOOBJ=
 
 FILES= README \
+       atrun \
        ftpd \
        gdm \
        imap \
diff --git a/etc/pam.d/atrun b/etc/pam.d/atrun
new file mode 100644 (file)
index 0000000..752e8f1
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# $FreeBSD: src/etc/pam.d/atrun,v 1.1 2007/06/15 12:02:16 yar Exp $
+#
+# PAM configuration for the "atrun" service
+#
+
+# Note well: enabling pam_nologin for atrun will currently result
+# in jobs discarded, not just delayed, during a no-login period.
+#account       required        pam_nologin.so
+account                required        pam_unix.so
index 569d181..33037ec 100644 (file)
@@ -1,4 +1,4 @@
-# $FreeBSD: src/libexec/atrun/Makefile,v 1.7.2.2 2001/04/25 10:40:47 ru Exp $
+# $FreeBSD: src/libexec/atrun/Makefile,v 1.15 2007/10/01 18:17:23 ru Exp $
 # $DragonFly: src/libexec/atrun/Makefile,v 1.3 2004/09/20 19:32:19 joerg Exp $
 
 MAINSRC=${.CURDIR}/../../usr.bin/at
@@ -14,6 +14,11 @@ BINDIR=      ${ATLIB_DIR}
 CLEANFILES= ${MAN}
 
 CFLAGS+=-I${MAINSRC} -I${.CURDIR}
+CFLAGS+=-DLOGIN_CAP -DPAM
+WFORMAT=0
+
+DPADD= ${LIBPAM} ${LIBUTIL}
+LDADD= ${MINUSLPAM} -lutil
 
 atrun.8: atrun.man
        @${ECHO} Making ${.TARGET:T} from ${.ALLSRC:T}; \
index e2c486d..b1d631b 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/libexec/atrun/atrun.c,v 1.14.2.1 2001/03/05 10:53:23 kris Exp $
+ * $FreeBSD: src/libexec/atrun/atrun.c,v 1.26 2007/06/15 12:02:16 yar Exp $
  * $DragonFly: src/libexec/atrun/atrun.c,v 1.5 2004/09/20 19:32:19 joerg Exp $
  */
 
@@ -39,6 +39,7 @@
 #include <grp.h>
 #include <pwd.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <utmp.h>
 #include <paths.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+#ifdef PAM
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+#endif
 
 #if (MAXLOGNAME-1) > UT_NAMESIZE
 #define LOGNAMESIZE UT_NAMESIZE
@@ -79,9 +87,11 @@ gid_t real_gid, effective_gid;
 
 /* File scope variables */
 
+static const char * const atrun = "atrun"; /* service name for syslog etc. */
 static int debug = 0;
 
-void           perr(const char *a);
+void           perr(const char *fmt, ...);
+void           perrx(const char *fmt, ...);
 static void    usage(void);
 
 /* Local functions */
@@ -109,7 +119,7 @@ myfork(void)
 static void
 run_file(const char *filename, uid_t uid, gid_t gid)
 {
-/* Run a file by by spawning off a process which redirects I/O,
+/* Run a file by spawning off a process which redirects I/O,
  * spawns a subshell, then waits for it to complete and sends
  * mail to the user.
  */
@@ -126,7 +136,14 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     int fflags;
     uid_t nuid;
     gid_t ngid;
-
+#ifdef PAM
+    pam_handle_t *pamh = NULL;
+    int pam_err;
+    struct pam_conv pamc = {
+       .conv = openpam_nullconv,
+       .appdata_ptr = NULL
+    };
+#endif
 
     PRIV_START
 
@@ -151,25 +168,33 @@ run_file(const char *filename, uid_t uid, gid_t gid)
 
     pentry = getpwuid(uid);
     if (pentry == NULL)
-    {
-       syslog(LOG_ERR,"Userid %lu not found - aborting job %s",
-              (unsigned long) uid, filename);
-        exit(EXIT_FAILURE);
-    }
+       perrx("Userid %lu not found - aborting job %s",
+               (unsigned long) uid, filename);
+
+#ifdef PAM
     PRIV_START
 
-    stream=fopen(filename, "r");
+    pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh);
+    if (pam_err != PAM_SUCCESS)
+       perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err));
+
+    pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
+    /* Expired password shouldn't prevent the job from running. */
+    if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD)
+       perrx("Account %s (userid %lu) unavailable for job %s: %s",
+           pentry->pw_name, (unsigned long)uid,
+           filename, pam_strerror(pamh, pam_err));
+
+    pam_end(pamh, pam_err);
 
     PRIV_END
+#endif /* PAM */
 
-#ifdef __DragonFly__
-    if (pentry->pw_expire && time(NULL) >= pentry->pw_expire)
-    {
-       syslog(LOG_ERR, "Userid %lu is expired - aborting job %s",
-               (unsigned long) uid, filename);
-       exit(EXIT_FAILURE);
-    }
-#endif
+    PRIV_START
+
+    stream=fopen(filename, "r");
+
+    PRIV_END
 
     if (stream == NULL)
        perr("cannot open input file");
@@ -183,23 +208,18 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     if (lstat(filename, &lbuf) == -1)
        perr("error in fstat of input file");
 
-    if (S_ISLNK(lbuf.st_mode)) {
-       syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting",
-               filename);
-       exit(EXIT_FAILURE);
-    }
+    if (S_ISLNK(lbuf.st_mode))
+       perrx("Symbolic link encountered in job %s - aborting", 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)) {
-       syslog(LOG_ERR,"Somebody changed files from under us for job %s - "
-       "aborting",filename);
-       exit(EXIT_FAILURE);
-    }
-    if (buf.st_nlink > 1) {
-       syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s",
+        (lbuf.st_size!=buf.st_size))
+       perrx("Somebody changed files from under us for job %s - aborting",
                filename);
-       exit(EXIT_FAILURE);
-    }
+
+    if (buf.st_nlink > 1)
+       perrx("Somebody is trying to run a linked script for job %s", filename);
+
     if ((fflags = fcntl(fd_in, F_GETFD)) <0)
        perr("error in fcntl");
 
@@ -208,28 +228,27 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     snprintf(fmt, sizeof(fmt),
        "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
                           LOGNAMESIZE);
-    if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) {
-       syslog(LOG_ERR,"File %s is in wrong format - aborting", filename);
-       exit(EXIT_FAILURE);
-    }
-    if (mailbuf[0] == '-') {
-       syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename);
-       exit(EXIT_FAILURE);
-    }
+
+    if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4)
+       perrx("File %s is in wrong format - aborting", filename);
+
+    if (mailbuf[0] == '-')
+       perrx("Illegal mail name %s in %s", mailbuf, filename);
+
     mailname = mailbuf;
-    if (nuid != uid) {
-       syslog(LOG_ERR,"Job %s - userid %u does not match file uid %lu",
+
+    if (nuid != uid)
+       perrx("Job %s - userid %ld does not match file uid %lu",
                filename, nuid, (unsigned long)uid);
-       exit(EXIT_FAILURE);
-    }
-    if (ngid != gid) {
-       syslog(LOG_ERR,"Job %s - groupid %u does not match file gid %lu",
+
+    if (ngid != gid)
+       perrx("Job %s - groupid %ld does not match file gid %lu",
                filename, ngid, (unsigned long)gid);
-       exit(EXIT_FAILURE);
-    }
+
     fclose(stream);
+
     if (chdir(ATSPOOL_DIR) < 0)
-       perr("cannot chdir to " ATSPOOL_DIR);
+       perr("cannot chdir to %s", ATSPOOL_DIR);
     
     /* Create a file to hold the output of the job we are about to run.
      * Write the mail header.
@@ -276,7 +295,7 @@ run_file(const char *filename, uid_t uid, gid_t gid)
        close(fd_in);
        close(fd_out);
        if (chdir(ATJOB_DIR) < 0)
-           perr("cannot chdir to " ATJOB_DIR);
+           perr("cannot chdir to %s", ATJOB_DIR);
 
        queue = *filename;
 
@@ -284,8 +303,21 @@ run_file(const char *filename, uid_t uid, gid_t gid)
 
         nice(tolower(queue) - 'a');
        
+#ifdef LOGIN_CAP
+       /*
+        * For simplicity and safety, set all aspects of the user context
+        * except for a selected subset:  Don't set priority, which was
+        * set based on the queue file name according to the tradition.
+        * Don't bother to set environment, including path vars, either
+        * because it will be discarded anyway.  Although the job file
+        * should set umask, preset it here just in case.
+        */
+       if (setusercontext(NULL, pentry, uid, LOGIN_SETALL &
+               ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0)
+           exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
        if (initgroups(pentry->pw_name,pentry->pw_gid))
-           perr("cannot delete saved userids");
+           perr("cannot init group access list");
 
        if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
            perr("cannot change group");
@@ -295,6 +327,7 @@ run_file(const char *filename, uid_t uid, gid_t gid)
 
        if (setuid(uid) < 0 || seteuid(uid) < 0)
            perr("cannot set user id");
+#endif /* LOGIN_CAP */
 
        if (chdir(pentry->pw_dir))
                chdir("/");
@@ -322,8 +355,15 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     {    
        PRIV_START
 
+#ifdef LOGIN_CAP
+       /*
+        * This time set full context to run the mailer.
+        */
+       if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0)
+           exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
        if (initgroups(pentry->pw_name,pentry->pw_gid))
-           perr("cannot delete saved userids");
+           perr("cannot init group access list");
 
        if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
            perr("cannot change group");
@@ -333,6 +373,7 @@ run_file(const char *filename, uid_t uid, gid_t gid)
 
        if (setuid(uid) < 0 || seteuid(uid) < 0)
            perr("cannot set user id");
+#endif /* LOGIN_CAP */
 
        if (chdir(pentry->pw_dir))
                chdir("/");
@@ -351,14 +392,38 @@ run_file(const char *filename, uid_t uid, gid_t gid)
 
 /* Needed in gloadavg.c */
 void
-perr(const char *a)
+perr(const char *fmt, ...)
 {
+    const char * const fmtadd = ": %m";
+    char nfmt[strlen(fmt) + strlen(fmtadd) + 1];
+    va_list ap;
+
+    va_start(ap, fmt);
     if (debug)
     {
-       warn("%s", a);
+       vwarn(fmt, ap);
     }
     else
-       syslog(LOG_ERR, "%s: %m", a);
+    {
+       snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd);
+       vsyslog(LOG_ERR, nfmt, ap);
+    }
+    va_end(ap);
+
+    exit(EXIT_FAILURE);
+}
+
+void
+perrx(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    if (debug)
+       vwarnx(fmt, ap);
+    else
+       vsyslog(LOG_ERR, fmt, ap);
+    va_end(ap);
 
     exit(EXIT_FAILURE);
 }
@@ -396,7 +461,7 @@ main(int argc, char *argv[])
 
     RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID)
 
-    openlog("atrun", LOG_PID, LOG_CRON);
+    openlog(atrun, LOG_PID, LOG_CRON);
 
     opterr = 0;
     while((c=getopt(argc, argv, "dl:"))!= -1)
@@ -421,7 +486,7 @@ main(int argc, char *argv[])
     }
 
     if (chdir(ATJOB_DIR) != 0)
-       perr("cannot change to " ATJOB_DIR);
+       perr("cannot change to %s", ATJOB_DIR);
 
     /* Main loop. Open spool directory for reading and look over all the
      * files in there. If the filename indicates that the job should be run
@@ -434,7 +499,7 @@ main(int argc, char *argv[])
      * atrun.
      */
     if ((spool = opendir(".")) == NULL)
-       perr("cannot read " ATJOB_DIR);
+       perr("cannot read %s", ATJOB_DIR);
 
     now = time(NULL);
     run_batch = 0;
@@ -443,7 +508,7 @@ main(int argc, char *argv[])
 
     while ((dirent = readdir(spool)) != NULL) {
        if (stat(dirent->d_name,&buf) != 0)
-           perr("cannot stat in " ATJOB_DIR);
+           perr("cannot stat in %s", ATJOB_DIR);
 
        /* We don't want directories
         */
@@ -458,7 +523,7 @@ main(int argc, char *argv[])
        if ((S_IXUSR & buf.st_mode) && (run_time <=now)) {
            if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) {
                run_batch = 1;
-               strncpy(batch_name, dirent->d_name, sizeof(batch_name));
+               strlcpy(batch_name, dirent->d_name, sizeof(batch_name));
                batch_uid = buf.st_uid;
                batch_gid = buf.st_gid;
            }
@@ -483,7 +548,7 @@ main(int argc, char *argv[])
 }
 
 static void
-usage()
+usage(void)
 {
     if (debug)
        fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n");
index 299878f..7470757 100644 (file)
@@ -1,6 +1,6 @@
-.\" $FreeBSD: src/libexec/atrun/atrun.man,v 1.7.2.3 2001/08/16 10:44:13 ru Exp $
+.\" $FreeBSD: src/libexec/atrun/atrun.man,v 1.13 2007/06/17 14:02:31 yar Exp $
 .\" $DragonFly: src/libexec/atrun/atrun.man,v 1.4 2007/07/29 17:27:45 swildner Exp $
-.Dd April 12, 1995
+.Dd June 17, 2007
 .Dt ATRUN 8
 .Os
 .Sh NAME
@@ -25,7 +25,7 @@ has to contain the line
 .Ed
 .Pp
 so that
-.Xr atrun 8
+.Nm
 gets invoked every five minutes.
 .Pp
 At every invocation,
@@ -35,6 +35,13 @@ time has elapsed.
 In addition, if the load average over the last minute was less than
 the specified limit then a maximum of one batch job (denoted by the
 uppercase queues) is started.
+.Pp
+Before starting a job,
+.Nm
+will check the status of its owner's account with
+.Xr pam 3
+and refuse to run the job if the account is unavailable,
+e.g., locked out or expired.
 .Sh OPTIONS
 .Bl -tag -width indent
 .It Fl l Ar load_avg
@@ -51,17 +58,23 @@ to work, you have to start up a
 .Xr cron 8
 daemon.
 .Sh FILES
-.Bl -tag -width /var/at/spool -compact
-.It Pa /var/at/spool
-Directory containing output spool files
+.Bl -tag -width /etc/pam.d/atrun -compact
+.It Pa /etc/pam.d/atrun
+.Xr pam.conf 5
+configuration file for
+.Nm
 .It Pa /var/at/jobs
 Directory containing job files
+.It Pa /var/at/spool
+Directory containing output spool files
 .El
 .Sh SEE ALSO
 .Xr at 1 ,
 .Xr crontab 1 ,
+.Xr pam 3 ,
 .Xr syslog 3 ,
 .Xr crontab 5 ,
+.Xr pam.conf 5 ,
 .Xr cron 8
 .Sh BUGS
 The functionality of
index 08231b2..4bf9109 100644 (file)
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/libexec/atrun/gloadavg.c,v 1.5 1999/08/28 00:09:12 peter Exp $
+ * $FreeBSD: src/libexec/atrun/gloadavg.c,v 1.6 2007/06/15 10:12:37 yar Exp $
  * $DragonFly: src/libexec/atrun/gloadavg.c,v 1.3 2004/02/03 23:24:50 rob Exp $
  */
 
-#ifndef __DragonFly__
-#define _POSIX_SOURCE 1
-
 /* System Headers */
 
-#include <stdio.h>
-#else
 #include <stdlib.h>
-#endif
 
 /* Local headers */
 
@@ -42,7 +36,7 @@
 
 /* Global functions */
 
-void perr(const char *a);
+void perr(const char *fmt, ...);
 
 double
 gloadavg(void)
@@ -51,20 +45,7 @@ gloadavg(void)
  */
 {
     double result;
-#ifndef __DragonFly__
-    FILE *fp;
-    
-    if((fp=fopen(PROC_DIR "loadavg","r")) == NULL)
-       result = -1.0;
-    else
-    {
-       if(fscanf(fp,"%lf",&result) != 1)
-           result = -1.0;
-       fclose(fp);
-    }
-#else
     if (getloadavg(&result, 1) != 1)
            perr("error in getloadavg");
-#endif
     return result;
 }