title: How can I allow PHP to use the at command in exec() calls when SELinux is enabled?
body: <p>I have a PHP script that takes a long time to load. We have moved our sites to a new server which lives behind a cloud load balancer. The maximum time limit for the load balancer is 120 seconds, but the script takes well over 5 minutes. Splitting the script up is not an option.</p>
  
  <p>I rewrote the script so it would run on the command line, and I can successfully call this:</p>
  
  <pre><code>php -f /path/to/long_php.php &gt; /path/to/log_file.php
  </code></pre>
  
  <p>I can then call this from PHP by doing this:</p>
  
  <pre><code>exec('php -f /path/to/long_php.php &gt; /path/to/log_file.php');
  </code></pre>
  
  <p>Of course, because this script takes a long time to load, I don't want the PHP page to wait. Back on the command line I successfully used the <code>at</code> command like so:</p>
  
  <pre><code>echo "php -f /path/to/long_php.php &gt; /path/to/log_file.php" | at now
  </code></pre>
  
  <p>So, I expected when running a similar thing in PHP it would work:</p>
  
  <pre><code>exec('echo "php -f /path/to/long_php.php &gt; /path/to/log_file.php" | at now');
  </code></pre>
  
  <p>However this doesn't work. Unlike all the previou tries, the final command here give me SELinux errors:</p>
  
  <pre><code>----
  type=SYSCALL msg=audit(07/25/2014 21:12:50.027:793672) : arch=x86_64 syscall=open success=no exit=-13(Permission denied) a0=7fd6fc2186bb a1=80000 a2=1b6 a3=0 items=0 ppid=55040 pid=55041 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=unix_chkpwd exe=/sbin/unix_chkpwd subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  type=AVC msg=audit(07/25/2014 21:12:50.027:793672) : avc:  denied  { read } for  pid=55041 comm=unix_chkpwd name=shadow dev=md2 ino=11797556 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file
  ----
  type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793673) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=7fff46547e40 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  type=AVC msg=audit(07/25/2014 21:12:50.028:793673) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket
  ----
  type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793674) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=1 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  type=AVC msg=audit(07/25/2014 21:12:50.028:793674) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket
  ----
  type=SYSCALL msg=audit(07/25/2014 21:12:50.028:793675) : arch=x86_64 syscall=socket success=no exit=-13(Permission denied) a0=10 a1=3 a2=9 a3=7fff46547eb0 items=0 ppid=55038 pid=55040 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1517 comm=at exe=/usr/bin/at subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  type=AVC msg=audit(07/25/2014 21:12:50.028:793675) : avc:  denied  { create } for  pid=55040 comm=at scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=netlink_audit_socket
  </code></pre>
  
  <p><strong>Note:</strong> the above is from <code>/var/log/audit/audit.log</code> piped through <code>ausearch -i</code></p>
  
  <p>After this I piped those in to <code>audit2allow -w</code> to see why, and the first error was regarding <code>allow_httpd_mod_auth_pam</code> not being enabled. I then enabled this and got some other errors. Running them through <code>audit2allow -M tmpat</code> produced me <code>tmpat.pp</code>, with an associated <code>tmpat.te</code>:</p>
  
  <pre><code>module tmpat 1.0;
  
  require {
          type initrc_var_run_t;
          type httpd_t;
          class capability audit_control;
          class file read;
  }
  
  #============= httpd_t ==============
  allow httpd_t initrc_var_run_t:file read;
  allow httpd_t self:capability audit_control;
  </code></pre>
  
  <p>Unfortunately, when I run <code>semodule -i tmpat.pp</code> it still doesn't work. Repeating the process of using <code>audit2allow -w</code> above gives me more of the same <code>Missing type enforcement (TE) allow rule.</code> errors. So with that in mind, I'm hoping someone can tell me what changes I need to make to the generated <code>tmpat.te</code> to make this work?</p>
  
  <p><strong>Note:</strong> I'm on CentOS 6.5, with Apache 2.2, PHP 5.4</p>
  
  <p><strong>Update:</strong></p>
  
  <p>After looking in to policies, albeit it being something new to me, and I came up with the following:</p>
  
  <pre><code>module tmpat 1.1;
    require {
    type tmp_t;
    type httpd_t;
    class file { create getattr open read rename unlink write };
    class dir { add_name getattr open read remove_name search write };
  }
  
  #============= httpd_t ==============
  allow httpd_t tmp_t:file { create getattr open read rename unlink write };
  allow httpd_t tmp_t:dir { add_name getattr open read remove_name search write };
  </code></pre>
  
  <p>Currently this compiles but won't install, giving me this:</p>
  
  <pre><code>[root@localhost tmp]# semodule -i tmpat
  semodule:  Failed on tmpat!
  </code></pre>
  
  <p>...but hopefully this might give some insight in to what I'm trying to do.</p>