Skip to content

Commit 3d2c810

Browse files
committed
Merge branch 'PHP-8.0'
* PHP-8.0: Fix #80710: imap_mail_compose() header injection
2 parents 476888d + a4d9ccb commit 3d2c810

File tree

3 files changed

+139
-9
lines changed

3 files changed

+139
-9
lines changed

ext/imap/php_imap.c

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3023,6 +3023,23 @@ PHP_FUNCTION(imap_fetch_overview)
30233023
}
30243024
/* }}} */
30253025

3026+
static zend_bool header_injection(zend_string *str, zend_bool adrlist)
3027+
{
3028+
char *p = ZSTR_VAL(str);
3029+
3030+
while ((p = strpbrk(p, "\r\n")) != NULL) {
3031+
if (!(p[0] == '\r' && p[1] == '\n')
3032+
/* adrlists do not support folding, but swallow trailing line breaks */
3033+
&& !((adrlist && p[1] == '\0')
3034+
/* other headers support folding */
3035+
|| !adrlist && (p[1] == ' ' || p[1] == '\t'))) {
3036+
return 1;
3037+
}
3038+
p++;
3039+
}
3040+
return 0;
3041+
}
3042+
30263043
/* {{{ Create a MIME message based on given envelope and body sections */
30273044
PHP_FUNCTION(imap_mail_compose)
30283045
{
@@ -3046,6 +3063,13 @@ PHP_FUNCTION(imap_mail_compose)
30463063
zend_argument_value_error(2, "cannot be empty");
30473064
}
30483065

3066+
#define CHECK_HEADER_INJECTION(zstr, adrlist, header) \
3067+
if (header_injection(zstr, adrlist)) { \
3068+
php_error_docref(NULL, E_WARNING, "header injection attempt in " header); \
3069+
RETVAL_FALSE; \
3070+
goto done; \
3071+
}
3072+
30493073
#define PHP_RFC822_PARSE_ADRLIST(target, value) \
30503074
str_copy = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); \
30513075
rfc822_parse_adrlist(target, str_copy, "NO HOST"); \
@@ -3054,46 +3078,57 @@ PHP_FUNCTION(imap_mail_compose)
30543078
env = mail_newenvelope();
30553079
if ((pvalue = zend_hash_str_find(envelope, "remail", sizeof("remail") - 1)) != NULL) {
30563080
convert_to_string(pvalue);
3081+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "remail");
30573082
env->remail = cpystr(Z_STRVAL_P(pvalue));
30583083
}
30593084
if ((pvalue = zend_hash_str_find(envelope, "return_path", sizeof("return_path") - 1)) != NULL) {
30603085
convert_to_string(pvalue);
3086+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "return_path");
30613087
PHP_RFC822_PARSE_ADRLIST(&env->return_path, pvalue);
30623088
}
30633089
if ((pvalue = zend_hash_str_find(envelope, "date", sizeof("date") - 1)) != NULL) {
30643090
convert_to_string(pvalue);
3091+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "date");
30653092
env->date = (unsigned char*)cpystr(Z_STRVAL_P(pvalue));
30663093
}
30673094
if ((pvalue = zend_hash_str_find(envelope, "from", sizeof("from") - 1)) != NULL) {
30683095
convert_to_string(pvalue);
3096+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "from");
30693097
PHP_RFC822_PARSE_ADRLIST(&env->from, pvalue);
30703098
}
30713099
if ((pvalue = zend_hash_str_find(envelope, "reply_to", sizeof("reply_to") - 1)) != NULL) {
30723100
convert_to_string(pvalue);
3101+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "reply_to");
30733102
PHP_RFC822_PARSE_ADRLIST(&env->reply_to, pvalue);
30743103
}
30753104
if ((pvalue = zend_hash_str_find(envelope, "in_reply_to", sizeof("in_reply_to") - 1)) != NULL) {
30763105
convert_to_string(pvalue);
3106+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "in_reply_to");
30773107
env->in_reply_to = cpystr(Z_STRVAL_P(pvalue));
30783108
}
30793109
if ((pvalue = zend_hash_str_find(envelope, "subject", sizeof("subject") - 1)) != NULL) {
30803110
convert_to_string(pvalue);
3111+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "subject");
30813112
env->subject = cpystr(Z_STRVAL_P(pvalue));
30823113
}
30833114
if ((pvalue = zend_hash_str_find(envelope, "to", sizeof("to") - 1)) != NULL) {
30843115
convert_to_string(pvalue);
3116+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "to");
30853117
PHP_RFC822_PARSE_ADRLIST(&env->to, pvalue);
30863118
}
30873119
if ((pvalue = zend_hash_str_find(envelope, "cc", sizeof("cc") - 1)) != NULL) {
30883120
convert_to_string(pvalue);
3121+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "cc");
30893122
PHP_RFC822_PARSE_ADRLIST(&env->cc, pvalue);
30903123
}
30913124
if ((pvalue = zend_hash_str_find(envelope, "bcc", sizeof("bcc") - 1)) != NULL) {
30923125
convert_to_string(pvalue);
3126+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "bcc");
30933127
PHP_RFC822_PARSE_ADRLIST(&env->bcc, pvalue);
30943128
}
30953129
if ((pvalue = zend_hash_str_find(envelope, "message_id", sizeof("message_id") - 1)) != NULL) {
30963130
convert_to_string(pvalue);
3131+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "message_id");
30973132
env->message_id=cpystr(Z_STRVAL_P(pvalue));
30983133
}
30993134

