diff --git a/refpolicy/Changelog b/refpolicy/Changelog
index f10b4ec1a..d939d4f02 100644
--- a/refpolicy/Changelog
+++ b/refpolicy/Changelog
@@ -1,3 +1,4 @@
+- Add ftpdctl domain to ftp, from Paul Howarth.
- Fix build system to not move type declarations out of optionals.
- Add gcc-config domain to portage.
- Add packet object class and support in corenetwork.
diff --git a/refpolicy/policy/modules/services/ftp.fc b/refpolicy/policy/modules/services/ftp.fc
index 2967dd74c..5ea69a0da 100644
--- a/refpolicy/policy/modules/services/ftp.fc
+++ b/refpolicy/policy/modules/services/ftp.fc
@@ -7,6 +7,8 @@
#
# /usr
#
+/usr/bin/ftpdctl -- gen_context(system_u:object_r:ftpdctl_exec_t,s0)
+
/usr/kerberos/sbin/ftpd -- gen_context(system_u:object_r:ftpd_exec_t,s0)
/usr/sbin/ftpwho -- gen_context(system_u:object_r:ftpd_exec_t,s0)
diff --git a/refpolicy/policy/modules/services/ftp.if b/refpolicy/policy/modules/services/ftp.if
index 9b8931546..113e56cb8 100644
--- a/refpolicy/policy/modules/services/ftp.if
+++ b/refpolicy/policy/modules/services/ftp.if
@@ -109,3 +109,26 @@ interface(`ftp_read_log',`
logging_search_logs($1)
allow $1 xferlog_t:file r_file_perms;
')
+
+########################################
+##
+## Execute the ftpdctl program in the ftpdctl domain.
+##
+##
+##
+## Domain allowed access.
+##
+##
+#
+interface(`ftp_domtrans_ftpdctl',`
+ gen_require(`
+ type ftpdctl_t, ftpdctl_exec_t;
+ ')
+
+ corecmd_search_bin($1)
+ domain_auto_trans($1, ftpdctl_exec_t, ftpdctl_t)
+
+ allow ftpdctl_t $1:fd use;
+ allow ftpdctl_t $1:fifo_file rw_file_perms;
+ allow ftpdctl_t $1:process sigchld;
+')
diff --git a/refpolicy/policy/modules/services/ftp.te b/refpolicy/policy/modules/services/ftp.te
index 7ef09117e..fb09648f5 100644
--- a/refpolicy/policy/modules/services/ftp.te
+++ b/refpolicy/policy/modules/services/ftp.te
@@ -26,12 +26,19 @@ files_tmpfs_file(ftpd_tmpfs_t)
type ftpd_var_run_t;
files_pid_file(ftpd_var_run_t)
+type ftpdctl_t;
+type ftpdctl_exec_t;
+init_system_domain(ftpdctl_t,ftpdctl_exec_t)
+
+type ftpdctl_tmp_t;
+files_tmp_file(ftpdctl_tmp_t)
+
type xferlog_t;
logging_log_file(xferlog_t)
########################################
#
-# Local policy
+# ftpd local policy
#
allow ftpd_t self:capability { chown fowner fsetid setgid setuid sys_chroot sys_nice sys_resource };
@@ -40,7 +47,7 @@ allow ftpd_t self:process signal_perms;
allow ftpd_t self:process { getcap setcap setsched setrlimit };
allow ftpd_t self:fifo_file rw_file_perms;
allow ftpd_t self:unix_dgram_socket { sendto create_socket_perms };
-allow ftpd_t self:unix_stream_socket create_socket_perms;
+allow ftpd_t self:unix_stream_socket create_stream_socket_perms;
allow ftpd_t self:tcp_socket create_stream_socket_perms;
allow ftpd_t self:udp_socket create_socket_perms;
@@ -62,6 +69,12 @@ allow ftpd_t ftpd_var_run_t:dir rw_dir_perms;
allow ftpd_t ftpd_var_run_t:sock_file manage_file_perms;
files_pid_filetrans(ftpd_t,ftpd_var_run_t,file)
+# proftpd requires the client side to bind a socket so that
+# it can stat the socket to perform access control decisions,
+# since getsockopt with SO_PEERCRED is not available on all
+# proftpd-supported OSs
+allow ftpd_t ftpdctl_tmp_t:sock_file { getattr unlink };
+
# Create and modify /var/log/xferlog.
allow ftpd_t xferlog_t:dir search_dir_perms;
allow ftpd_t xferlog_t:file create_file_perms;
@@ -234,3 +247,28 @@ optional_policy(`
optional_policy(`
udev_read_db(ftpd_t)
')
+
+########################################
+#
+# ftpdctl local policy
+#
+
+# Allow ftpdctl to talk to ftpd over a socket connection
+allow ftpdctl_t ftpd_t:unix_stream_socket connectto;
+allow ftpdctl_t ftpd_var_run_t:dir search;
+allow ftpdctl_t ftpd_var_run_t:sock_file write;
+
+# ftpdctl creates a socket so that the daemon can perform
+# access control decisions (see comments in ftpd_t rules above)
+allow ftpdctl_t ftpdctl_tmp_t:sock_file { create setattr };
+files_tmp_filetrans(ftpdctl_t, ftpdctl_tmp_t, sock_file)
+
+# Allow ftpdctl to read config files
+files_read_etc_files(ftpdctl_t)
+
+libs_use_ld_so(ftpdctl_t)
+libs_use_shared_libs(ftpdctl_t)
+
+ifdef(`targeted_policy',`
+ term_use_generic_ptys(ftpdctl_t)
+')
diff --git a/refpolicy/policy/modules/system/unconfined.te b/refpolicy/policy/modules/system/unconfined.te
index 1f83eff4d..d8509df5f 100644
--- a/refpolicy/policy/modules/system/unconfined.te
+++ b/refpolicy/policy/modules/system/unconfined.te
@@ -105,6 +105,10 @@ ifdef(`targeted_policy',`
firstboot_domtrans(unconfined_t)
')
+ optional_policy(`
+ ftp_domtrans_ftpdctl(unconfined_t)
+ ')
+
optional_policy(`
inn_domtrans(unconfined_t)
')