Skip to content

Commit

Permalink
Fix CVE 2013-4495. Note: this patch has been verified as fixing this …
Browse files Browse the repository at this point in the history
…security hole but has not received other regression testing. Could not cherry-pick as 2.5 and 4.1 are very different.
  • Loading branch information
David Beer committed Nov 11, 2013
1 parent 480ab5e commit 2aad72c
Showing 1 changed file with 170 additions and 95 deletions.
265 changes: 170 additions & 95 deletions src/server/svr_mail.c
Expand Up @@ -89,6 +89,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "list_link.h"
#include "attribute.h"
#include "server_limits.h"
Expand Down Expand Up @@ -136,22 +137,97 @@ void free_mail_info(



void add_body_info(

char *bodyfmtbuf /* I */,
mail_info *mi /* I */)

{
char *bodyfmt = NULL;
bodyfmt = strcpy(bodyfmtbuf, "PBS Job Id: %i\n"
"Job Name: %j\n");
if (mi->exec_host != NULL)
{
strcat(bodyfmt, "Exec host: %h\n");
}

strcat(bodyfmt, "%m\n");

if (mi->text != NULL)
{
strcat(bodyfmt, "%d\n");
}
}


/*
* write_email()
*
* In emailing, the mail body is written to a pipe connected to
* standard input for sendmail. This function supplies the body
* of the message.
*
*/
void write_email(

FILE *outmail_input,
mail_info *mi)

{
char *bodyfmt = NULL;
char *subjectfmt = NULL;

/* Pipe in mail headers: To: and Subject: */
fprintf(outmail_input, "To: %s\n", mi->mailto);

/* mail subject line formating statement */
get_svr_attr_str(SRV_ATR_MailSubjectFmt, (char **)&subjectfmt);
if (subjectfmt == NULL)
{
subjectfmt = "PBS JOB %i";
}

fprintf(outmail_input, "Subject: ");
svr_format_job(outmail_input, mi, subjectfmt);
fprintf(outmail_input, "\n");

/* Set "Precedence: bulk" to avoid vacation messages, etc */
fprintf(outmail_input, "Precedence: bulk\n\n");

/* mail body formating statement */
get_svr_attr_str(SRV_ATR_MailBodyFmt, &bodyfmt);
if (bodyfmt == NULL)
{
char bodyfmtbuf[MAXLINE];
add_body_info(bodyfmtbuf, mi);
bodyfmt = bodyfmtbuf;
}

/* Now pipe in the email body */
svr_format_job(outmail_input, mi, bodyfmt);

} /* write_email() */



void *send_the_mail(

void *vp)

{
mail_info *mi = (mail_info *)vp;

int i;
char *mailfrom = NULL;
char *subjectfmt = NULL;
char *bodyfmt = NULL;
char *cmdbuf = NULL;
char bodyfmtbuf[MAXLINE];
FILE *outmail;
mail_info *mi = (mail_info *)vp;

int status = 0;
int numargs = 0;
int pipes[2];
int counter;
pid_t pid;
char *mailptr;
char *mailfrom = NULL;
char tmpBuf[LOG_BUF_SIZE];
// We call sendmail with cmd_name + 2 arguments + # of mailto addresses + 1 for null
char *sendmail_args[100];
FILE *stream;

/* Who is mail from, if SRV_ATR_mailfrom not set use default */
get_svr_attr_str(SRV_ATR_mailfrom, &mailfrom);
Expand All @@ -173,124 +249,123 @@ void *send_the_mail(
mailfrom = PBS_DEFAULT_MAIL;
}

/* mail subject line formating statement */
get_svr_attr_str(SRV_ATR_MailSubjectFmt, &subjectfmt);
if (subjectfmt == NULL)
{
subjectfmt = "PBS JOB %i";
}
sendmail_args[numargs++] = (char *)SENDMAIL_CMD;
sendmail_args[numargs++] = (char *)"-f";
sendmail_args[numargs++] = (char *)mailfrom;

/* mail body formating statement */
get_svr_attr_str(SRV_ATR_MailBodyFmt, &bodyfmt);
if (bodyfmt == NULL)
/* Add the e-mail addresses to the command line */
mailptr = strdup(mi->mailto);
sendmail_args[numargs++] = mailptr;
for (counter=0; counter < (int)strlen(mailptr); counter++)
{
bodyfmt = strcpy(bodyfmtbuf, "PBS Job Id: %i\n"
"Job Name: %j\n");
if (mi->exec_host != NULL)
if (mailptr[counter] == ',')
{
strcat(bodyfmt, "Exec host: %h\n");
}

strcat(bodyfmt, "%m\n");

if (mi->text != NULL)
{
strcat(bodyfmt, "%d\n");
mailptr[counter] = '\0';
sendmail_args[numargs++] = mailptr + counter + 1;
if (numargs >= 99)
break;
}
}

/* setup sendmail command line with -f from_whom */
i = strlen(SENDMAIL_CMD) + strlen(mailfrom) + strlen(mi->mailto) + 6;

if ((cmdbuf = calloc(1, i + 1)) == NULL)
sendmail_args[numargs] = NULL;

/* Create a pipe to talk to the sendmail process we are about to fork */
if (pipe(pipes) == -1)
{
char tmpBuf[LOG_BUF_SIZE];

snprintf(tmpBuf,sizeof(tmpBuf),
"Unable to popen() command '%s' for writing: '%s' (error %d)\n",
SENDMAIL_CMD,
strerror(errno),
errno);
snprintf(tmpBuf, sizeof(tmpBuf), "Unable to pipes for sending e-mail\n");
log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
tmpBuf);

free_mail_info(mi);

free_mail_info(mi);
free(mailptr);
return(NULL);
}

sprintf(cmdbuf, "%s -f %s %s",
SENDMAIL_CMD,
mailfrom,
mi->mailto);

outmail = popen(cmdbuf, "w");

if (outmail == NULL)
if ((pid=fork()) == -1)
{
char tmpBuf[LOG_BUF_SIZE];

snprintf(tmpBuf,sizeof(tmpBuf),
"Unable to popen() command '%s' for writing: '%s' (error %d)\n",
cmdbuf,
strerror(errno),
errno);
snprintf(tmpBuf, sizeof(tmpBuf), "Unable to fork for sending e-mail\n");
log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
tmpBuf);

free_mail_info(mi);
free(cmdbuf);

free(mailptr);
close(pipes[0]);
close(pipes[1]);
return(NULL);
}
else if (pid == 0)
{
/* CHILD */

/* Pipe in mail headers: To: and Subject: */
fprintf(outmail, "To: %s\n", mi->mailto);
/* Make stdin the read end of the pipe */
dup2(pipes[0], 0);

fprintf(outmail, "Subject: ");
svr_format_job(outmail, mi, subjectfmt);
fprintf(outmail, "\n");
/* Close the rest of the open file descriptors */
int numfds = sysconf(_SC_OPEN_MAX);
while (--numfds > 0)
close(numfds);

/* Set "Precedence: bulk" to avoid vacation messages, etc */
fprintf(outmail, "Precedence: bulk\n\n");
execv(SENDMAIL_CMD, sendmail_args);
/* This never returns, but if the execv fails the child should exit */
exit(1);
}
else
{
/* This is the parent */

/* Now pipe in the email body */
svr_format_job(outmail, mi, bodyfmt);
/* Close the read end of the pipe */
close(pipes[0]);

errno = 0;
if ((i = pclose(outmail)) != 0)
{
char tmpBuf[LOG_BUF_SIZE];
/* Write the body to the pipe */
stream = fdopen(pipes[1], "w");
write_email(stream, mi);

snprintf(tmpBuf,sizeof(tmpBuf),
"Email '%c' to %s failed: Child process '%s' %s %d (errno %d:%s)\n",
mi->mail_point,
mi->mailto,
cmdbuf,
((WIFEXITED(i)) ? ("returned") : ((WIFSIGNALED(i)) ? ("killed by signal") : ("croaked"))),
((WIFEXITED(i)) ? (WEXITSTATUS(i)) : ((WIFSIGNALED(i)) ? (WTERMSIG(i)) : (i))),
errno,
strerror(errno));
log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
tmpBuf);
}
else if (LOGLEVEL >= 4)
{
log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
"Email sent successfully\n");
}
fflush(stream);

/* Close and wait for the command to finish */
if (fclose(stream) != 0)
{
snprintf(tmpBuf,sizeof(tmpBuf),
"Piping mail body to sendmail closed: errno %d:%s\n",
errno, strerror(errno));

log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
tmpBuf);
}

// we aren't going to block in order to find out whether or not sendmail worked
if ((waitpid(pid, &status, WNOHANG) != 0) &&
(status != 0))
{
snprintf(tmpBuf,sizeof(tmpBuf),
"Sendmail command returned %d. Mail may not have been sent\n",
status);

log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
PBS_EVENTCLASS_JOB,
mi->jobid,
tmpBuf);
}

free_mail_info(mi);
free(cmdbuf);
// don't leave zombies
while (waitpid(-1, &status, WNOHANG) != 0)
{
// zombie reaped, NO-OP
}

free_mail_info(mi);
free(mailptr);
return(NULL);
}

/* NOT REACHED */

return(NULL);
} /* END send_the_mail() */

Expand Down

0 comments on commit 2aad72c

Please sign in to comment.