Skip to content

Commit a4d9ccb

Browse files
committed
Merge branch 'PHP-7.4' into PHP-8.0
* PHP-7.4: Fix #80710: imap_mail_compose() header injection
2 parents eed03fc + 568df31 commit a4d9ccb

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

ext/imap/php_imap.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3063,6 +3063,23 @@ PHP_FUNCTION(imap_fetch_overview)
30633063
}
30643064
/* }}} */
30653065

3066+
static zend_bool header_injection(zend_string *str, zend_bool adrlist)
3067+
{
3068+
char *p = ZSTR_VAL(str);
3069+
3070+
while ((p = strpbrk(p, "\r\n")) != NULL) {
3071+
if (!(p[0] == '\r' && p[1] == '\n')
3072+
/* adrlists do not support folding, but swallow trailing line breaks */
3073+
&& !((adrlist && p[1] == '\0')
3074+
/* other headers support folding */
3075+
|| !adrlist && (p[1] == ' ' || p[1] == '\t'))) {
3076+
return 1;
3077+
}
3078+
p++;
3079+
}
3080+
return 0;
3081+
}
3082+
30663083
/* {{{ Create a MIME message based on given envelope and body sections */
30673084
PHP_FUNCTION(imap_mail_compose)
30683085
{
@@ -3086,6 +3103,13 @@ PHP_FUNCTION(imap_mail_compose)
30863103
zend_argument_value_error(2, "cannot be empty");
30873104
}
30883105

3106+
#define CHECK_HEADER_INJECTION(zstr, adrlist, header) \
3107+
if (header_injection(zstr, adrlist)) { \
3108+
php_error_docref(NULL, E_WARNING, "header injection attempt in " header); \
3109+
RETVAL_FALSE; \
3110+
goto done; \
3111+
}
3112+
30893113
#define PHP_RFC822_PARSE_ADRLIST(target, value) \
30903114
str_copy = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); \
30913115
rfc822_parse_adrlist(target, str_copy, "NO HOST"); \
@@ -3094,46 +3118,57 @@ PHP_FUNCTION(imap_mail_compose)
30943118
env = mail_newenvelope();
30953119
if ((pvalue = zend_hash_str_find(envelope, "remail", sizeof("remail") - 1)) != NULL) {
30963120
convert_to_string_ex(pvalue);
3121+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "remail");
30973122
env->remail = cpystr(Z_STRVAL_P(pvalue));
30983123
}
30993124
if ((pvalue = zend_hash_str_find(envelope, "return_path", sizeof("return_path") - 1)) != NULL) {
31003125
convert_to_string_ex(pvalue);
3126+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "return_path");
31013127
PHP_RFC822_PARSE_ADRLIST(&env->return_path, pvalue);
31023128
}
31033129
if ((pvalue = zend_hash_str_find(envelope, "date", sizeof("date") - 1)) != NULL) {
31043130
convert_to_string_ex(pvalue);
3131+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "date");
31053132
env->date = (unsigned char*)cpystr(Z_STRVAL_P(pvalue));
31063133
}
31073134
if ((pvalue = zend_hash_str_find(envelope, "from", sizeof("from") - 1)) != NULL) {
31083135
convert_to_string_ex(pvalue);
3136+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "from");
31093137
PHP_RFC822_PARSE_ADRLIST(&env->from, pvalue);
31103138
}
31113139
if ((pvalue = zend_hash_str_find(envelope, "reply_to", sizeof("reply_to") - 1)) != NULL) {
31123140
convert_to_string_ex(pvalue);
3141+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "reply_to");
31133142
PHP_RFC822_PARSE_ADRLIST(&env->reply_to, pvalue);
31143143
}
31153144
if ((pvalue = zend_hash_str_find(envelope, "in_reply_to", sizeof("in_reply_to") - 1)) != NULL) {
31163145
convert_to_string_ex(pvalue);
3146+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "in_reply_to");
31173147
env->in_reply_to = cpystr(Z_STRVAL_P(pvalue));
31183148
}
31193149
if ((pvalue = zend_hash_str_find(envelope, "subject", sizeof("subject") - 1)) != NULL) {
31203150
convert_to_string_ex(pvalue);
3151+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "subject");
31213152
env->subject = cpystr(Z_STRVAL_P(pvalue));
31223153
}
31233154
if ((pvalue = zend_hash_str_find(envelope, "to", sizeof("to") - 1)) != NULL) {
31243155
convert_to_string_ex(pvalue);
3156+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "to");
31253157
PHP_RFC822_PARSE_ADRLIST(&env->to, pvalue);
31263158
}
31273159
if ((pvalue = zend_hash_str_find(envelope, "cc", sizeof("cc") - 1)) != NULL) {
31283160
convert_to_string_ex(pvalue);
3161+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "cc");
31293162
PHP_RFC822_PARSE_ADRLIST(&env->cc, pvalue);
31303163
}
31313164
if ((pvalue = zend_hash_str_find(envelope, "bcc", sizeof("bcc") - 1)) != NULL) {
31323165
convert_to_string_ex(pvalue);
3166+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "bcc");
31333167
PHP_RFC822_PARSE_ADRLIST(&env->bcc, pvalue);
31343168
}
31353169
if ((pvalue = zend_hash_str_find(envelope, "message_id", sizeof("message_id") - 1)) != NULL) {
31363170
convert_to_string_ex(pvalue);
3171+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "message_id");
31373172
env->message_id=cpystr(Z_STRVAL_P(pvalue));
31383173
}
31393174

