From 52a414387e192a89f5fec19d9876159d03cf112b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 7 Aug 2017 10:19:01 -0400
Subject: [PATCH] Require update permission for the large object written by
 lo_put().

lo_put() surely should require UPDATE permission, the same as lowrite(),
but it failed to check for that, as reported by Chapman Flack.  Oversight
in commit c50b7c09d; backpatch to 9.4 where that was introduced.

Tom Lane and Michael Paquier

Security: CVE-2017-7548
---
 src/backend/libpq/be-fsstubs.c           | 12 ++++++++++++
 src/test/regress/expected/privileges.out | 10 ++++++++++
 src/test/regress/sql/privileges.sql      |  4 ++++
 3 files changed, 26 insertions(+)

diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index 764f602aaa2..50e416da79e 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -896,6 +896,18 @@ lo_put(PG_FUNCTION_ARGS)
 	CreateFSContext();
 
 	loDesc = inv_open(loOid, INV_WRITE, fscxt);
+
+	/* Permission check */
+	if (!lo_compat_privileges &&
+		pg_largeobject_aclcheck_snapshot(loDesc->id,
+										 GetUserId(),
+										 ACL_UPDATE,
+										 loDesc->snapshot) != ACLCHECK_OK)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for large object %u",
+						loDesc->id)));
+
 	inv_seek(loDesc, offset, SEEK_SET);
 	written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
 	Assert(written == VARSIZE_ANY_EXHDR(str));
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 52ba77f822d..b845fdd842a 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -1219,6 +1219,14 @@ SELECT lo_create(2002);
       2002
 (1 row)
 
+SELECT loread(lo_open(1001, x'20000'::int), 32);	-- allowed, for now
+ loread 
+--------
+ \x
+(1 row)
+
+SELECT lowrite(lo_open(1001, x'40000'::int), 'abcd');	-- fail, wrong mode
+ERROR:  large object descriptor 0 was not opened for writing
 SELECT loread(lo_open(1001, x'40000'::int), 32);
  loread 
 --------
@@ -1314,6 +1322,8 @@ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');	-- to be denied
 ERROR:  permission denied for large object 1002
 SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);	-- to be denied
 ERROR:  permission denied for large object 1002
+SELECT lo_put(1002, 1, 'abcd');				-- to be denied
+ERROR:  permission denied for large object 1002
 SELECT lo_unlink(1002);					-- to be denied
 ERROR:  must be owner of large object 1002
 SELECT lo_export(1001, '/dev/null');			-- to be denied
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 5622f7e4a89..b86c1452850 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -770,6 +770,9 @@ SET SESSION AUTHORIZATION regress_user2;
 SELECT lo_create(2001);
 SELECT lo_create(2002);
 
+SELECT loread(lo_open(1001, x'20000'::int), 32);	-- allowed, for now
+SELECT lowrite(lo_open(1001, x'40000'::int), 'abcd');	-- fail, wrong mode
+
 SELECT loread(lo_open(1001, x'40000'::int), 32);
 SELECT loread(lo_open(1002, x'40000'::int), 32);	-- to be denied
 SELECT loread(lo_open(1003, x'40000'::int), 32);
@@ -809,6 +812,7 @@ SET SESSION AUTHORIZATION regress_user4;
 SELECT loread(lo_open(1002, x'40000'::int), 32);	-- to be denied
 SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');	-- to be denied
 SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);	-- to be denied
+SELECT lo_put(1002, 1, 'abcd');				-- to be denied
 SELECT lo_unlink(1002);					-- to be denied
 SELECT lo_export(1001, '/dev/null');			-- to be denied
 
-- 
GitLab