diff --git a/ext/imap/php_imap.c b/ext/imap/php_imap.c index 9ff1ae06188..806d9d1bba3 100644 --- a/ext/imap/php_imap.c +++ b/ext/imap/php_imap.c @@ -3063,6 +3063,23 @@ PHP_FUNCTION(imap_fetch_overview) } /* }}} */ +static zend_bool header_injection(zend_string *str, zend_bool adrlist) +{ + char *p = ZSTR_VAL(str); + + while ((p = strpbrk(p, "\r\n")) != NULL) { + if (!(p[0] == '\r' && p[1] == '\n') + /* adrlists do not support folding, but swallow trailing line breaks */ + && !((adrlist && p[1] == '\0') + /* other headers support folding */ + || !adrlist && (p[1] == ' ' || p[1] == '\t'))) { + return 1; + } + p++; + } + return 0; +} + /* {{{ Create a MIME message based on given envelope and body sections */ PHP_FUNCTION(imap_mail_compose) { @@ -3086,6 +3103,13 @@ PHP_FUNCTION(imap_mail_compose) zend_argument_value_error(2, "cannot be empty"); } +#define CHECK_HEADER_INJECTION(zstr, adrlist, header) \ + if (header_injection(zstr, adrlist)) { \ + php_error_docref(NULL, E_WARNING, "header injection attempt in " header); \ + RETVAL_FALSE; \ + goto done; \ + } + #define PHP_RFC822_PARSE_ADRLIST(target, value) \ str_copy = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); \ rfc822_parse_adrlist(target, str_copy, "NO HOST"); \ @@ -3094,46 +3118,57 @@ PHP_FUNCTION(imap_mail_compose) env = mail_newenvelope(); if ((pvalue = zend_hash_str_find(envelope, "remail", sizeof("remail") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "remail"); env->remail = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(envelope, "return_path", sizeof("return_path") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "return_path"); PHP_RFC822_PARSE_ADRLIST(&env->return_path, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "date", sizeof("date") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "date"); env->date = (unsigned char*)cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(envelope, "from", sizeof("from") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "from"); PHP_RFC822_PARSE_ADRLIST(&env->from, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "reply_to", sizeof("reply_to") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "reply_to"); PHP_RFC822_PARSE_ADRLIST(&env->reply_to, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "in_reply_to", sizeof("in_reply_to") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "in_reply_to"); env->in_reply_to = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(envelope, "subject", sizeof("subject") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "subject"); env->subject = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(envelope, "to", sizeof("to") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "to"); PHP_RFC822_PARSE_ADRLIST(&env->to, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "cc", sizeof("cc") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "cc"); PHP_RFC822_PARSE_ADRLIST(&env->cc, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "bcc", sizeof("bcc") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "bcc"); PHP_RFC822_PARSE_ADRLIST(&env->bcc, pvalue); } if ((pvalue = zend_hash_str_find(envelope, "message_id", sizeof("message_id") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "message_id"); env->message_id=cpystr(Z_STRVAL_P(pvalue)); } @@ -3144,6 +3179,7 @@ PHP_FUNCTION(imap_mail_compose) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pvalue), env_data) { custom_headers_param = mail_newbody_parameter(); convert_to_string_ex(env_data); + CHECK_HEADER_INJECTION(Z_STR_P(env_data), 0, "custom_headers"); custom_headers_param->value = (char *) fs_get(Z_STRLEN_P(env_data) + 1); custom_headers_param->attribute = NULL; 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) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset"); tmp_param = mail_newbody_parameter(); tmp_param->value = cpystr(Z_STRVAL_P(pvalue)); tmp_param->attribute = cpystr("CHARSET"); @@ -3198,9 +3235,11 @@ PHP_FUNCTION(imap_mail_compose) SEPARATE_ARRAY(pvalue); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) { if (key == NULL) continue; + CHECK_HEADER_INJECTION(key, 0, "body disposition key"); disp_param = mail_newbody_parameter(); disp_param->attribute = cpystr(ZSTR_VAL(key)); convert_to_string_ex(disp_data); + CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value"); disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1); memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1); disp_param->next = tmp_param; @@ -3211,18 +3250,22 @@ PHP_FUNCTION(imap_mail_compose) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype"); bod->subtype = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id"); bod->id = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description"); bod->description = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type"); bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1); memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1); } @@ -3232,9 +3275,11 @@ PHP_FUNCTION(imap_mail_compose) SEPARATE_ARRAY(pvalue); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) { if (key == NULL) continue; + CHECK_HEADER_INJECTION(key, 0, "body type.parameters key"); disp_param = mail_newbody_parameter(); disp_param->attribute = cpystr(ZSTR_VAL(key)); convert_to_string_ex(disp_data); + CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value"); disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1); memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1); disp_param->next = tmp_param; @@ -3265,6 +3310,7 @@ PHP_FUNCTION(imap_mail_compose) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5"); bod->md5 = cpystr(Z_STRVAL_P(pvalue)); } } else if (Z_TYPE_P(data) == IS_ARRAY && topbod->type == TYPEMULTIPART) { @@ -3297,6 +3343,7 @@ PHP_FUNCTION(imap_mail_compose) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset"); tmp_param = mail_newbody_parameter(); tmp_param->value = (char *) fs_get(Z_STRLEN_P(pvalue) + 1); memcpy(tmp_param->value, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue) + 1); @@ -3310,9 +3357,11 @@ PHP_FUNCTION(imap_mail_compose) SEPARATE_ARRAY(pvalue); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) { if (key == NULL) continue; + CHECK_HEADER_INJECTION(key, 0, "body type.parameters key"); disp_param = mail_newbody_parameter(); disp_param->attribute = cpystr(ZSTR_VAL(key)); convert_to_string_ex(disp_data); + CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value"); disp_param->value = (char *)fs_get(Z_STRLEN_P(disp_data) + 1); memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1); disp_param->next = tmp_param; @@ -3323,18 +3372,22 @@ PHP_FUNCTION(imap_mail_compose) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype"); bod->subtype = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id"); bod->id = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description"); bod->description = cpystr(Z_STRVAL_P(pvalue)); } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type"); bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1); memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1); } @@ -3344,9 +3397,11 @@ PHP_FUNCTION(imap_mail_compose) SEPARATE_ARRAY(pvalue); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) { if (key == NULL) continue; + CHECK_HEADER_INJECTION(key, 0, "body disposition key"); disp_param = mail_newbody_parameter(); disp_param->attribute = cpystr(ZSTR_VAL(key)); convert_to_string_ex(disp_data); + CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value"); disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1); memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1); disp_param->next = tmp_param; @@ -3377,6 +3432,7 @@ PHP_FUNCTION(imap_mail_compose) } if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) { convert_to_string_ex(pvalue); + CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5"); bod->md5 = cpystr(Z_STRVAL_P(pvalue)); } } diff --git a/ext/imap/tests/bug80710_1.phpt b/ext/imap/tests/bug80710_1.phpt new file mode 100644 index 00000000000..5cdee034017 --- /dev/null +++ b/ext/imap/tests/bug80710_1.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug #80710 (imap_mail_compose() header injection) - MIME Splitting Attack +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: imap_mail_compose(): header injection attempt in from in %s on line %d diff --git a/ext/imap/tests/bug80710_2.phpt b/ext/imap/tests/bug80710_2.phpt new file mode 100644 index 00000000000..b9f2fa8544e --- /dev/null +++ b/ext/imap/tests/bug80710_2.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug #80710 (imap_mail_compose() header injection) - Remail +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: imap_mail_compose(): header injection attempt in remail in %s on line %d