Bug 1482251

Summary: pydns source port selection does not use ephemeral range
Product: [Fedora] Fedora EPEL Reporter: exions <jbubeck>
Component: python-pydnsAssignee: Paul Wouters <paul.wouters>
Status: CLOSED WONTFIX QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: medium Docs Contact:
Priority: unspecified    
Version: epel7CC: jbubeck, pwouters
Target Milestone: ---   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2023-07-06 19:06:05 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
Example script
none
Example TE policy
none
Example file context for policy none

Description exions 2017-08-16 21:00:12 UTC
Created attachment 1314403 [details]
Example script

Description of problem:
PyDNS randomly binds to TCP or UDP source ports from the range 1024-65535 as a defense against cache poisoning.  Some ports in this range are defined as SELinux ports.  Someone who is writing an SELinux policy to confine a python script that uses PyDNS will either have to:

* Accept that ~1% of TCP queries will fail
* Make the SELinux policy allow more than needed (i.e. corenet_tcp_bind_all_ports)


Version-Release number of selected component (if applicable):
2.3.6-2.el7


How reproducible:
Basic SELinux policy files and python script to demonstrate the issue are attached.


Steps to Reproduce:
1. Build and install SELinux policy
Place pydnstest.te and pydnstest.fc in ~/pydnstest/
$ cd ~/pydnstest
$ make -f /usr/share/selinux/devel/Makefile
# semodule -i ~/pydnstest/pydnstest.pp
# semanage dontaudit off

2. Install script
Place pydnstest.py in /usr/local/bin/
# restorecon /usr/local/bin/pydnstest.py

3. Run script
$ runcon system_u:system_r:pydnstest_t:s0 /usr/local/bin/pydnstest.py


Actual results:
SELinux denials occur during approx. 1% of lookups.

$ runcon system_u:system_r:pydnstest_t:s0 /usr/local/bin/pydnstest.py 
Lookup 107 failed: SocketError(error(13, 'Permission denied'),)
Lookup 176 failed: SocketError(error(13, 'Permission denied'),)

# ausearch -ts today 20:21 -i -m avc -su pydnstest_t | audit2allow


#============= pydnstest_t ==============
allow pydnstest_t i18n_input_port_t:tcp_socket name_bind;
allow pydnstest_t munin_port_t:tcp_socket name_bind;

In this case, ports 4949 and 9010 were among the 200 randomly selected source ports.  Since they were already defined as SELinux ports (i18n_input_port_t and munin_port_t), it resulted in denials.


Expected results:
DNS lookups use source ports in the ephemeral range, avoiding conflicts with defined SELinux ports.


Additional info:
Changing source_port to 0 makes the kernel assign a random-looking port in the ephemeral range.  Making this change resulted in no SELinux denials with the attached script/policy:

--- /usr/lib/python2.7/site-packages/DNS/Base.py.orig	2017-05-18 16:17:50.000000000 +0000
+++ /usr/lib/python2.7/site-packages/DNS/Base.py	2017-08-16 20:30:05.717033232 +0000
@@ -181,7 +181,7 @@
         "Pick random source port to avoid DNS cache poisoning attack."
         while True:
             try:
-                source_port = random.randint(1024,65535)
+                source_port = 0
                 self.s.bind(('', source_port))
                 break
             except socket.error, msg:

This reduces the pool of random source ports from 64512 to 28232.  This shouldn't matter for TCP, but will weaken this defense against DNS cache poisoning attacks for UDP queries.

Comment 1 exions 2017-08-16 21:01:23 UTC
Created attachment 1314404 [details]
Example TE policy

Comment 2 exions 2017-08-16 21:02:03 UTC
Created attachment 1314405 [details]
Example file context for policy

Comment 3 Fedora Admin user for bugzilla script actions 2021-04-26 12:34:16 UTC
This package has changed maintainer in Fedora. Reassigning to the new maintainer of this component.

Comment 4 Paul Wouters 2023-07-06 19:06:05 UTC
This package is dead since fedora 31. Please use python3-dns or python3-unbound