Hide Forgot
Description of problem: Managing confined services guide said: httpd_can_network_connect - When disabled, this Boolean prevents HTTP scripts and modules from initiating a connection to a network or remote port. Turn this Boolean on to allow this access." If I understand that correctly, SELinux should prevent php scripts (running via mod_php) from opening remote urls with fopen function by default, that is when httpd_can_network_connect --> off Here are the links that confirm that behavior in the past: http://www.php.net/manual/en/function.fopen.php#56551 https://bugzilla.redhat.com/show_bug.cgi?id=164700 My problem is that Apache, fopen function of php running via mod_php and every program that php executes (like wget, so it runs in httpd_t context) are all allowed access to remote machine's port 80/tcp. SELinux IS preventing Apache, php and httpd_t programs from accessing eg. port 31254/tcp, but not port 80/tcp. Version-Release number of selected component (if applicable): [root@turncoat ~]# uname -a Linux turncoat.ha.rs 2.6.32-131.17.1.el6.x86_64 #1 SMP Thu Sep 29 10:24:25 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux [root@turncoat ~]# cat /etc/redhat-release Red Hat Enterprise Linux Server release 6.1 (Santiago) [root@turncoat ~]# semanage -o - boolean -D login -D login -a -s unconfined_u -r 's0-s0:c0.c1023' __default__ login -a -s unconfined_u -r 's0-s0:c0.c1023' root login -a -s system_u -r 's0-s0:c0.c1023' system_u user -D port -D interface -D node -D fcontext -D [root@turncoat ~]# getsebool -a|grep http allow_httpd_anon_write --> off allow_httpd_mod_auth_ntlm_winbind --> off allow_httpd_mod_auth_pam --> off allow_httpd_sys_script_anon_write --> off httpd_builtin_scripting --> on httpd_can_check_spam --> off httpd_can_network_connect --> off httpd_can_network_connect_cobbler --> off httpd_can_network_connect_db --> off httpd_can_network_memcache --> off httpd_can_network_relay --> off httpd_can_sendmail --> off httpd_dbus_avahi --> on httpd_enable_cgi --> on httpd_enable_ftp_server --> off httpd_enable_homedirs --> off httpd_execmem --> off httpd_read_user_content --> off httpd_setrlimit --> off httpd_ssi_exec --> off httpd_tmp_exec --> off httpd_tty_comm --> on httpd_unified --> on httpd_use_cifs --> off httpd_use_gpg --> off httpd_use_nfs --> off [root@turncoat ~]# sestatus SELinux status: enabled SELinuxfs mount: /selinux Current mode: enforcing Mode from config file: enforcing Policy version: 24 Policy from config file: targeted [root@turncoat ~]# rpm -qa|grep selinux libselinux-2.0.94-5.el6.x86_64 selinux-policy-3.7.19-93.el6_1.7.noarch selinux-policy-targeted-3.7.19-93.el6_1.7.noarch libselinux-python-2.0.94-5.el6.x86_64 libselinux-utils-2.0.94-5.el6.x86_64 How reproducible: Always Steps to Reproduce: 1. yum install httpd mod_php 2. create /var/www/html/marko1.php with: <?php $file = fopen ("http://www.example.com", "r"); if (!$file) { echo "<p>Unable to open remote file.\n"; exit; } while (!feof ($file)) { $line = fgets ($file, 1024); echo ($line); } fclose($file); ?> 3. create /var/www/html/marko2.php with: <?php $file = fopen ("http://www.example.com:31254/", "r"); if (!$file) { echo "<p>Unable to open remote file.\n"; exit; } while (!feof ($file)) { $line = fgets ($file, 1024); echo ($line); } fclose($file); ?> 4. create /var/www/html/marko3.php with: <?php echo exec('whoami'); echo exec ('id -Z'); exec ('wget http://www.example.com -O /tmp/example.html'); exec ('wget http://www.example.com:31254 -O /tmp/example-31254.html'); ?> Actual results: - By opening in browser marko1.php, the html code from www.example.com is delivered (which is bad). No AVC's, no prevention of accessing remote machine's port 80/tcp and packets are actually sent over network. - By opening in browser marko2.php, the "Unable to open remote file." message is reported (which is good), and AVC message is reported: type=AVC msg=audit(1320417644.481:140): avc: denied { name_connect } for pid=1152 comm="httpd" dest=31254 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket - By opening in browser marko3.php, the /tmp/example.html file is created on server with content from http://www.example.com (which is bad). The /tmp/example-31254.html file is also created, but it is empty (which is good) as wget is prevented to access remote network with AVC message (which is good): type=AVC msg=audit(1320417649.970:141): avc: denied { name_connect } for pid=1306 comm="wget" dest=31254 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket Expected results: When disabled, httpd_can_network_connect boolean should prevent HTTP scripts, modules and programs running under httpd_t context from initiating a connection to a network or remote port, no matter what remote port is in question. Additional info: No proxy* modules are enabled in apache's httpd.conf (and the result is the same when they are) and I can verify that tcp packets have been sent over network as a result of accessing those php files in browser.
This is really not a bug, although we could do better. apachectl requires this access to shut down an apache server. We probably could tighten it do be only local host.
Joe what do you think?
"apachectl requires this access to shut down an apache server" Specifically, the requirement is that the httpd parent must be able to connect to port 80 on addresses bound to local interfaces by default. Yes, it is certainly desirable that php scripts cannot make outgoing connections to port 80 by default, that would help prevent e.g. canned attacks against bad PHP webapps etc - the first step is often to wget some toolkit from a remote site. Would it be possible for to have separate roles for httpd parent and children? Or else is there separation between making a TCP connection to the local host vs a remote address which can be expressed in the rule? (and is the distinction between "local" and "remote" more sophisticated than "::1 and 127.0.0.1" vs everything else?)
Joe I don't understand what you mean by parent and child? Are there two different process here? Or is apachectl connecting to the httpd service via port 80? I would try this out on my F17 box but I have hosed it up trying to setup PrivateTmp for httpd. Currently in F16 we are not allowing httpd_t to connect to port 80 by default, and I want to know if that breaks apachectl.
I am not sure why you keep refering to "apachectl" specifically. httpd runs a monitoring process, the parent, and a bunch of child processes which handle connections. The parent runs as root, the children all setuid() to uid/gid apache once forked, and only handle connections once running as that less-privileged user. When the httpd init script, or systemd, or apachectl want to gracefully restart httpd to load a new configuration, it works as following: a) the init script/apachectl/systemd send a signal to the httpd parent process b) the parent process sends signals to all running child processes c) the parent process sends dummy HTTP requests to address/port combinations on which it knows the children are listening. This ensures they pop out of accept() if they are blocked there. d) existing children all terminate, and new ones are started up using the new configuration (c) is the requirement to be able to connect to port 80 on the local host by default.
Ok thanks for the explanation. Of course httpd is not forking and execing so we can not use program transitions to run the parent as a different context then the children. One option would be to add SELinux smarts to httpd to let it to a setexec() httpd_parent_t -> fork() setcur(httpd_t) Then just allow httpd_parent_t to connect to http_port_t by default. Or we turn on labelling of the localhost nodes/ethernet and only allow httpd_t to connect send/recv packets over localhost ether net or notes, and allow httpd_t to connect to http_port_t, But I am not sure we can block that since we have to allow httpd_t to send/recv packets for DNS name resolution.