diff --git a/src/pretix/base/email.py b/src/pretix/base/email.py index db3ee6299a..dc0a59915d 100644 --- a/src/pretix/base/email.py +++ b/src/pretix/base/email.py @@ -19,7 +19,6 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -import ipaddress import logging import smtplib import socket @@ -52,13 +51,12 @@ from pretix.base.services.placeholders import ( # noqa SimpleFunctionalTextPlaceholder as SimpleFunctionalMailTextPlaceholder, ) from pretix.base.settings import get_name_parts_localized # noqa +from pretix.helpers.ssrf import should_block_access logger = logging.getLogger('pretix.base.email') T = TypeVar("T", bound=EmailBackend) -_cgnat_net = ipaddress.ip_network('100.64.0.0/10') - def test_custom_smtp_backend(backend: T, from_addr: str) -> None: try: @@ -254,16 +252,9 @@ def create_connection(address, timeout=socket.getdefaulttimeout(), af, socktype, proto, canonname, sa = res if not getattr(settings, "MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS", False): - ip_addr = ipaddress.ip_address(sa[0]) - check_ip4 = ip_addr.ipv4_mapped if getattr(ip_addr, "ipv4_mapped", None) else ip_addr - if ip_addr.is_multicast: - raise socket.error(f"Request to multicast address {sa[0]} blocked") - if ip_addr.is_loopback or ip_addr.is_link_local: - raise socket.error(f"Request to local address {sa[0]} blocked") - if ip_addr.is_private: - raise socket.error(f"Request to private address {sa[0]} blocked") - if check_ip4 in _cgnat_net: - raise socket.error(f"Request to RFC 6598 address {sa[0]} blocked") + is_private, msg = should_block_access(sa) + if is_private: + raise socket.error(msg) sock = None try: diff --git a/src/pretix/helpers/monkeypatching.py b/src/pretix/helpers/monkeypatching.py index 39150e3341..a92b3785b0 100644 --- a/src/pretix/helpers/monkeypatching.py +++ b/src/pretix/helpers/monkeypatching.py @@ -19,7 +19,6 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -import ipaddress import socket import sys import types @@ -42,8 +41,7 @@ from urllib3.util.connection import ( from urllib3.util.timeout import _DEFAULT_TIMEOUT from pretix.helpers.reportlab import ThumbnailingImageReader - -_cgnat_net = ipaddress.ip_network('100.64.0.0/10') +from pretix.helpers.ssrf import should_block_access def monkeypatch_vobject_performance(): @@ -150,16 +148,9 @@ def monkeypatch_urllib3_ssrf_protection(): af, socktype, proto, canonname, sa = res if not getattr(settings, "ALLOW_HTTP_TO_PRIVATE_NETWORKS", False): - ip_addr = ipaddress.ip_address(sa[0]) - check_ip4 = ip_addr.ipv4_mapped if getattr(ip_addr, "ipv4_mapped", None) else ip_addr - if ip_addr.is_multicast: - raise HTTPError(f"Request to multicast address {sa[0]} blocked") - if ip_addr.is_loopback or ip_addr.is_link_local: - raise HTTPError(f"Request to local address {sa[0]} blocked") - if ip_addr.is_private: - raise HTTPError(f"Request to private address {sa[0]} blocked") - if check_ip4 in _cgnat_net: - raise HTTPError(f"Request to RFC 6598 address {sa[0]} blocked") + is_private, msg = should_block_access(sa) + if is_private: + raise HTTPError(msg) sock = None try: diff --git a/src/pretix/helpers/ssrf.py b/src/pretix/helpers/ssrf.py new file mode 100644 index 0000000000..03598ee4a9 --- /dev/null +++ b/src/pretix/helpers/ssrf.py @@ -0,0 +1,19 @@ +import ipaddress + + +_cgnat_net = ipaddress.ip_network('100.64.0.0/10') + + +def should_block_access(sa): + ip_addr = ipaddress.ip_address(sa[0]) + check_ip4 = ip_addr.ipv4_mapped if getattr(ip_addr, "ipv4_mapped", None) else ip_addr + if ip_addr.is_multicast: + return True, f"Request to multicast address {sa[0]} blocked" + if ip_addr.is_loopback or ip_addr.is_link_local: + return True, f"Request to local address {sa[0]} blocked" + if ip_addr.is_private: + return True, f"Request to private address {sa[0]} blocked" + if check_ip4 in _cgnat_net: + return True, f"Request to RFC 6598 address {sa[0]} blocked" + + return False, None