diff --git a/debian/changelog b/debian/changelog
index d400cdb057ee6f2e49ef8672b9cea149726124c5..171577f584b2c46d2a0631040aac2079b11739be 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,8 @@
 postgresql-common (188) UNRELEASED; urgency=medium
 
+  * pg_ctlcluster, pg_createcluster, pg_upgradecluster: Use lchown instead
+    of chown to mitigate privilege escalation via symlinks. (Related to
+    CVE-2017-12172 in PostgreSQL; extends our earlier fix for CVE-2016-1255.)
   * dh_make_pgxs: Add options to set package name and version.
   * pg_lsclusters: Raise error when called on a specific cluster that does not
     exist. This was the behavior before the "accept dead postgresql.conf
diff --git a/pg_createcluster b/pg_createcluster
index e8aa59e7b0d903a296a6317e4e4931aa0ddcb6cf..d6002c4693ed915419ecf2630a3136e16f0f57da 100755
--- a/pg_createcluster
+++ b/pg_createcluster
@@ -18,7 +18,7 @@
 
 use PgCommon;
 use Getopt::Long;
-use POSIX qw/setlocale LC_ALL LC_CTYPE/;
+use POSIX qw/lchown setlocale LC_ALL LC_CTYPE/;
 
 $ENV{'PATH'} = '/bin:/usr/bin'; # untaint
 
@@ -369,8 +369,9 @@ set_cluster_pg_ctl_conf $version, $cluster, '';
 move_conffile "$datadir/postgresql.conf", $confdir, $owneruid, $ownergid, '644';
 move_conffile "$datadir/pg_hba.conf", $confdir, $owneruid, $ownergid, '640', 'hba_file';
 move_conffile "$datadir/pg_ident.conf", $confdir, $owneruid, $ownergid, '640', 'ident_file';
-chown $owneruid, $ownergid, $datadir, $confdir, "$confdir/start.conf" or die "chown: $!";
-chown $owneruid, $ownergid, $datadir, $confdir, "$confdir/pg_ctl.conf" or die "chown: $!";
+foreach my $f ($datadir, $confdir, "$confdir/start.conf", "$confdir/pg_ctl.conf") {
+    lchown $owneruid, $ownergid, $f or error "lchown $f: $!";
+}
 
 PgCommon::set_conf_value $version, $cluster, 'postgresql.conf', 'data_directory', $datadir;
 
@@ -410,7 +411,7 @@ if (! -d '/var/log/postgresql') {
     mkdir '/var/log/postgresql' or
 	error "could not create log directory; you might need to run this program with root privileges";
     chmod 01775, '/var/log/postgresql';
-    chown 0, $postgres_user[3], '/var/log/postgresql';
+    lchown 0, $postgres_user[3], '/var/log/postgresql';
 }
 $real_logfile = $custom_logfile || "/var/log/postgresql/postgresql-$version-$cluster.log";
 error "logfile $real_logfile is a directory, not a file" if (-d $real_logfile);
@@ -425,11 +426,11 @@ if ($owneruid < 500) {
 } else {
     $g = $ownergid;
 }
-chown $owneruid, $g, $real_logfile;
+lchown $owneruid, $g, $real_logfile;
 # if we are using a non-default log file, create a log symlink
 if ($custom_logfile) {
     symlink $real_logfile, "$confdir/log";
-    chown $owneruid, $ownergid, "$confdir/log";
+    lchown $owneruid, $ownergid, "$confdir/log";
 }
 
 # SSL configuration
@@ -539,7 +540,7 @@ open ENV, ">$confdir/environment" or error "could not create environment file $c
 print ENV $defaultenv;
 close ENV;
 chmod 0644, "$confdir/environment";
-chown $owneruid, $ownergid, "$confdir/environment";
+lchown $owneruid, $ownergid, "$confdir/environment";
 
 $cleanup_cruft = 0;
 
@@ -559,7 +560,7 @@ foreach my $guc (sort keys %defaultconf) {
         next if ($version < 9.3);
         if ($val =~ /^[\w.]+$/ and not -e "$confdir/$val") { # create directory relative to new config directory
             mkdir "$confdir/$val", 0755;
-            chown $owneruid, $ownergid, "$confdir/$val";
+            lchown $owneruid, $ownergid, "$confdir/$val";
         }
     }
     PgCommon::set_conf_value $version, $cluster, 'postgresql.conf', $guc, $val;
diff --git a/pg_ctlcluster b/pg_ctlcluster
index 6d19e2ba2ebdde8bde5f761221c0c76c69b74d74..def8b6bef30e50eedd461e53cf2c4d2b9efeb148 100755
--- a/pg_ctlcluster
+++ b/pg_ctlcluster
@@ -24,6 +24,7 @@ use Getopt::Long;
 use POSIX qw/setsid dup2 :sys_wait_h/;
 use PgCommon;
 use Fcntl qw(SEEK_SET O_RDWR O_CREAT O_EXCL);
+use POSIX qw(lchown);
 
 my ($version, $cluster, $pg_ctl, $force);
 my (@postgres_auxoptions, @pg_ctl_opts_from_cli);
@@ -452,7 +453,7 @@ if ($action ne 'stop' && $info{'logfile'} && ! -e $info{'logfile'}) {
     $( = $) = 0;
     if ($info{'owneruid'} < 500) {
         my $g = (getgrnam 'adm')[2];
-        chown $info{'owneruid'}, $g, $info{'logfile'} if (defined $g);
+        lchown $info{'owneruid'}, $g, $info{'logfile'} if (defined $g);
     }
 }
 
diff --git a/pg_upgradecluster b/pg_upgradecluster
index ed93114d9489bdc1b057db6fb34e817c1e40ef5a..d08364ea322aa3bebf8b1e4b94eec8f3d37fd0c4 100755
--- a/pg_upgradecluster
+++ b/pg_upgradecluster
@@ -20,7 +20,7 @@ use strict;
 use warnings;
 use PgCommon;
 use Getopt::Long;
-use POSIX;
+use POSIX qw(lchown);
 
 # untaint environment
 $ENV{'PATH'} = '/bin:/usr/bin';
@@ -128,7 +128,7 @@ sub adapt_conffiles {
 	}
 	close O;
 	close N;
-	chown $newinfo{'owneruid'}, $newinfo{'ownergid'}, "$hba.new";
+	lchown $newinfo{'owneruid'}, $newinfo{'ownergid'}, "$hba.new";
 	chmod 0640, "$hba.new";
 	rename "$hba.new", $hba or error "rename: $!";
     }
@@ -208,7 +208,7 @@ sub disable_connections {
         error "could not create $hba";
     }
     chmod 0400, $hba;
-    chown $_[3], 0, $hba;
+    lchown $_[3], 0, $hba;
     if ($_[0] >= '8.4') {
 	print F "local all $_[2] ident\n";
     } else {