]> git.saurik.com Git - apple/system_cmds.git/blobdiff - atrun.tproj/atrun.c
system_cmds-597.1.1.tar.gz
[apple/system_cmds.git] / atrun.tproj / atrun.c
index 40552c0af28f49c5d273120aef5c3cef6a6704bd..717ba20fa5a244fb3b2b921684d47e5fc53fb17d 100644 (file)
@@ -25,7 +25,7 @@
 
 #ifndef lint
 static const char rcsid[] =
-  "$FreeBSD: src/libexec/atrun/atrun.c,v 1.18 2004/07/11 17:37:32 stefanf Exp $";
+  "$FreeBSD: src/libexec/atrun/atrun.c,v 1.27 2009/12/25 10:30:54 ed Exp $";
 #endif /* not lint */
 
 /* System Headers */
@@ -41,6 +41,7 @@ static const char rcsid[] =
 #include <grp.h>
 #include <pwd.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,17 +49,17 @@ static const char rcsid[] =
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
-#include <utmpx.h>
 #if 1
 #include <paths.h>
 #else
 #include <getopt.h>
 #endif
-
-#if (MAXLOGNAME-1) > _UTX_USERSIZE
-#define LOGNAMESIZE _UTX_USERSIZE
-#else
-#define LOGNAMESIZE (MAXLOGNAME-1)
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+#ifdef PAM
+#include <security/pam_appl.h>
+#include <security/openpam.h>
 #endif
 
 /* Local headers */
@@ -66,6 +67,7 @@ static const char rcsid[] =
 #define MAIN
 #include "privs.h"
 #include "pathnames.h"
+
 /* Macros */
 
 #ifndef ATJOB_DIR
@@ -82,9 +84,11 @@ static const char rcsid[] =
 
 /* 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 */
@@ -119,8 +123,7 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     pid_t pid;
     int fd_out, fd_in;
     int queue;
-    char mailbuf[LOGNAMESIZE + 1];
-    char *fmt = NULL;
+    char mailbuf[MAXLOGNAME], fmt[64];
     char *mailname = NULL;
     FILE *stream;
     int send_mail = 0;
@@ -130,7 +133,14 @@ run_file(const char *filename, uid_t uid, gid_t gid)
     int fflags;
     long nuid;
     long ngid;
-
+#ifdef PAM
+    pam_handle_t *pamh = NULL;
+    int pam_err;
+    struct pam_conv pamc = {
+       .conv = openpam_nullconv,
+       .appdata_ptr = NULL
+    };
+#endif
 
     PRIV_START
 
@@ -162,25 +172,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 __FreeBSD__
-    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");
@@ -194,55 +212,47 @@ 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");
 
     fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
 
-    asprintf(&fmt, "%s%d%s",
-            "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %",
-            LOGNAMESIZE,
-            "s %d");
-    if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) {
-       syslog(LOG_ERR,"File %s is in wrong format - aborting", filename);
-       exit(EXIT_FAILURE);
-    }
-    free(fmt);
-    if (mailbuf[0] == '-') {
-       syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename);
-       exit(EXIT_FAILURE);
-    }
+    snprintf(fmt, sizeof(fmt),
+       "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
+                          MAXLOGNAME - 1);
+
+    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 %ld 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 %ld 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.
@@ -289,7 +299,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;
 
@@ -297,17 +307,31 @@ 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 (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
            perr("cannot change group");
 
        if (initgroups(pentry->pw_name,pentry->pw_gid))
-           perr("cannot delete saved userids");
+           perr("cannot init group access list");
 
        if (setlogin(pentry->pw_name))
            perr("cannot set login name");
 
        if (setuid(uid) < 0 || seteuid(uid) < 0)
            perr("cannot set user id");
+#endif /* LOGIN_CAP */
 
        if (chdir(pentry->pw_dir))
                chdir("/");
@@ -335,17 +359,25 @@ 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 (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
            perr("cannot change group");
 
        if (initgroups(pentry->pw_name,pentry->pw_gid))
-           perr("cannot delete saved userids");
+           perr("cannot init group access list");
 
        if (setlogin(pentry->pw_name))
            perr("cannot set login name");
 
        if (setuid(uid) < 0 || seteuid(uid) < 0)
            perr("cannot set user id");
+#endif /* LOGIN_CAP */
 
        if (chdir(pentry->pw_dir))
                chdir("/");
@@ -368,14 +400,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);
 }
@@ -413,7 +469,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)
@@ -438,7 +494,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
@@ -451,7 +507,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;
@@ -460,7 +516,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
         */
@@ -473,9 +529,9 @@ main(int argc, char *argv[])
        run_time = (time_t) ctm*60;
 
        if ((S_IXUSR & buf.st_mode) && (run_time <=now)) {
-           if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) {
+           if ((isupper(queue) || queue == 'b') && (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;
            }