@@ -3144,6 +3179,7 @@ PHP_FUNCTION(imap_mail_compose)
31443179
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pvalue), env_data) {
31453180
custom_headers_param = mail_newbody_parameter();
31463181
convert_to_string_ex(env_data);
3182+
CHECK_HEADER_INJECTION(Z_STR_P(env_data), 0, "custom_headers");
31473183
custom_headers_param->value = (char *) fs_get(Z_STRLEN_P(env_data) + 1);
31483184
custom_headers_param->attribute = NULL;
31493185
memcpy(custom_headers_param->value, Z_STRVAL_P(env_data), Z_STRLEN_P(env_data) + 1);
@@ -3186,6 +3222,7 @@ PHP_FUNCTION(imap_mail_compose)
31863222
}
31873223
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
31883224
convert_to_string_ex(pvalue);
3225+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
31893226
tmp_param = mail_newbody_parameter();
31903227
tmp_param->value = cpystr(Z_STRVAL_P(pvalue));
31913228
tmp_param->attribute = cpystr("CHARSET");
@@ -3198,9 +3235,11 @@ PHP_FUNCTION(imap_mail_compose)
31983235
SEPARATE_ARRAY(pvalue);
31993236
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
32003237
if (key == NULL) continue;
3238+
CHECK_HEADER_INJECTION(key, 0, "body disposition key");
32013239
disp_param = mail_newbody_parameter();
32023240
disp_param->attribute = cpystr(ZSTR_VAL(key));
32033241
convert_to_string_ex(disp_data);
3242+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
32043243
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
32053244
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
32063245
disp_param->next = tmp_param;
@@ -3211,18 +3250,22 @@ PHP_FUNCTION(imap_mail_compose)
32113250
}
32123251
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
32133252
convert_to_string_ex(pvalue);
3253+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
32143254
bod->subtype = cpystr(Z_STRVAL_P(pvalue));
32153255
}
32163256
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
32173257
convert_to_string_ex(pvalue);
3258+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
32183259
bod->id = cpystr(Z_STRVAL_P(pvalue));
32193260
}
32203261
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
32213262
convert_to_string_ex(pvalue);
3263+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
32223264
bod->description = cpystr(Z_STRVAL_P(pvalue));
32233265
}
32243266
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
32253267
convert_to_string_ex(pvalue);
3268+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
32263269
bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
32273270
memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
32283271
}
@@ -3232,9 +3275,11 @@ PHP_FUNCTION(imap_mail_compose)
32323275
SEPARATE_ARRAY(pvalue);
32333276
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
32343277
if (key == NULL) continue;
3278+
CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
32353279
disp_param = mail_newbody_parameter();
32363280
disp_param->attribute = cpystr(ZSTR_VAL(key));
32373281
convert_to_string_ex(disp_data);
3282+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
32383283
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
32393284
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
32403285
disp_param->next = tmp_param;
@@ -3265,6 +3310,7 @@ PHP_FUNCTION(imap_mail_compose)
32653310
}
32663311
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
32673312
convert_to_string_ex(pvalue);
3313+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
32683314
bod->md5 = cpystr(Z_STRVAL_P(pvalue));
32693315
}
32703316
} else if (Z_TYPE_P(data) == IS_ARRAY && topbod->type == TYPEMULTIPART) {
@@ -3297,6 +3343,7 @@ PHP_FUNCTION(imap_mail_compose)
32973343
}
32983344
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
32993345
convert_to_string_ex(pvalue);
3346+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
33003347
tmp_param = mail_newbody_parameter();
33013348
tmp_param->value = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
33023349
memcpy(tmp_param->value, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue) + 1);
@@ -3310,9 +3357,11 @@ PHP_FUNCTION(imap_mail_compose)
33103357
SEPARATE_ARRAY(pvalue);
33113358
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
33123359
if (key == NULL) continue;
3360+
CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
33133361
disp_param = mail_newbody_parameter();
33143362
disp_param->attribute = cpystr(ZSTR_VAL(key));
33153363
convert_to_string_ex(disp_data);
3364+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
33163365
disp_param->value = (char *)fs_get(Z_STRLEN_P(disp_data) + 1);
33173366
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
33183367
disp_param->next = tmp_param;
@@ -3323,18 +3372,22 @@ PHP_FUNCTION(imap_mail_compose)
33233372
}
33243373
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
33253374
convert_to_string_ex(pvalue);
3375+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
33263376
bod->subtype = cpystr(Z_STRVAL_P(pvalue));
33273377
}
33283378
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
33293379
convert_to_string_ex(pvalue);
3380+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
33303381
bod->id = cpystr(Z_STRVAL_P(pvalue));
33313382
}
33323383
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
33333384
convert_to_string_ex(pvalue);
3385+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
33343386
bod->description = cpystr(Z_STRVAL_P(pvalue));
33353387
}
33363388
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
33373389
convert_to_string_ex(pvalue);
3390+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
33383391
bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
33393392
memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
33403393
}
@@ -3344,9 +3397,11 @@ PHP_FUNCTION(imap_mail_compose)
33443397
SEPARATE_ARRAY(pvalue);
33453398
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
33463399
if (key == NULL) continue;
3400+
CHECK_HEADER_INJECTION(key, 0, "body disposition key");
33473401
disp_param = mail_newbody_parameter();
33483402
disp_param->attribute = cpystr(ZSTR_VAL(key));
33493403
convert_to_string_ex(disp_data);
3404+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
33503405
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
33513406
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
33523407
disp_param->next = tmp_param;
@@ -3377,6 +3432,7 @@ PHP_FUNCTION(imap_mail_compose)
33773432
}
33783433
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
33793434
convert_to_string_ex(pvalue);
3435+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
33803436
bod->md5 = cpystr(Z_STRVAL_P(pvalue));
33813437
}
33823438
}

