diff --git a/at-3.1.14-pam.patch b/at-3.1.14-pam.patch new file mode 100644 index 0000000..4550281 --- /dev/null +++ b/at-3.1.14-pam.patch @@ -0,0 +1,446 @@ +diff -up at-3.1.14/at.c.pam at-3.1.14/at.c +--- at-3.1.14/at.c.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/at.c 2013-09-26 14:28:40.549603545 +0200 +@@ -144,18 +144,14 @@ 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 +311,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. + */ +@@ -654,7 +643,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"); +@@ -664,7 +653,7 @@ process_jobs(int argc, char **argv, int + rc = EXIT_FAILURE; + } + +- setregid(effective_gid, real_gid); ++ PRIV_END + done = 1; + + break; +@@ -674,7 +663,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) { +@@ -687,7 +676,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.14/atd.c.pam at-3.1.14/atd.c +--- at-3.1.14/atd.c.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/atd.c 2013-09-26 15:04:28.762185123 +0200 +@@ -83,6 +83,10 @@ + #include "getloadavg.h" + #endif + ++#ifndef LOG_ATD ++#define LOG_ATD LOG_DAEMON ++#endif ++ + /* Macros */ + + #define BATCH_INTERVAL_DEFAULT 60 +@@ -108,7 +112,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; +@@ -117,15 +121,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 +@@ -220,7 +216,7 @@ run_file(const char *filename, uid_t uid + char fmt[64]; + unsigned long jobno; + int rc; +-#ifdef HAVE_PAM ++#ifdef WTIH_PAM + int retcode; + #endif + +@@ -377,17 +373,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); +@@ -401,6 +391,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. +@@ -420,8 +418,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)) +@@ -438,6 +434,17 @@ 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. +@@ -450,14 +457,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. + */ +@@ -488,7 +487,20 @@ 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 ++ AT_START_PAM; ++ AT_OPEN_PAM_SESSION; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); ++#endif ++ ++ mail_pid = fork(); ++ ++ if ( mail_pid == 0 ) ++ { + + PRIV_START + +@@ -511,7 +523,20 @@ 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 ++ AT_CLOSE_PAM; ++ closelog(); ++ openlog("atd", LOG_PID, LOG_ATD); ++#endif + } ++#endif /* ATD_MAIL_PROGRAM */ + exit(EXIT_SUCCESS); + } + +@@ -727,7 +752,7 @@ main(int argc, char *argv[]) + #define LOG_CRON LOG_DAEMON + #endif + +- openlog("atd", LOG_PID, LOG_CRON); ++ openlog("atd", LOG_PID, LOG_ATD); + + opterr = 0; + errno = 0; +diff -up at-3.1.14/config.h.in.pam at-3.1.14/config.h.in +--- at-3.1.14/config.h.in.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/config.h.in 2013-09-26 14:28:40.550603546 +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.14/configure.ac.pam at-3.1.14/configure.ac +--- at-3.1.14/configure.ac.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/configure.ac 2013-09-26 14:28:40.550603546 +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_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.14/perm.c.pam at-3.1.14/perm.c +--- at-3.1.14/perm.c.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/perm.c 2013-09-26 14:28:40.550603546 +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.14/privs.h.pam at-3.1.14/privs.h +--- at-3.1.14/privs.h.pam 2013-09-08 14:43:53.000000000 +0200 ++++ at-3.1.14/privs.h 2013-09-26 14:28:40.551603547 +0200 +@@ -144,3 +144,64 @@ extern gid_t real_gid, effective_gid, da + #error "Cannot implement user ID swapping without setreuid or setresuid" + #endif + #endif ++ ++#ifdef WITH_PAM ++int retcode; ++/* 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.spec b/at.spec index c27d4af..e8f807d 100644 --- a/at.spec +++ b/at.spec @@ -73,13 +73,11 @@ is not used as the system init process. %setup -q cp %{SOURCE1} . %patch1 -p1 -b .make -#%%patch2 -p1 -b .pam +%patch2 -p1 -b .pam #%%patch3 -p1 -b .selinux #%%patch2 -p1 -b .opt_V #%%patch3 -p1 -b .shell #%%patch4 -p1 -b .nit -#%%patch5 -p1 -b .pam -#%%patch6 -p1 -b .selinux #%%patch7 -p1 -b .nowrap #%%patch8 -p1 -b .export #%%patch9 -p1 -b .mail