Fix #80751: Comma in recipient name breaks email delivery

So far, `SendText()` simply separates potential email address lists at
any comma, disregarding that commas inside a quoted-string do not
delimit addresses.  We fix that by introducing an own variant of
`strtok_r()` which caters to quoted-strings.

We also make `FormatEmailAddress()` aware of quoted strings.

We do not cater to email address comments, and potentially other quirks
of RFC 5322 email addresses, but catering to quoted-strings is supposed
to solve almost all practical use cases.

Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>

Closes GH-6735.
This commit is contained in:
Christoph M. Becker 2021-03-01 16:18:40 +01:00
parent 2c508c4d40
commit 71297a254b
2 changed files with 179 additions and 16 deletions

View file

@ -333,6 +333,37 @@ PHPAPI char *GetSMErrorText(int index)
}
}
/* strtok_r like, but recognizes quoted-strings */
static char *find_address(char *list, char **state)
{
zend_bool in_quotes = 0;
char *p = list;
if (list == NULL) {
if (*state == NULL) {
return NULL;
}
p = list = *state;
}
*state = NULL;
while ((p = strpbrk(p, ",\"\\")) != NULL) {
if (*p == '\\' && in_quotes) {
if (p[1] == '\0') {
/* invalid address; let SMTP server deal with it */
break;
}
p++;
} else if (*p == '"') {
in_quotes = !in_quotes;
} else if (*p == ',' && !in_quotes) {
*p = '\0';
*state = p + 1;
break;
}
p++;
}
return list;
}
/*********************************************************************
// Name: SendText
@ -357,7 +388,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
{
int res;
char *p;
char *tempMailTo, *token, *pos1, *pos2;
char *tempMailTo, *token, *token_state, *pos1, *pos2;
char *server_response = NULL;
char *stripped_header = NULL;
zend_string *data_cln;
@ -410,7 +441,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
tempMailTo = estrdup(mailTo);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -424,14 +455,14 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
if (mailCc && *mailCc) {
tempMailTo = estrdup(mailCc);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -445,7 +476,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
}
@ -471,7 +502,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
tempMailTo = estrndup(pos1, pos2 - pos1);
}
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -485,7 +516,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL,&token_state);
}
efree(tempMailTo);
}
@ -496,7 +527,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
if (mailBcc && *mailBcc) {
tempMailTo = estrdup(mailBcc);
/* Send mail to all rcpt's */
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -510,7 +541,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
}
@ -544,7 +575,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
}
}
token = strtok(tempMailTo, ",");
token = find_address(tempMailTo, &token_state);
while (token != NULL)
{
SMTP_SKIP_SPACE(token);
@ -558,7 +589,7 @@ static int SendText(char *RPath, char *Subject, char *mailTo, char *mailCc, char
efree(tempMailTo);
return (res);
}
token = strtok(NULL, ",");
token = find_address(NULL, &token_state);
}
efree(tempMailTo);
@ -978,6 +1009,46 @@ static unsigned long GetAddr(LPSTR szHost)
return (lAddr);
} /* end GetAddr() */
/* returns the contents of an angle-addr (caller needs to efree) or NULL */
static char *get_angle_addr(char *address)
{
zend_bool in_quotes = 0;
char *p1 = address, *p2;
while ((p1 = strpbrk(p1, "<\"\\")) != NULL) {
if (*p1 == '\\' && in_quotes) {
if (p1[1] == '\0') {
/* invalid address; let SMTP server deal with it */
return NULL;
}
p1++;
} else if (*p1 == '"') {
in_quotes = !in_quotes;
} else if (*p1 == '<' && !in_quotes) {
break;
}
p1++;
}
if (p1 == NULL) return NULL;
p2 = ++p1;
while ((p2 = strpbrk(p2, ">\"\\")) != NULL) {
if (*p2 == '\\' && in_quotes) {
if (p2[1] == '\0') {
/* invalid address; let SMTP server deal with it */
return NULL;
}
p2++;
} else if (*p2 == '"') {
in_quotes = !in_quotes;
} else if (*p2 == '>' && !in_quotes) {
break;
}
p2++;
}
if (p2 == NULL) return NULL;
return estrndup(p1, p2 - p1);
}
/*********************************************************************
// Name: int FormatEmailAddress
@ -993,13 +1064,12 @@ static unsigned long GetAddr(LPSTR szHost)
// History:
//********************************************************************/
static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString) {
char *tmpAddress1, *tmpAddress2;
char *tmpAddress;
int result;
if( (tmpAddress1 = strchr(EmailAddress, '<')) && (tmpAddress2 = strchr(tmpAddress1, '>')) ) {
*tmpAddress2 = 0; // terminate the string temporarily.
result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString , tmpAddress1+1);
*tmpAddress2 = '>'; // put it back the way it was.
if ((tmpAddress = get_angle_addr(EmailAddress)) != NULL) {
result = snprintf(Buf, MAIL_BUFFER_SIZE, FormatString, tmpAddress);
efree(tmpAddress);
return result;
}
return snprintf(Buf, MAIL_BUFFER_SIZE , FormatString , EmailAddress );