@@ -3104,6 +3139,7 @@ PHP_FUNCTION(imap_mail_compose)
31043139
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pvalue), env_data) {
31053140
custom_headers_param = mail_newbody_parameter();
31063141
convert_to_string(env_data);
3142+
CHECK_HEADER_INJECTION(Z_STR_P(env_data), 0, "custom_headers");
31073143
custom_headers_param->value = (char *) fs_get(Z_STRLEN_P(env_data) + 1);
31083144
custom_headers_param->attribute = NULL;
31093145
memcpy(custom_headers_param->value, Z_STRVAL_P(env_data), Z_STRLEN_P(env_data) + 1);
@@ -3146,6 +3182,7 @@ PHP_FUNCTION(imap_mail_compose)
31463182
}
31473183
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
31483184
convert_to_string(pvalue);
3185+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
31493186
tmp_param = mail_newbody_parameter();
31503187
tmp_param->value = cpystr(Z_STRVAL_P(pvalue));
31513188
tmp_param->attribute = cpystr("CHARSET");
@@ -3158,9 +3195,11 @@ PHP_FUNCTION(imap_mail_compose)
31583195
SEPARATE_ARRAY(pvalue);
31593196
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
31603197
if (key == NULL) continue;
3198+
CHECK_HEADER_INJECTION(key, 0, "body disposition key");
31613199
disp_param = mail_newbody_parameter();
31623200
disp_param->attribute = cpystr(ZSTR_VAL(key));
31633201
convert_to_string(disp_data);
3202+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
31643203
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
31653204
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
31663205
disp_param->next = tmp_param;
@@ -3170,19 +3209,23 @@ PHP_FUNCTION(imap_mail_compose)
31703209
}
31713210
}
31723211
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
3173-
convert_to_string(pvalue);
3212+
convert_to_string_ex(pvalue);
3213+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
31743214
bod->subtype = cpystr(Z_STRVAL_P(pvalue));
31753215
}
31763216
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
31773217
convert_to_string(pvalue);
3218+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
31783219
bod->id = cpystr(Z_STRVAL_P(pvalue));
31793220
}
31803221
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
31813222
convert_to_string(pvalue);
3223+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
31823224
bod->description = cpystr(Z_STRVAL_P(pvalue));
31833225
}
31843226
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
31853227
convert_to_string(pvalue);
3228+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
31863229
bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
31873230
memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
31883231
}
@@ -3192,9 +3235,11 @@ PHP_FUNCTION(imap_mail_compose)
31923235
SEPARATE_ARRAY(pvalue);
31933236
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
31943237
if (key == NULL) continue;
3238+
CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
31953239
disp_param = mail_newbody_parameter();
31963240
disp_param->attribute = cpystr(ZSTR_VAL(key));
31973241
convert_to_string(disp_data);
3242+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
31983243
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
31993244
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
32003245
disp_param->next = tmp_param;
@@ -3225,6 +3270,7 @@ PHP_FUNCTION(imap_mail_compose)
32253270
}
32263271
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
32273272
convert_to_string(pvalue);
3273+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
32283274
bod->md5 = cpystr(Z_STRVAL_P(pvalue));
32293275
}
32303276
} else if (Z_TYPE_P(data) == IS_ARRAY && topbod->type == TYPEMULTIPART) {
@@ -3256,7 +3302,8 @@ PHP_FUNCTION(imap_mail_compose)
32563302
}
32573303
}
32583304
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
3259-
convert_to_string(pvalue);
3305+
convert_to_string_ex(pvalue);
3306+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
32603307
tmp_param = mail_newbody_parameter();
32613308
tmp_param->value = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
32623309
memcpy(tmp_param->value, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue) + 1);
@@ -3270,9 +3317,11 @@ PHP_FUNCTION(imap_mail_compose)
32703317
SEPARATE_ARRAY(pvalue);
32713318
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
32723319
if (key == NULL) continue;
3320+
CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
32733321
disp_param = mail_newbody_parameter();
32743322
disp_param->attribute = cpystr(ZSTR_VAL(key));
3275-
convert_to_string(disp_data);
3323+
convert_to_string_ex(disp_data);
3324+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
32763325
disp_param->value = (char *)fs_get(Z_STRLEN_P(disp_data) + 1);
32773326
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
32783327
disp_param->next = tmp_param;
@@ -3282,19 +3331,23 @@ PHP_FUNCTION(imap_mail_compose)
32823331
}
32833332
}
32843333
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
3285-
convert_to_string(pvalue);
3334+
convert_to_string_ex(pvalue);
3335+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
32863336
bod->subtype = cpystr(Z_STRVAL_P(pvalue));
32873337
}
32883338
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
3289-
convert_to_string(pvalue);
3339+
convert_to_string_ex(pvalue);
3340+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
32903341
bod->id = cpystr(Z_STRVAL_P(pvalue));
32913342
}
32923343
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
3293-
convert_to_string(pvalue);
3344+
convert_to_string_ex(pvalue);
3345+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
32943346
bod->description = cpystr(Z_STRVAL_P(pvalue));
32953347
}
32963348
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
3297-
convert_to_string(pvalue);
3349+
convert_to_string_ex(pvalue);
3350+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
32983351
bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
32993352
memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
33003353
}
@@ -3304,9 +3357,11 @@ PHP_FUNCTION(imap_mail_compose)
33043357
SEPARATE_ARRAY(pvalue);
33053358
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
33063359
if (key == NULL) continue;
3360+
CHECK_HEADER_INJECTION(key, 0, "body disposition key");
33073361
disp_param = mail_newbody_parameter();
33083362
disp_param->attribute = cpystr(ZSTR_VAL(key));
3309-
convert_to_string(disp_data);
3363+
convert_to_string_ex(disp_data);
3364+
CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
33103365
disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
33113366
memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
33123367
disp_param->next = tmp_param;
@@ -3336,7 +3391,8 @@ PHP_FUNCTION(imap_mail_compose)
33363391
bod->size.bytes = zval_get_long(pvalue);
33373392
}
33383393
if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
3339-
convert_to_string(pvalue);
3394+
convert_to_string_ex(pvalue);
3395+
CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
33403396
bod->md5 = cpystr(Z_STRVAL_P(pvalue));
33413397
}
33423398
}

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)