ext/imap/tests/bug80710_1.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Bug #80710 (imap_mail_compose() header injection) - MIME Splitting Attack
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("imap")) die("skip imap extension not available");
6+
?>
7+
--FILE--
8+
<?php
9+
$envelope["from"]= "joe@example.com\n From : X-INJECTED";
10+
$envelope["to"] = "foo@example.com\nFrom: X-INJECTED";
11+
$envelope["cc"] = "bar@example.com\nFrom: X-INJECTED";
12+
$envelope["subject"] = "bar@example.com\n\n From : X-INJECTED";
13+
$envelope["x-remail"] = "bar@example.com\nFrom: X-INJECTED";
14+
$envelope["something"] = "bar@example.com\nFrom: X-INJECTED";
15+
16+
$part1["type"] = TYPEMULTIPART;
17+
$part1["subtype"] = "mixed";
18+
19+
$part2["type"] = TYPEAPPLICATION;
20+
$part2["encoding"] = ENCBINARY;
21+
$part2["subtype"] = "octet-stream\nContent-Type: X-INJECTED";
22+
$part2["description"] = "some file\nContent-Type: X-INJECTED";
23+
$part2["contents.data"] = "ABC\nContent-Type: X-INJECTED";
24+
25+
$part3["type"] = TYPETEXT;
26+
$part3["subtype"] = "plain";
27+
$part3["description"] = "description3";
28+
$part3["contents.data"] = "contents.data3\n\n\n\t";
29+
30+
$body[1] = $part1;
31+
$body[2] = $part2;
32+
$body[3] = $part3;
33+
34+
echo imap_mail_compose($envelope, $body);
35+
?>
36+
--EXPECTF--
37+
Warning: imap_mail_compose(): header injection attempt in from in %s on line %d

ext/imap/tests/bug80710_2.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Bug #80710 (imap_mail_compose() header injection) - Remail
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("imap")) die("skip imap extension not available");
6+
?>
7+
--FILE--
8+
<?php
9+
$envelope["from"]= "joe@example.com\n From : X-INJECTED";
10+
$envelope["to"] = "foo@example.com\nFrom: X-INJECTED";
11+
$envelope["cc"] = "bar@example.com\nFrom: X-INJECTED";
12+
$envelope["subject"] = "bar@example.com\n\n From : X-INJECTED";
13+
$envelope["remail"] = "X-INJECTED-REMAIL: X-INJECTED\nFrom: X-INJECTED-REMAIL-FROM"; //<--- Injected as first hdr
14+
$envelope["something"] = "bar@example.com\nFrom: X-INJECTED";
15+
16+
$part1["type"] = TYPEMULTIPART;
17+
$part1["subtype"] = "mixed";
18+
19+
$part2["type"] = TYPEAPPLICATION;
20+
$part2["encoding"] = ENCBINARY;
21+
$part2["subtype"] = "octet-stream\nContent-Type: X-INJECTED";
22+
$part2["description"] = "some file\nContent-Type: X-INJECTED";
23+
$part2["contents.data"] = "ABC\nContent-Type: X-INJECTED";
24+
25+
$part3["type"] = TYPETEXT;
26+
$part3["subtype"] = "plain";
27+
$part3["description"] = "description3";
28+
$part3["contents.data"] = "contents.data3\n\n\n\t";
29+
30+
$body[1] = $part1;
31+
$body[2] = $part2;
32+
$body[3] = $part3;
33+
34+
echo imap_mail_compose($envelope, $body);
35+
?>
36+
--EXPECTF--
37+
Warning: imap_mail_compose(): header injection attempt in remail in %s on line %d

0 commit comments

Comments
 (0)