| diff -up at-3.1.13/at.c.pam at-3.1.13/at.c |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 <security/pam_appl.h> |
| |
| 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,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); |
| @@ -419,7 +405,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 +431,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 +449,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 +471,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 +501,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 |
| |
| if (initgroups(pentry->pw_name, pentry->pw_gid)) |
| @@ -535,7 +539,21 @@ 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 |
| exit(EXIT_SUCCESS); |
| } |
| |
| diff -up at-3.1.13/config.h.in.pam at-3.1.13/config.h.in |
| |
| |
| @@ -68,8 +68,8 @@ |
| /* Define to 1 if you have the <nlist.h> 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -51,6 +51,14 @@ |
| #define PRIV_END while(0) |
| #endif |
| |
| +#ifdef WITH_PAM |
| +#include <security/pam_appl.h> |
| +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.13/privs.h.pam at-3.1.13/privs.h |
| |
| |
| @@ -144,3 +144,63 @@ extern gid_t real_gid, effective_gid, da |
| #error "Cannot implement user ID swapping without setreuid or setresuid" |
| #endif |
| #endif |
| + |
| +#ifdef WITH_PAM |
| +/* PAM failed after session was open. */ |
| +#define PAM_SESSION_FAIL if (retcode != PAM_SUCCESS) \ |
| + pam_close_session(pamh,PAM_SILENT); |
| + |
| +/* syslog will be logging error messages */ |
| +#ifdef HAVE_UNISTD_H |
| +#include <syslog.h> |
| +#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 |
| + |