Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sOHG5-00DQH7-AA for pgsql-hackers@arkaria.postgresql.org; Mon, 01 Jul 2024 13:39:53 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1sOHG3-001tSX-O0 for pgsql-hackers@arkaria.postgresql.org; Mon, 01 Jul 2024 13:39:52 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sOHG3-001tSP-DC for pgsql-hackers@lists.postgresql.org; Mon, 01 Jul 2024 13:39:51 +0000 Received: from mail-wr1-x433.google.com ([2a00:1450:4864:20::433]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1sOHFy-004Uej-DA for pgsql-hackers@lists.postgresql.org; Mon, 01 Jul 2024 13:39:51 +0000 Received: by mail-wr1-x433.google.com with SMTP id ffacd0b85a97d-36703b0f914so1873148f8f.0 for ; Mon, 01 Jul 2024 06:39:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1719841183; x=1720445983; darn=lists.postgresql.org; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id:from :to:cc:subject:date:message-id:reply-to; bh=tlDwXf/ixW/ESfQSoVzNkIYbFFFvWMjAL2RXNaDHxoA=; b=hIvPg18JYWhTVTe+CiyZxCqFeV3BCMnEwyPOGjIsUYKpfxcIje+QE17mFuVzQwCkXN g1E4z45yQ7TLxrc52iWaBNQZqumrS3Kg1LhqlZAdZkPI8LDWlmvWkWMPJrATXU6Q4CRj M+/AdDYJfBC1YIg+ieR/AYwQp90v31yYXFp+FnY61taensjp8epgQMevlauChI7zgs0A Fm5LQeR9UyE9f2HkBLfI9ToNoOCXexxDmfCQgrvgHt9ONuL+SDasAG3sUF7AM9aYBMft +s70nVSq6dipG5s7gglAuIpyWhdR/ZVSUEHR24rinQv6z+FUJAmrCgZ3uVS5wTWzEg+d BU8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719841183; x=1720445983; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tlDwXf/ixW/ESfQSoVzNkIYbFFFvWMjAL2RXNaDHxoA=; b=rEsGjV58Rn9lUiSIW5+E6G5GsCB2dTjxX0/NL2dJipIvrIlFB/YgJ/s4zADz95Ep+1 bQwOSG4/iHxjUWfQHkmWobHpo5mg3qE+muHdb8gnJfR1og3K6JCJKyCjbmuNBPA3SfNt NIqUcHIrDNqx/IeVsvGtWmGcqDbqNcWLtfKqPi2+nCexuNlyF+BpG0PFIh/cD0R3WXFT wZiub0o3/0O4bvZ8ePpz98MISC6R/mF1YbS3E01M6PfBJopOfD7/Moz4s499vQB14q92 8t1UHAAZ/Qgd99T/beHUyh0YZEyBrf3oVwF+9wVJ9f9qD9T2tspY06jcJ3QPc8fU6fe4 7SKQ== X-Gm-Message-State: AOJu0Yya2HwQME669DH50Qfhw65DRPNN7g1Ezl1cwIcbqQJGJ6lixWKF 0eQMQl7tsd700HBj/Ppvih5rfEcaXu7kTSCthrhtRj4smlQdJnovA+Lnew== X-Google-Smtp-Source: AGHT+IF/7WGreWoQO1Pd90VD4IBX75jn3eD9Zey16Ooedh+gRkMaielRSXWZgGRu9D8iDN1AKsYnMQ== X-Received: by 2002:a5d:4e49:0:b0:367:3282:a258 with SMTP id ffacd0b85a97d-36774f6cebdmr4011043f8f.18.1719841183004; Mon, 01 Jul 2024 06:39:43 -0700 (PDT) Received: from [10.0.0.20] ([102.39.187.244]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a0e12fcsm10052508f8f.48.2024.07.01.06.39.41 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 01 Jul 2024 06:39:42 -0700 (PDT) Message-ID: Date: Mon, 1 Jul 2024 15:39:39 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Wrong security context for deferred triggers? To: pgsql-hackers@lists.postgresql.org References: <77ee784cf248e842f74588418f55c2931e47bd78.camel@cybertec.at> <6f27f51bc98354d7254bbde1c171fa13aca03f28.camel@cybertec.at> <8e8ef9739bdcc99d4e58916578a2d0a7b5c4268b.camel@cybertec.at> Content-Language: en-US From: Bennie Swart In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, Allow me to provide some background on how we came across this. (This is my first time posting to a pgsql list so hopefully I've got everything set up correctly.) We have a db with a big legacy section that we're in the process of modernizing. To compensate for some of the shortcomings we have designed a layer of writable views to better represent the underlying data and make working with it more convenient. The following should illustrate what we're doing:     -- this is the schema containing the view layer.     create schema api;     -- and this user is granted access to the api, but not the rest of the legacy db.     create role apiuser;     grant usage on schema api to apiuser;     -- some dummy objects in the legacy db - poorly laid out and poorly named.     create schema legacy;     create table legacy.stock_base (         id serial primary key       , lbl text not null unique       , num int not null       -- etc     );     create table legacy.stock_extra (         id int not null unique references legacy.stock_base (id)       , man text       -- etc     );     -- create the stock view which better names and logically groups the data.     create view api.stock as       select sb.id            , sb.lbl as description            , sb.num as quantity            , se.man as manufacturer         from legacy.stock_base sb           left join legacy.stock_extra se using (id);     -- make it writable so it is easier to work with. use security definer to allow access to legacy sections.     create function api.stock_cud() returns trigger language plpgsql security definer as $$     begin         -- insert/update legacy.stock_base and legacy.stock_extra depending on trigger action, modified fields, etc.         assert tg_op = 'INSERT'; -- assume insert for example's sake.         insert into legacy.stock_base (lbl, num) values (new.description, new.quantity) returning id into new.id;         insert into legacy.stock_extra (id, man) values (new.id, new.manufacturer);         return new;     end;     $$;     create trigger stock_cud         instead of insert or update or delete on api.stock         for each row execute function api.stock_cud();     -- grant the apiuser permission to work with the view.     grant insert, update, delete on api.stock to apiuser;     -- insert as superuser - works as expected.     insert into api.stock (description, quantity, manufacturer) values ('item1', 10, 'manufacturer1');     -- insert as apiuser - works as expected.     set role apiuser;     insert into api.stock (description, quantity, manufacturer) values ('item2', 10, 'manufacturer2'); In some cases there are constraint triggers on the underlying tables to validate certain states. It is, however, possible for a state to be temporarily invalid between statements, so long as it is valid at tx commit. For this reason the triggers are deferred by default. Consider the following example:     reset role;     create function legacy.stock_check_state() returns trigger language plpgsql as $$     begin         -- do some queries to check the state of stock based on modified rows and error if invalid.         raise notice 'current_user %', current_user;         -- dummy validation code.         perform * from legacy.stock_base sb left join legacy.stock_extra se using (id) where sb.id = new.id;         return new;     end;     $$;     create constraint trigger stock_check_state         after insert or update or delete on legacy.stock_base         deferrable initially deferred         for each row execute function legacy.stock_check_state(); Then repeat the inserts:     -- insert as superuser - works as expected.     reset role;     insert into api.stock (description, quantity, manufacturer) values ('item3', 10, 'manufacturer3'); -- NOTICE:  current_user postgres     -- insert as apiuser - fails.     set role apiuser;     insert into api.stock (description, quantity, manufacturer) values ('item4', 10, 'manufacturer4'); -- NOTICE:  current_user apiuser     -- insert as apiuser, not deferred - works as expected.     begin;     set constraints all immediate;     insert into api.stock (description, quantity, manufacturer) values ('item4', 10, 'manufacturer4'); -- NOTICE:  current_user postgres     commit; The insert as apiuser fails when the constraint trigger is deferred, but works as expected when it is immediate. Hopefully this helps to better paint the picture. Our workaround currently is to just make `legacy.stock_check_state()` security definer as well. As I told Laurenz, we're not looking to advocate for any specific outcome here. We noticed this strange behaviour and thought it to be a bug that should be fixed - whatever "fixed" ends up meaning. Regards, Bennie Swart On 2024/06/26 17:53, Laurenz Albe wrote: > On Wed, 2024-06-26 at 07:38 -0700, David G. Johnston wrote: >> We have a few choices then: >> 1. Status quo + documentation backpatch >> 2. Change v18 narrowly + documentation backpatch >> 3. Backpatch narrowly (one infers the new behavior after reading the existing documentation) >> 4. Option 1, plus a new v18 owner-execution mode in lieu of the narrow change to fix the POLA violation >> >> I've been presenting option 4. >> >> Pondering further, I see now that having the owner-execution mode be the only way to avoid >> the POLA violation in deferred triggers isn't great since many triggers benefit from the >> implied security of being able to run in the invoker's execution context - especially if >> the trigger doesn't do anything that PUBLIC cannot already do. >> >> So, I'm on board with option 2 at this point. > Nice. > > I think we can safely rule out option 3. > Even if it is a bug, it is not one that has bothered anybody so far that a backpatch > is indicated. > > Yours, > Laurenz Albe > > > >