Thread: Status Report on SE-PostgreSQL

Status Report on SE-PostgreSQL

From
KaiGai Kohei
Date:
I also think it is a good idea to summarize current status of
SE-PostgreSQL, as Simon Riggs doing on his works.

The current revision of SE-PostgreSQL is 1425, available here:
[1/5] http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1425.patch[2/5]
http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1425.patch[3/5]
http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1425.patch[4/5]
http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1425.patch[5/5]
http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1425.patch

We had various kind of comments, feature requests and discussions during
previous/current commit fest, then whole of them are already included.

Currently, we have no open issues here.

As I summarized as follows, we had many discussions about its design
issues mainly, so my patch set has been updated to support them.
I believe we should move to detailed-reviews to merge the feature any
time now, since we should aware of v8.4 schedule.

I really would like folks to help/volunteer reviewing the patches, please!

* CommitFest:Nov- Simon Riggs requires a new GUC option to turn on/off row-level security  labeling to reduce storage
comsumption,then updated as follows:    http://archives.postgresql.org/message-id/492691A8.8030103@ak.jp.nec.com- Bruce
Momjiansuggested Row-level database ACLs to be compiled in default.- Discussions for default compile options:
PostgreSQLdoesn't prefer compile  time option to turn on/off features, except for platform specific one.  SE-PostgreSQL
isindeed platform specific feature. But, it makes other  issue that need mutually-exclusive enhanced security feature.
Weconcluded it as follows:  - All configurable features should be compiled within a single binary.  - Both of DAC and
MACshould be available simultaneously in row-level also.  - DAC is hardwired, and we allow users to choose an enhanced
securityfeature.- I updated the patch set to support both of Row-level database ACLs and  an enhanced security feature
(SELinux)simultaneously. ('08/12/17)    http://archives.postgresql.org/message-id/4948B6BD.1050402@ak.jp.nec.com-
RobertHaas concerned about Stephen Frost's column-level privileges has  a trouble, so it's unclear whether it can get
mergedinto v8.4.  - I also worked for his patch, then it got being ready for commit:
http://archives.postgresql.org/message-id/20090116045825.GY4656@tamriel.snowman.net-Alvaro Herrera suggested "static
inline"is not preferable.
 

* CommitFest:Sep- Peter Eisentraut commented about its design specifications:
http://archives.postgresql.org/message-id/48D03953.6000308@gmx.net-The hot issues were lack of fine-grained access
controlsin SQL-level,  and covert channels with row-level controls.- We finally made agreement to provide platform
independentrow-level controls,  and explicit documentation about covert channels in PK/FK constraints.  No one didn't
wantto apply polyinstantiation idea.- Simon Riggs requires wiki article to introduce SE-PostgreSQL.
http://wiki.postgresql.org/wiki/SEPostgreSQL-Patch set was updated to support Row-level database ACLs
http://archives.postgresql.org/message-id/48F46606.4080207@ak.jp.nec.com

* CommitFest:Jul- The patch set got documentation/testcases.- Peter Eisentraut commented about some of items:
http://archives.postgresql.org/message-id/200807071739.58428.peter_e@gmx.net-Then, these items are updated:
http://archives.postgresql.org/message-id/48773188.6000809@ak.jp.nec.com

* CommitFest:May- First patch set for v8.4 were proposed.- Tom Lane gave us various items to be improved.
http://archives.postgresql.org/message-id/3275.1210019965@sss.pgh.pa.us-I had a presentation at PGcon2008 ottawa.
http://sepgsql.googlecode.com/files/PGCON20080523.pdf

* Prior phase- First proposal of PGACE security framework, but I didn't know it was  just after the date of feature
freezein v8.3. So, it was suggested  to wait for v8.4 development cycle. ('07/04/17)- 8.2.x based SE-PostgreSQL
announced.('07/09/04)- SE-PostgreSQL package got merged into Fedora Project. ('07/11/08)- 8.3.x based SE-PostgreSQL
announced.('08/03/08)
 

Thanks,
-- 
KaiGai Kohei <kaigai@kaigai.gr.jp>


SE-PostgreSQL Updated Revision (r1460)

From
KaiGai Kohei
Date:
The patch set of SE-PostgreSQL and related stuff were updated (r1460).

[1/5] http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1460.patch
[2/5] http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1460.patch
[3/5] http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1460.patch
[4/5] http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1460.patch
[5/5] http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1460.patch

I reviewed the patch set by myself, and updated the following items.
However, I would like other hackers to review the code in honesty.

SE-PostgreSQL need any volunteers to review and comment the patch set.
Please give us your support!

List of updates:
- Rebased to the latest CVS HEAD, which includes the column-level privileges based on the SQL-standard. (The previous
r1425conflicts in some points.)
 

- Security policy (sepostgresql-devel.pp) was updated to fit both of Fedora 10 and rawhide. Test cases are also
modifiedto care the new security policy.
 

- Cleanup: NUM_SELINUX_CATALOG was replaced by lengthof() macro to avoid code duplications.

- Cleanup: sepgsqlCheckEmbeddedProcedure() is renamed to sepgsqlCheckProcedureInstall() due to its confusable naming.

- Add a new permission: db_procedure:{install} It enables to prevent malicious user-defined functions are installed as
apart of operators, conversions, types and so on. The default policy allows to install functions labeled as
"sepgsql_proc_t"only, as an implementation of these facilities. Meanwhile, functions defined by unprivileged users are
labeledas "user_sepgsql_proc_t" in default, and it is not allowed to install as an operator and so on. If DBA want to
installuser-defined functions for the purpose, he has to confirm its harmless and relabel it to "sepgsql_proc_t" at
first.In the previous revision, it checked "db_procedure:{execute}" here, but it is not enough actually, because
unprivilgeduser is allowed to execute self defined function.
 

- Code revising: The previous revision always denied required permissions, when the kernel does not define them within
itssecurity policy. But it can make unexpected behavior when we work SE-PostgreSQL on a system with legacy security
policywhich lacks a part of newly added permissions. The revised one simply allows actions when these are undefined.
 

- Fixbug: It required superfluous permissions when we try to update "security_label" system column but it does not
changeanything actually.   For example:     UPDATE t SET security_label = security_label; This query does not change
security_label,so we don't need to check "db_tuple:{relabelfrom}" permission here. It is obvious we cannot know what
tuplesare actually relabeled on sepgsqlExecScan(), so any permission checks for write-operations are moved to
sepgsqlHeapTuple(Insert|Update|Delete)hooks.
 

- Fixbug: when we update pg_largeobject system catalog by hand, it has a possibility to create/drop specific
largeobject,so we add a check on "db_blob:{create drop}" when pg_largeobject.loid is modified by UPDATE statement.
Forexample:     UPDATE pg_largeobject SET loid = loid::int + 10 WHERE loid = 1234; It is theoretically same as dropping
alargeobject with loid:1234 and creating a largeobject with loid:1244.
 

- Fixbug: Tome Lane pointed out a matter when a whole-row-reference on the relation with RTE_JOIN makes crash at the
"Column-LevelPrivileges" thread. This revision added a special care for the situation. It recursively walks on refered
JoinExprand picks up its sources to check permission to them.
 

- Code revising: T_SEvalItemRelation and T_SEvalItemAttribute nodes are integrated into T_SelinuxEvalItem node. In the
previousrevision, it simply chains all appeared tables and columns as a list of obsoleted node on Query->pgaceItem. But
ithas a trend the length of list grows long. T_SelinuxEvalItem contains required permissions on a table and an array of
permissionsfor columns. It enables to keep the length of the list minimum. Related stuffs in sepgsql/proxy.c is also
revised. - addEvalRelation() / addEvalAttribute() enhanced to handle T_SelinuxEvalItem.  - Functions to handle
inheritancetables and whole-row-reference are clearly    sorted out. expandEvalItemInheritance() handles inheritance
tables,and    expandEvalItemWholeRowRefs() handles whole-row-reference.
 

- Add a hook: pgaceExecuteTruncate() The previous revision checks permissions on truncated tables and tuples on
pgaceProcessUtility(),but this approach need to extract all the target including cascaded ones, so it made code
duplication.The new hook is deployed on ExecuteTruncate() and delivers a list of already opened relations with
AccessExclusiveLock.A new sepgsqlExecuteTruncate() checks needed permission on the hook.
 

- Cleanup: sepgsqlTupleName() always copied an identifier of tuple for audit record into its internal buffer, and
returnsits pointer to caller. But it is not necessary for most cases. It is revised to return a pointer within given
tupleto avoid useless strcpy(), if possible. So, its valid duration is limited to the duration of tuple, but there is
noreal matter. In addition, callers of sepgsqlTupleName() are cleaned up, because its length of line tend to grow a bit
long.

- Add a check: CREATE/ALTER FOREIGN DATA WRAPPER has a capability to load a discretional shared library module, so it
isnecessary to check db_database:{install_module} permission.
 

- Add source code comments: src/backend/security/sepgsql/avc.c

Thanks,
-- 
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>


Re: SE-PostgreSQL Updated Revision (r1460)

From
Robert Haas
Date:
On Fri, Jan 23, 2009 at 12:30 AM, KaiGai Kohei <kaigai@ak.jp.nec.com> wrote:
> The patch set of SE-PostgreSQL and related stuff were updated (r1460).
>
> [1/5] http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1460.patch
> [2/5] http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1460.patch
> [3/5] http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1460.patch
> [4/5] http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1460.patch
> [5/5] http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1460.patch

KaiGai -

I read through your docs patch tonight and did some copy editing.
Please see the attached patches, which I hope you will find helpful.
I have attached my suggested changes both as a patch against v1460
(sepostgresql-docs-rmh-vs-1460.gz) and also as patch against CVS HEAD
(sepostgresql-docs-rmh-vs-cvs-head), since I am not sure which is
easier for you.  I have a couple of general comments about the
documentation:

1. The docs as written are very Red Hat-centric, even to the point of
making reference to specific versions of Red Hat RPMs.  I think that
the community will find this unacceptable, as Red Hat is certainly not
the only SELinux-enabled distribution and I presume that we want to
support all of them to an equal degree.

2. Some of the information that is documented here properly belongs in
other sections of the documentation.  For example, the information
about GUCs clearly belongs somewhere in the section on server
configuration where all of the other GUCs are documented, not in a
separate sections about SE-PostgreSQL.  I suspect that all of the
information about row-level ACLs should be ripped out of security.sgml
and inserted into an appropriate portion of the "Database Roles and
Privileges" chapter, leaving this file to talk just about
SE-PostgreSQL.

3. It seems to me that the analogy between SQL DAC and Unix user/group
DAC is mentioned far too many times, and there are other cases where
information is repeated as well.  I think it might help to reorganize
the document a bit so that you introduce concepts in the right order.
For example, the section that defines MAC and DAC is a ways down in
the document, but you use those terms a whole bunch of times before
defining them.  I'm not 100% sure that we even want to be defining MAC
and DAC in our documentation, since those are general industry terms
that are not PostgreSQL-specific.  But if we are going to define them
then we should try to do so in the clearest way possible.

Overall, I would say there is a fair amount of work left to be done to
get this documentation up to par, but it's a good start and I hope
that the attached patches and suggestions will be helpful.

...Robert

Attachment

Re: SE-PostgreSQL Updated Revision (r1460)

From
KaiGai Kohei
Date:
Robert Haas wrote:
> On Fri, Jan 23, 2009 at 12:30 AM, KaiGai Kohei <kaigai@ak.jp.nec.com> wrote:
>> The patch set of SE-PostgreSQL and related stuff were updated (r1460).
>>
>> [1/5] http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1460.patch
>> [2/5] http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1460.patch
>> [3/5] http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1460.patch
>> [4/5] http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1460.patch
>> [5/5] http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1460.patch
> 
> KaiGai -
> 
> I read through your docs patch tonight and did some copy editing.
> Please see the attached patches, which I hope you will find helpful.
> I have attached my suggested changes both as a patch against v1460
> (sepostgresql-docs-rmh-vs-1460.gz) and also as patch against CVS HEAD
> (sepostgresql-docs-rmh-vs-cvs-head), since I am not sure which is
> easier for you.  I have a couple of general comments about the
> documentation:

Thanks your feedbacks!

I basically applied your fixes as is, expect for the following items:
- You replaced  ! Its providing access controls are not _bypassable_ for any clients ...    by  ! The access controls
implementedby SE-PostgrSQL may not be _biased_ even ...
 
  I wanted to express it is "unavoidable" here, so I changed as:  ! The access controls implemented by SE-PostgrSQL may
notbe _bypassed_ even ...
 

- I found a typo: "MAC" is described as "MAc".

And, I have a question about documentation manner.
- You represented "getpeercon()" function as a system call.  But, it is actually a wrapper function of getsockopt(2)
systemcall,  so the "getpeercon(3)" is not a system call strictly.  Is it necessary to represent these stuffs strictly
correct? (Thus, I wrote it as "API" in the r1460.)
 


> 1. The docs as written are very Red Hat-centric, even to the point of
> making reference to specific versions of Red Hat RPMs.  I think that
> the community will find this unacceptable, as Red Hat is certainly not
> the only SELinux-enabled distribution and I presume that we want to
> support all of them to an equal degree.

I guess you pointed out about: 1. The "Requirement" section in "Build and Installation" assumes    RedHat/Fedora's RPM
packageand its version number. 2. The security context and security policy used to explanation    assumes specific
securitypolicy. 3. "Labeled IPsec" seciton points to "RedHatEL4 Security Guide",    and it assumes the racoon's
configurationfiles are deployed    as RPM package doing.
 

About 1, is it necessary to rip the RPM specific version number
and replace it as:  selinux-policy which includes SE-PostgreSQL related stuffs.

About 2, SELinux community provides its default security policy,
and distributor's policy (including RedHat's one) is a derivative
of the default policy.
It is developed independent from distributor's cycle.  http://oss.tresys.com/projects/refpolicy
http://oss.tresys.com/repos/refpolicy/trunk/policy/modules/services/postgresql.te

You can find some of sepgsql_xxxx identifiers in postgresql.te.
All the appeared identifiers are upstreamed, so these are not Red Hat
specific.

About 3, If it rips the link to Red Hat and does not assume specific
path of "racoon.conf", the explnation become neutral.


> 2. Some of the information that is documented here properly belongs in
> other sections of the documentation.  For example, the information
> about GUCs clearly belongs somewhere in the section on server
> configuration where all of the other GUCs are documented, not in a
> separate sections about SE-PostgreSQL.

These explanations are moved to "Security and Authentication" section
in "Chapter 18. Server Configuration".

> I suspect that all of the
> information about row-level ACLs should be ripped out of security.sgml
> and inserted into an appropriate portion of the "Database Roles and
> Privileges" chapter, leaving this file to talk just about
> SE-PostgreSQL.

It is indeed an aspect of row-level ACLs.
However, it is also a feature on PGACE framework, same as SE-PostgreSQL.
An idea is to put a reference to indicate the row-level ACLs section
on "Database Roles and Privileges" chapter, like:
  PostgreSQL has an enhancement of database roles and privileges mechanism  which allows to database ACLs in row-level
granuality.See, <xref ...>  for more details.
 

What do you think?

> 3. It seems to me that the analogy between SQL DAC and Unix user/group
> DAC is mentioned far too many times, and there are other cases where
> information is repeated as well.  I think it might help to reorganize
> the document a bit so that you introduce concepts in the right order.

Indeed, it was redundant explanation. Thanks for youe edit.

> For example, the section that defines MAC and DAC is a ways down in
> the document, but you use those terms a whole bunch of times before
> defining them.  I'm not 100% sure that we even want to be defining MAC
> and DAC in our documentation, since those are general industry terms
> that are not PostgreSQL-specific.  But if we are going to define them
> then we should try to do so in the clearest way possible.

I can add the definitions of terms.
However, it is unclear whether PostgreSQL documentation should include
them, or not. For example, wikipedia has enough explanation for their
generam meanings.  http://en.wikipedia.org/wiki/Discretionary_Access_Control
http://en.wikipedia.org/wiki/Mandatory_Access_Control

It seems to me "Discretionary Access Control (DAC)" is an enough key
to search its meaning.

> Overall, I would say there is a fair amount of work left to be done to
> get this documentation up to par, but it's a good start and I hope
> that the attached patches and suggestions will be helpful.

I'm glad to see your help.
I'll pay my efforts for documentations also. But English is not my mother
language, so any suggestions are helpful for me.

Thanks,
-- 
KaiGai Kohei <kaigai@kaigai.gr.jp>


Re: Status Report on SE-PostgreSQL

From
KaiGai Kohei
Date:
The patch set of SE-PostgreSQL and related stuff were updated (r1467).

[1/5] http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1467.patch
[2/5] http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1467.patch
[3/5] http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1467.patch
[4/5] http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1467.patch
[5/5] http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1467.patch

List of updates:
- Documentation is updated based on Robert Haas's suggestions. See,
http://archives.postgresql.org/message-id/497C6808.2060109@kaigai.gr.jp
- Bugfix: SE-PostgreSQL related functions didn't raise an error when "pgace_feature" option is not "selinux".
- Add a launcher program to run testcases with various kind of security context.

Thanks,
-- 
KaiGai Kohei <kaigai@kaigai.gr.jp>


Re: SE-PostgreSQL Updated Revision (r1460)

From
KaiGai Kohei
Date:
Robert,

The attached patch is a draft to replace RedHat/Fedora RPM centric
expressions, to add a reference at "Database Roles and Privileges"
chapter and a bit cleanups for the latest revision (r1467).
In the previous revision, it noted users to check the version of
RPM package, but the revised one notes actually required features.
The version number is rewritten as a hint.

What is your opinion?

Thanks,

KaiGai Kohei wrote:
> Robert Haas wrote:
>> On Fri, Jan 23, 2009 at 12:30 AM, KaiGai Kohei <kaigai@ak.jp.nec.com>
>> wrote:
>>> The patch set of SE-PostgreSQL and related stuff were updated (r1460).
>>>
>>> [1/5]
>>> http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1460.patch
>>>
>>> [2/5]
>>> http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1460.patch
>>>
>>> [3/5]
>>> http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1460.patch
>>>
>>> [4/5]
>>> http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1460.patch
>>>
>>> [5/5]
>>> http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1460.patch
>>>
>>
>> KaiGai -
>>
>> I read through your docs patch tonight and did some copy editing.
>> Please see the attached patches, which I hope you will find helpful.
>> I have attached my suggested changes both as a patch against v1460
>> (sepostgresql-docs-rmh-vs-1460.gz) and also as patch against CVS HEAD
>> (sepostgresql-docs-rmh-vs-cvs-head), since I am not sure which is
>> easier for you.  I have a couple of general comments about the
>> documentation:
>
> Thanks your feedbacks!
>
> I basically applied your fixes as is, expect for the following items:
> - You replaced
>   ! Its providing access controls are not _bypassable_ for any clients ...
>     by
>   ! The access controls implemented by SE-PostgrSQL may not be _biased_
> even ...
>
>   I wanted to express it is "unavoidable" here, so I changed as:
>   ! The access controls implemented by SE-PostgrSQL may not be
> _bypassed_ even ...
>
> - I found a typo: "MAC" is described as "MAc".
>
> And, I have a question about documentation manner.
> - You represented "getpeercon()" function as a system call.
>   But, it is actually a wrapper function of getsockopt(2) system call,
>   so the "getpeercon(3)" is not a system call strictly.
>   Is it necessary to represent these stuffs strictly correct?
>   (Thus, I wrote it as "API" in the r1460.)
>
>
>> 1. The docs as written are very Red Hat-centric, even to the point of
>> making reference to specific versions of Red Hat RPMs.  I think that
>> the community will find this unacceptable, as Red Hat is certainly not
>> the only SELinux-enabled distribution and I presume that we want to
>> support all of them to an equal degree.
>
> I guess you pointed out about:
>  1. The "Requirement" section in "Build and Installation" assumes
>     RedHat/Fedora's RPM package and its version number.
>  2. The security context and security policy used to explanation
>     assumes specific security policy.
>  3. "Labeled IPsec" seciton points to "RedHatEL4 Security Guide",
>     and it assumes the racoon's configuration files are deployed
>     as RPM package doing.
>
> About 1, is it necessary to rip the RPM specific version number
> and replace it as:
>   selinux-policy which includes SE-PostgreSQL related stuffs.
>
> About 2, SELinux community provides its default security policy,
> and distributor's policy (including RedHat's one) is a derivative
> of the default policy.
> It is developed independent from distributor's cycle.
>   http://oss.tresys.com/projects/refpolicy
>
> http://oss.tresys.com/repos/refpolicy/trunk/policy/modules/services/postgresql.te
>
>
> You can find some of sepgsql_xxxx identifiers in postgresql.te.
> All the appeared identifiers are upstreamed, so these are not Red Hat
> specific.
>
> About 3, If it rips the link to Red Hat and does not assume specific
> path of "racoon.conf", the explnation become neutral.
>
>
>> 2. Some of the information that is documented here properly belongs in
>> other sections of the documentation.  For example, the information
>> about GUCs clearly belongs somewhere in the section on server
>> configuration where all of the other GUCs are documented, not in a
>> separate sections about SE-PostgreSQL.
>
> These explanations are moved to "Security and Authentication" section
> in "Chapter 18. Server Configuration".
>
>> I suspect that all of the
>> information about row-level ACLs should be ripped out of security.sgml
>> and inserted into an appropriate portion of the "Database Roles and
>> Privileges" chapter, leaving this file to talk just about
>> SE-PostgreSQL.
>
> It is indeed an aspect of row-level ACLs.
> However, it is also a feature on PGACE framework, same as SE-PostgreSQL.
> An idea is to put a reference to indicate the row-level ACLs section
> on "Database Roles and Privileges" chapter, like:
>
>   PostgreSQL has an enhancement of database roles and privileges mechanism
>   which allows to database ACLs in row-level granuality. See, <xref ...>
>   for more details.
>
> What do you think?
>
>> 3. It seems to me that the analogy between SQL DAC and Unix user/group
>> DAC is mentioned far too many times, and there are other cases where
>> information is repeated as well.  I think it might help to reorganize
>> the document a bit so that you introduce concepts in the right order.
>
> Indeed, it was redundant explanation. Thanks for youe edit.
>
>> For example, the section that defines MAC and DAC is a ways down in
>> the document, but you use those terms a whole bunch of times before
>> defining them.  I'm not 100% sure that we even want to be defining MAC
>> and DAC in our documentation, since those are general industry terms
>> that are not PostgreSQL-specific.  But if we are going to define them
>> then we should try to do so in the clearest way possible.
>
> I can add the definitions of terms.
> However, it is unclear whether PostgreSQL documentation should include
> them, or not. For example, wikipedia has enough explanation for their
> generam meanings.
>   http://en.wikipedia.org/wiki/Discretionary_Access_Control
>   http://en.wikipedia.org/wiki/Mandatory_Access_Control
>
> It seems to me "Discretionary Access Control (DAC)" is an enough key
> to search its meaning.
>
>> Overall, I would say there is a fair amount of work left to be done to
>> get this documentation up to par, but it's a good start and I hope
>> that the attached patches and suggestions will be helpful.
>
> I'm glad to see your help.
> I'll pay my efforts for documentations also. But English is not my mother
> language, so any suggestions are helpful for me.
>
> Thanks,


--
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
diff -Nrpc base/configure sepgsql/configure
*** base/configure    Fri Jan 23 10:23:37 2009
--- sepgsql/configure    Fri Jan 23 10:55:35 2009
*************** with_libxml
*** 710,715 ****
--- 710,716 ----
  with_libxslt
  with_system_tzdata
  with_zlib
+ enable_selinux
  GREP
  EGREP
  ELF_SYS
*************** Optional Features:
*** 1378,1383 ****
--- 1379,1385 ----
    --enable-thread-safety  make client libraries thread-safe
    --enable-thread-safety-force
                            force thread-safety despite thread test failure
+   --enable-selinux        enable to build with SELinux support
    --disable-float4-byval  disable float4 passed by value
    --disable-float8-byval  disable float8 passed by value
    --disable-largefile     omit support for large files
*************** fi
*** 5531,5536 ****
--- 5533,5644 ----


  #
+ # SELinux support
+ #
+
+ pgac_args="$pgac_args enable_selinux"
+
+ # Check whether --enable-selinux was given.
+ if test "${enable_selinux+set}" = set; then
+   enableval=$enable_selinux;
+   case $enableval in
+     yes)
+       :
+       ;;
+     no)
+       :
+       ;;
+     *)
+       { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5
+ echo "$as_me: error: no argument expected for --enable-selinux option" >&2;}
+    { (exit 1); exit 1; }; }
+       ;;
+   esac
+
+ else
+   enable_selinux=no
+
+ fi
+
+
+ if test "$enable_selinux" = yes; then
+     { echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5
+ echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6; }
+ if test "${ac_cv_lib_selinux_getpeercon+set}" = set; then
+   echo $ECHO_N "(cached) $ECHO_C" >&6
+ else
+   ac_check_lib_save_LIBS=$LIBS
+ LIBS="-lselinux  $LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+ /* confdefs.h.  */
+ _ACEOF
+ cat confdefs.h >>conftest.$ac_ext
+ cat >>conftest.$ac_ext <<_ACEOF
+ /* end confdefs.h.  */
+
+ /* Override any GCC internal prototype to avoid an error.
+    Use char because int might match the return type of a GCC
+    builtin and then its argument prototype would still apply.  */
+ #ifdef __cplusplus
+ extern "C"
+ #endif
+ char getpeercon ();
+ int
+ main ()
+ {
+ return getpeercon ();
+   ;
+   return 0;
+ }
+ _ACEOF
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { (ac_try="$ac_link"
+ case "(($ac_try" in
+   *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+   *) ac_try_echo=$ac_try;;
+ esac
+ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+   (eval "$ac_link") 2>conftest.er1
+   ac_status=$?
+   grep -v '^ *+' conftest.er1 >conftest.err
+   rm -f conftest.er1
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } && {
+      test -z "$ac_c_werror_flag" ||
+      test ! -s conftest.err
+        } && test -s conftest$ac_exeext &&
+        $as_test_x conftest$ac_exeext; then
+   ac_cv_lib_selinux_getpeercon=yes
+ else
+   echo "$as_me: failed program was:" >&5
+ sed 's/^/| /' conftest.$ac_ext >&5
+
+     ac_cv_lib_selinux_getpeercon=no
+ fi
+
+ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+       conftest$ac_exeext conftest.$ac_ext
+ LIBS=$ac_check_lib_save_LIBS
+ fi
+ { echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5
+ echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6; }
+ if test $ac_cv_lib_selinux_getpeercon = yes; then
+
+ cat >>confdefs.h <<_ACEOF
+ #define HAVE_SELINUX 1
+ _ACEOF
+
+
+ else
+   { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5
+ echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;}
+    { (exit 1); exit 1; }; }
+ fi
+
+ fi
+
+ #
  # Elf
  #

*************** with_libxml!$with_libxml$ac_delim
*** 27803,27813 ****
  with_libxslt!$with_libxslt$ac_delim
  with_system_tzdata!$with_system_tzdata$ac_delim
  with_zlib!$with_zlib$ac_delim
  GREP!$GREP$ac_delim
  EGREP!$EGREP$ac_delim
  ELF_SYS!$ELF_SYS$ac_delim
  LDFLAGS_SL!$LDFLAGS_SL$ac_delim
- LD!$LD$ac_delim
  _ACEOF

    if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
--- 27911,27921 ----
  with_libxslt!$with_libxslt$ac_delim
  with_system_tzdata!$with_system_tzdata$ac_delim
  with_zlib!$with_zlib$ac_delim
+ enable_selinux!$enable_selinux$ac_delim
  GREP!$GREP$ac_delim
  EGREP!$EGREP$ac_delim
  ELF_SYS!$ELF_SYS$ac_delim
  LDFLAGS_SL!$LDFLAGS_SL$ac_delim
  _ACEOF

    if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
*************** _ACEOF
*** 27849,27854 ****
--- 27957,27963 ----
  ac_delim='%!_!# '
  for ac_last_try in false false false false false :; do
    cat >conf$$subs.sed <<_ACEOF
+ LD!$LD$ac_delim
  with_gnu_ld!$with_gnu_ld$ac_delim
  ld_R_works!$ld_R_works$ac_delim
  RANLIB!$RANLIB$ac_delim
*************** vpath_build!$vpath_build$ac_delim
*** 27911,27917 ****
  LTLIBOBJS!$LTLIBOBJS$ac_delim
  _ACEOF

!   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then
      break
    elif $ac_last_try; then
      { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
--- 28020,28026 ----
  LTLIBOBJS!$LTLIBOBJS$ac_delim
  _ACEOF

!   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 61; then
      break
    elif $ac_last_try; then
      { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
diff -Nrpc base/configure.in sepgsql/configure.in
*** base/configure.in    Fri Jan 23 10:23:37 2009
--- sepgsql/configure.in    Fri Jan 23 10:55:35 2009
*************** PGAC_ARG_BOOL(with, zlib, yes,
*** 763,768 ****
--- 763,781 ----
  AC_SUBST(with_zlib)

  #
+ # SELinux support
+ #
+ PGAC_ARG_BOOL(enable, selinux, no,
+               [enable to build with SELinux support])
+ if test "$enable_selinux" = yes; then
+     AC_CHECK_LIB(selinux, getpeercon,
+                  AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1,
+                                     [SE-PostgreSQL feature is enabled])
+                  AC_SUBST(enable_selinux),
+                  AC_MSG_ERROR("--enable-selinux requires libselinux."))
+ fi
+
+ #
  # Elf
  #

diff -Nrpc base/src/Makefile.global.in sepgsql/src/Makefile.global.in
*** base/src/Makefile.global.in    Fri Jan 23 10:23:37 2009
--- sepgsql/src/Makefile.global.in    Fri Jan 23 10:55:35 2009
*************** enable_rpath    = @enable_rpath@
*** 164,169 ****
--- 164,170 ----
  enable_nls    = @enable_nls@
  enable_debug    = @enable_debug@
  enable_dtrace    = @enable_dtrace@
+ enable_selinux    = @enable_selinux@
  enable_coverage    = @enable_coverage@
  enable_thread_safety    = @enable_thread_safety@

diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile
*** base/src/backend/Makefile    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/Makefile    Sat Jan  3 15:58:18 2009
*************** include $(top_builddir)/src/Makefile.glo
*** 16,22 ****

  SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
      main nodes optimizer port postmaster regex rewrite \
!     storage tcop tsearch utils $(top_builddir)/src/timezone

  include $(srcdir)/common.mk

--- 16,22 ----

  SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
      main nodes optimizer port postmaster regex rewrite \
!     security storage tcop tsearch utils $(top_builddir)/src/timezone

  include $(srcdir)/common.mk

*************** LIBS := $(filter-out -lpgport, $(LIBS))
*** 34,39 ****
--- 34,44 ----
  # The backend doesn't need everything that's in LIBS, however
  LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))

+ # SELinux support needs to link libselinux
+ ifeq ($(enable_selinux), yes)
+ LIBS += -lselinux
+ endif
+
  ##########################################################################

  all: submake-libpgport postgres $(POSTGRES_IMP)
diff -Nrpc base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c
*** base/src/backend/access/common/heaptuple.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/access/common/heaptuple.c    Sat Jan  3 15:58:18 2009
***************
*** 61,66 ****
--- 61,67 ----
  #include "access/sysattr.h"
  #include "access/tuptoaster.h"
  #include "executor/tuptable.h"
+ #include "security/pgace.h"


  /* Does att's datatype allow packing into the 1-byte-header varlena format? */
*************** heap_attisnull(HeapTuple tup, int attnum
*** 287,292 ****
--- 288,295 ----
          case MinCommandIdAttributeNumber:
          case MaxTransactionIdAttributeNumber:
          case MaxCommandIdAttributeNumber:
+         case SecurityAclAttributeNumber:
+         case SecurityLabelAttributeNumber:
              /* these are never null */
              break;

*************** heap_getsysattr(HeapTuple tup, int attnu
*** 599,604 ****
--- 602,613 ----
          case TableOidAttributeNumber:
              result = ObjectIdGetDatum(tup->t_tableOid);
              break;
+         case SecurityAclAttributeNumber:
+             result = rowaclHeapGetSecurityAclSysattr(tup);
+             break;
+         case SecurityLabelAttributeNumber:
+             result = pgaceHeapGetSecurityLabelSysattr(tup);
+             break;
          default:
              elog(ERROR, "invalid attnum: %d", attnum);
              result = 0;            /* keep compiler quiet */
*************** heap_form_tuple(TupleDesc tupleDescripto
*** 723,728 ****
--- 732,743 ----
      if (tupleDescriptor->tdhasoid)
          len += sizeof(Oid);

+     if (tupleDescriptor->tdhasrowacl)
+         len += sizeof(Oid);
+
+     if (tupleDescriptor->tdhasseclabel)
+         len += sizeof(Oid);
+
      hoff = len = MAXALIGN(len); /* align user data safely */

      data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
*************** heap_form_tuple(TupleDesc tupleDescripto
*** 754,759 ****
--- 769,779 ----
      if (tupleDescriptor->tdhasoid)        /* else leave infomask = 0 */
          td->t_infomask = HEAP_HASOID;

+     if (tupleDescriptor->tdhasrowacl)
+         td->t_infomask2 |= HEAP_HAS_ROWACL;
+     if (tupleDescriptor->tdhasseclabel)
+         td->t_infomask2 |= HEAP_HAS_SECLABEL;
+
      heap_fill_tuple(tupleDescriptor,
                      values,
                      isnull,
*************** heap_modify_tuple(HeapTuple tuple,
*** 864,869 ****
--- 884,893 ----
      newTuple->t_tableOid = tuple->t_tableOid;
      if (tupleDesc->tdhasoid)
          HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
+     if (HeapTupleHasRowAcl(newTuple))
+         HeapTupleSetRowAcl(newTuple, HeapTupleGetRowAcl(tuple));
+     if (HeapTupleHasSecLabel(newTuple))
+         HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple));

      return newTuple;
  }
*************** heap_form_minimal_tuple(TupleDesc tupleD
*** 1475,1480 ****
--- 1499,1510 ----
      if (tupleDescriptor->tdhasoid)
          len += sizeof(Oid);

+     if (tupleDescriptor->tdhasrowacl)
+         len += sizeof(Oid);
+
+     if (tupleDescriptor->tdhasseclabel)
+         len += sizeof(Oid);
+
      hoff = len = MAXALIGN(len); /* align user data safely */

      data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
*************** heap_form_minimal_tuple(TupleDesc tupleD
*** 1496,1501 ****
--- 1526,1536 ----
      if (tupleDescriptor->tdhasoid)        /* else leave infomask = 0 */
          tuple->t_infomask = HEAP_HASOID;

+     if (tupleDescriptor->tdhasrowacl)
+         tuple->t_infomask2 |= HEAP_HAS_ROWACL;
+     if (tupleDescriptor->tdhasseclabel)
+         tuple->t_infomask2 |= HEAP_HAS_SECLABEL;
+
      heap_fill_tuple(tupleDescriptor,
                      values,
                      isnull,
diff -Nrpc base/src/backend/access/common/reloptions.c sepgsql/src/backend/access/common/reloptions.c
*** base/src/backend/access/common/reloptions.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/access/common/reloptions.c    Tue Jan 13 10:00:14 2009
***************
*** 22,27 ****
--- 22,28 ----
  #include "catalog/pg_type.h"
  #include "commands/defrem.h"
  #include "nodes/makefuncs.h"
+ #include "security/pgace.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
***************
*** 48,53 ****
--- 49,62 ----

  static relopt_bool boolRelOpts[] =
  {
+     {
+         {
+             "row_level_acl",
+             "Row-level ACLs validator",
+             RELOPT_KIND_HEAP
+         },
+         false,
+     },
      /* list terminator */
      { { NULL } }
  };
*************** static relopt_real realRelOpts[] =
*** 98,103 ****
--- 107,121 ----

  static relopt_string stringRelOpts[] =
  {
+     {
+         {
+             "default_row_acl",
+             "Default Row-level ACLs",
+             RELOPT_KIND_HEAP
+         },
+         0, true, rawaclValidateDefaultRowAclRelopt,
+         "",
+     },
      /* list terminator */
      { { NULL } }
  };
*************** default_reloptions(Datum reloptions, boo
*** 877,883 ****
      StdRdOptions   *rdopts;
      int                numoptions;
      relopt_parse_elt tab[] = {
!         {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}
      };

      options = parseRelOptions(reloptions, validate, kind, &numoptions);
--- 895,903 ----
      StdRdOptions   *rdopts;
      int                numoptions;
      relopt_parse_elt tab[] = {
!         {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
!         {"row_level_acl", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, row_level_acl)},
!         {"default_row_acl", RELOPT_TYPE_STRING, offsetof(StdRdOptions, default_row_acl)},
      };

      options = parseRelOptions(reloptions, validate, kind, &numoptions);
diff -Nrpc base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c
*** base/src/backend/access/common/tupdesc.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/access/common/tupdesc.c    Fri Jan 23 10:55:35 2009
*************** CreateTemplateTupleDesc(int natts, bool
*** 88,93 ****
--- 88,95 ----
      desc->tdtypeid = RECORDOID;
      desc->tdtypmod = -1;
      desc->tdhasoid = hasoid;
+     desc->tdhasrowacl = false;        /* set a proper bool, if necessary */
+     desc->tdhasseclabel = false;    /* set a proper bool, if necessary */
      desc->tdrefcount = -1;        /* assume not reference-counted */

      return desc;
*************** CreateTupleDesc(int natts, bool hasoid,
*** 121,126 ****
--- 123,130 ----
      desc->tdtypeid = RECORDOID;
      desc->tdtypmod = -1;
      desc->tdhasoid = hasoid;
+     desc->tdhasrowacl = false;        /* set a proper bool, if necessary */
+     desc->tdhasseclabel = false;    /* set a proper bool, if necessary */
      desc->tdrefcount = -1;        /* assume not reference-counted */

      return desc;
*************** CreateTupleDescCopy(TupleDesc tupdesc)
*** 150,155 ****
--- 154,161 ----

      desc->tdtypeid = tupdesc->tdtypeid;
      desc->tdtypmod = tupdesc->tdtypmod;
+     desc->tdhasrowacl = tupdesc->tdhasrowacl;
+     desc->tdhasseclabel = tupdesc->tdhasseclabel;

      return desc;
  }
*************** CreateTupleDescCopyConstr(TupleDesc tupd
*** 208,213 ****
--- 214,221 ----

      desc->tdtypeid = tupdesc->tdtypeid;
      desc->tdtypmod = tupdesc->tdtypmod;
+     desc->tdhasrowacl = tupdesc->tdhasrowacl;
+     desc->tdhasseclabel = tupdesc->tdhasseclabel;

      return desc;
  }
*************** equalTupleDescs(TupleDesc tupdesc1, Tupl
*** 314,319 ****
--- 322,331 ----
          return false;
      if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
          return false;
+     if (tupdesc1->tdhasrowacl != tupdesc2->tdhasrowacl)
+         return false;
+     if (tupdesc1->tdhasseclabel != tupdesc2->tdhasseclabel)
+         return false;

      for (i = 0; i < tupdesc1->natts; i++)
      {
diff -Nrpc base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c
*** base/src/backend/access/heap/heapam.c    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/access/heap/heapam.c    Thu Jan 22 14:41:23 2009
***************
*** 54,59 ****
--- 54,60 ----
  #include "catalog/namespace.h"
  #include "miscadmin.h"
  #include "pgstat.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/freespace.h"
  #include "storage/lmgr.h"
*************** heap_insert(Relation relation, HeapTuple
*** 2056,2061 ****
--- 2057,2068 ----
  Oid
  simple_heap_insert(Relation relation, HeapTuple tup)
  {
+     if (!pgaceHeapTupleInsert(relation, tup, true, false))
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("could not insert tuple on \"%s\" due to pgace security",
+                         RelationGetRelationName(relation))));
+
      return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
  }

*************** simple_heap_delete(Relation relation, It
*** 2349,2354 ****
--- 2356,2367 ----
      ItemPointerData update_ctid;
      TransactionId update_xmax;

+     if (!pgaceHeapTupleDelete(relation, tid, true, false))
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("could not delete tuple on \"%s\" due to pgace security",
+                         RelationGetRelationName(relation))));
+
      result = heap_delete(relation, tid,
                           &update_ctid, &update_xmax,
                           GetCurrentCommandId(true), InvalidSnapshot,
*************** simple_heap_update(Relation relation, It
*** 3018,3023 ****
--- 3031,3042 ----
      ItemPointerData update_ctid;
      TransactionId update_xmax;

+     if (!pgaceHeapTupleUpdate(relation, otid, tup, true, false))
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("could not update tuple on \"%s\" due to pgace security",
+                         RelationGetRelationName(relation))));
+
      result = heap_update(relation, otid, tup,
                           &update_ctid, &update_xmax,
                           GetCurrentCommandId(true), InvalidSnapshot,
diff -Nrpc base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c
*** base/src/backend/access/heap/tuptoaster.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/access/heap/tuptoaster.c    Wed Jan 14 09:52:01 2009
***************
*** 35,40 ****
--- 35,41 ----
  #include "access/tuptoaster.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
+ #include "security/pgace.h"
  #include "utils/fmgroids.h"
  #include "utils/pg_lzcompress.h"
  #include "utils/rel.h"
*************** toast_insert_or_update(Relation rel, Hea
*** 591,596 ****
--- 592,601 ----
          hoff += BITMAPLEN(numAttrs);
      if (newtup->t_data->t_infomask & HEAP_HASOID)
          hoff += sizeof(Oid);
+     if (HeapTupleHasRowAcl(newtup))
+         hoff += sizeof(Oid);
+     if (HeapTupleHasSecLabel(newtup))
+         hoff += sizeof(Oid);
      hoff = MAXALIGN(hoff);
      Assert(hoff == newtup->t_data->t_hoff);
      /* now convert to a limit on the tuple data size */
*************** toast_insert_or_update(Relation rel, Hea
*** 864,869 ****
--- 869,878 ----
              new_len += BITMAPLEN(numAttrs);
          if (olddata->t_infomask & HEAP_HASOID)
              new_len += sizeof(Oid);
+         if (HeapTupleHeaderHasRowAcl(olddata))
+             new_len += sizeof(Oid);
+         if (HeapTupleHeaderHasSecLabel(olddata))
+             new_len += sizeof(Oid);
          new_len = MAXALIGN(new_len);
          Assert(new_len == olddata->t_hoff);
          new_data_len = heap_compute_data_size(tupleDesc,
*************** toast_flatten_tuple_attribute(Datum valu
*** 1015,1020 ****
--- 1024,1033 ----
          new_len += BITMAPLEN(numAttrs);
      if (olddata->t_infomask & HEAP_HASOID)
          new_len += sizeof(Oid);
+     if (HeapTupleHeaderHasRowAcl(olddata))
+         new_len += sizeof(Oid);
+     if (HeapTupleHeaderHasSecLabel(olddata))
+         new_len += sizeof(Oid);
      new_len = MAXALIGN(new_len);
      Assert(new_len == olddata->t_hoff);
      new_data_len = heap_compute_data_size(tupleDesc,
*************** toast_save_datum(Relation rel, Datum val
*** 1213,1218 ****
--- 1226,1237 ----
          memcpy(VARDATA(&chunk_data), data_p, chunk_size);
          toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);

+         if (!pgaceHeapTupleInsert(toastrel, toasttup, true, false))
+             ereport(ERROR,
+                     (errcode(ERRCODE_PGACE_ERROR),
+                      errmsg("could not insert tuple \"%s\" due to pgace security",
+                             RelationGetRelationName(toastrel))));
+
          heap_insert(toastrel, toasttup, mycid, options, NULL);

          /*
diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y
*** base/src/backend/bootstrap/bootparse.y    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/bootstrap/bootparse.y    Sat Jan  3 15:58:18 2009
***************
*** 42,47 ****
--- 42,48 ----
  #include "nodes/pg_list.h"
  #include "nodes/primnodes.h"
  #include "rewrite/prs2lock.h"
+ #include "security/pgace.h"
  #include "storage/block.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
*************** Boot_CreateStmt:
*** 206,211 ****
--- 207,221 ----
                                                     RELKIND_RELATION,
                                                     $3,
                                                     true);
+                         /*
+                          * fixup boot_reldesc->rd_att->tdhassec(acl|label) via PGACE
+                          */
+                         boot_reldesc->rd_rel->relkind = RELKIND_RELATION;
+                         boot_reldesc->rd_att->tdhasrowacl
+                             = pgaceTupleDescHasRowAcl(boot_reldesc, NIL);
+                         boot_reldesc->rd_att->tdhasseclabel
+                             = pgaceTupleDescHasSecLabel(boot_reldesc, NIL);
+
                          elog(DEBUG4, "bootstrap relation created");
                      }
                      else
*************** Boot_CreateStmt:
*** 225,231 ****
                                                        0,
                                                        ONCOMMIT_NOOP,
                                                        (Datum) 0,
!                                                       true);
                          elog(DEBUG4, "relation created with oid %u", id);
                      }
                      do_end();
--- 235,242 ----
                                                        0,
                                                        ONCOMMIT_NOOP,
                                                        (Datum) 0,
!                                                       true,
!                                                       NIL);
                          elog(DEBUG4, "relation created with oid %u", id);
                      }
                      do_end();
diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c
*** base/src/backend/bootstrap/bootstrap.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/bootstrap/bootstrap.c    Fri Jan 23 10:55:35 2009
***************
*** 32,37 ****
--- 32,38 ----
  #include "nodes/makefuncs.h"
  #include "postmaster/bgwriter.h"
  #include "postmaster/walwriter.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/ipc.h"
  #include "storage/proc.h"
*************** BootstrapModeMain(void)
*** 500,505 ****
--- 501,508 ----
       */
      boot_yyparse();

+     pgacePostBootstrapingMode();
+
      /* Perform a checkpoint to ensure everything's down to disk */
      SetProcessingMode(NormalProcessing);
      CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
*************** InsertOneTuple(Oid objectid)
*** 797,802 ****
--- 800,810 ----
      tupDesc = CreateTupleDesc(numattr,
                                RelationGetForm(boot_reldesc)->relhasoids,
                                attrtypes);
+     tupDesc->tdhasrowacl
+         = rowaclTupleDescHasRowAcl(boot_reldesc, NIL);
+     tupDesc->tdhasseclabel
+         = pgaceTupleDescHasSecLabel(boot_reldesc, NIL);
+
      tuple = heap_form_tuple(tupDesc, values, Nulls);
      if (objectid != (Oid) 0)
          HeapTupleSetOid(tuple, objectid);
diff -Nrpc base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile
*** base/src/backend/catalog/Makefile    Wed Dec 24 11:45:25 2008
--- sepgsql/src/backend/catalog/Makefile    Wed Dec 24 12:18:39 2008
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 34,39 ****
--- 34,40 ----
      pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
      pg_database.h pg_tablespace.h pg_pltemplate.h \
      pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+     pg_security.h \
      pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
      pg_ts_parser.h pg_ts_template.h \
      pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c
*** base/src/backend/catalog/aclchk.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/catalog/aclchk.c    Fri Jan 23 10:55:35 2009
*************** dumpacl(Acl *acl)
*** 105,111 ****
   *
   * NB: the original old_acl is pfree'd.
   */
! static Acl *
  merge_acl_with_grant(Acl *old_acl, bool is_grant,
                       bool grant_option, DropBehavior behavior,
                       List *grantees, AclMode privileges,
--- 105,111 ----
   *
   * NB: the original old_acl is pfree'd.
   */
! Acl *
  merge_acl_with_grant(Acl *old_acl, bool is_grant,
                       bool grant_option, DropBehavior behavior,
                       List *grantees, AclMode privileges,
diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c
*** base/src/backend/catalog/catalog.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/catalog/catalog.c    Sat Jan  3 15:58:18 2009
***************
*** 31,36 ****
--- 31,37 ----
  #include "catalog/pg_database.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_pltemplate.h"
+ #include "catalog/pg_security.h"
  #include "catalog/pg_shdepend.h"
  #include "catalog/pg_shdescription.h"
  #include "catalog/pg_tablespace.h"
*************** IsSharedRelation(Oid relationId)
*** 304,309 ****
--- 305,311 ----
          relationId == AuthMemRelationId ||
          relationId == DatabaseRelationId ||
          relationId == PLTemplateRelationId ||
+         relationId == SecurityRelationId ||
          relationId == SharedDescriptionRelationId ||
          relationId == SharedDependRelationId ||
          relationId == TableSpaceRelationId)
*************** IsSharedRelation(Oid relationId)
*** 316,321 ****
--- 318,325 ----
          relationId == DatabaseNameIndexId ||
          relationId == DatabaseOidIndexId ||
          relationId == PLTemplateNameIndexId ||
+         relationId == SecurityOidIndexId ||
+         relationId == SecuritySeclabelIndexId ||
          relationId == SharedDescriptionObjIndexId ||
          relationId == SharedDependDependerIndexId ||
          relationId == SharedDependReferenceIndexId ||
diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c
*** base/src/backend/catalog/heap.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/catalog/heap.c    Fri Jan 23 10:55:35 2009
***************
*** 56,61 ****
--- 56,62 ----
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
  #include "parser/parse_relation.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/freespace.h"
  #include "storage/smgr.h"
*************** static void AddNewRelationTuple(Relation
*** 74,80 ****
                      Oid new_rel_oid, Oid new_type_oid,
                      Oid relowner,
                      char relkind,
!                     Datum reloptions);
  static Oid AddNewRelationType(const char *typeName,
                     Oid typeNamespace,
                     Oid new_rel_oid,
--- 75,82 ----
                      Oid new_rel_oid, Oid new_type_oid,
                      Oid relowner,
                      char relkind,
!                     Datum reloptions,
!                     List *pgaceAttrList);
  static Oid AddNewRelationType(const char *typeName,
                     Oid typeNamespace,
                     Oid new_rel_oid,
*************** static List *insert_ordered_unique_oid(L
*** 112,148 ****
  static FormData_pg_attribute a1 = {
      0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
      SelfItemPointerAttributeNumber, 0, -1, -1,
!     false, 'p', 's', true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a2 = {
      0, {"oid"}, OIDOID, 0, sizeof(Oid),
      ObjectIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a3 = {
      0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
      MinTransactionIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a4 = {
      0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
      MinCommandIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a5 = {
      0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
      MaxTransactionIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a6 = {
      0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
      MaxCommandIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

  /*
--- 114,150 ----
  static FormData_pg_attribute a1 = {
      0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
      SelfItemPointerAttributeNumber, 0, -1, -1,
!     false, 'p', 's', 0, true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a2 = {
      0, {"oid"}, OIDOID, 0, sizeof(Oid),
      ObjectIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a3 = {
      0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
      MinTransactionIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a4 = {
      0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
      MinCommandIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a5 = {
      0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
      MaxTransactionIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

  static FormData_pg_attribute a6 = {
      0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
      MaxCommandIdAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

  /*
*************** static FormData_pg_attribute a6 = {
*** 154,163 ****
  static FormData_pg_attribute a7 = {
      0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
      TableOidAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', true, false, false, true, 0, { 0 }
  };

! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};

  /*
   * This function returns a Form_pg_attribute pointer for a system attribute.
--- 156,177 ----
  static FormData_pg_attribute a7 = {
      0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
      TableOidAttributeNumber, 0, -1, -1,
!     true, 'p', 'i', 0, true, false, false, true, 0, { 0 }
  };

! static FormData_pg_attribute a8 = {
!     0, {SecurityAclAttributeName}, ACLITEMARRAYOID, 0, -1,
!     SecurityAclAttributeNumber, 1, -1, -1,
!     false, 'x', 'i', 0, true, false, false, true, 0, { 0 }
! };
!
! static FormData_pg_attribute a9 = {
!     0, {SecurityLabelAttributeName}, TEXTOID, 0, -1,
!     SecurityLabelAttributeNumber, 0, -1, -1,
!     false, 'x', 'i', 0, true, false, false, true, 0, { 0 }
! };
!
! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9};

  /*
   * This function returns a Form_pg_attribute pointer for a system attribute.
*************** SystemAttributeByName(const char *attnam
*** 197,202 ****
--- 211,229 ----
      return NULL;
  }

+ /*
+  * This function returns true, if the given attribute number is writable
+  * system column. If not, returns false.
+  */
+ bool
+ SystemAttributeIsWritable(AttrNumber attnum)
+ {
+     if (attnum == SecurityAclAttributeNumber ||
+         attnum == SecurityLabelAttributeNumber)
+         return true;
+
+     return false;
+ }

  /* ----------------------------------------------------------------
   *                XXX END OF UGLY HARD CODED BADNESS XXX
*************** CheckAttributeType(const char *attname,
*** 486,492 ****
  void
  InsertPgAttributeTuple(Relation pg_attribute_rel,
                         Form_pg_attribute new_attribute,
!                        CatalogIndexState indstate)
  {
      Datum        values[Natts_pg_attribute];
      bool        nulls[Natts_pg_attribute];
--- 513,520 ----
  void
  InsertPgAttributeTuple(Relation pg_attribute_rel,
                         Form_pg_attribute new_attribute,
!                        CatalogIndexState indstate,
!                        List *pgaceAttrList)
  {
      Datum        values[Natts_pg_attribute];
      bool        nulls[Natts_pg_attribute];
*************** InsertPgAttributeTuple(Relation pg_attri
*** 508,513 ****
--- 536,542 ----
      values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval);
      values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage);
      values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign);
+     values[Anum_pg_attribute_attkind - 1] = CharGetDatum(new_attribute->attkind);
      values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull);
      values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef);
      values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
*************** InsertPgAttributeTuple(Relation pg_attri
*** 519,524 ****
--- 548,555 ----

      tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);

+     pgaceCreateAttributeCommon(pg_attribute_rel, tup, pgaceAttrList);
+
      /* finally insert the new tuple, update the indexes, and clean up */
      simple_heap_insert(pg_attribute_rel, tup);

*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 541,547 ****
                        TupleDesc tupdesc,
                        char relkind,
                        bool oidislocal,
!                       int oidinhcount)
  {
      Form_pg_attribute attr;
      int            i;
--- 572,579 ----
                        TupleDesc tupdesc,
                        char relkind,
                        bool oidislocal,
!                       int oidinhcount,
!                       List *pgaceAttrList)
  {
      Form_pg_attribute attr;
      int            i;
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 565,577 ****
      for (i = 0; i < natts; i++)
      {
          attr = tupdesc->attrs[i];
!         /* Fill in the correct relation OID */
          attr->attrelid = new_rel_oid;
          /* Make sure these are OK, too */
          attr->attstattarget = -1;
          attr->attcacheoff = -1;

!         InsertPgAttributeTuple(rel, attr, indstate);

          /* Add dependency info */
          myself.classId = RelationRelationId;
--- 597,610 ----
      for (i = 0; i < natts; i++)
      {
          attr = tupdesc->attrs[i];
!         /* Fill in the correct relation OID and relkind */
          attr->attrelid = new_rel_oid;
+         attr->attkind = relkind;
          /* Make sure these are OK, too */
          attr->attstattarget = -1;
          attr->attcacheoff = -1;

!         InsertPgAttributeTuple(rel, attr, indstate, pgaceAttrList);

          /* Add dependency info */
          myself.classId = RelationRelationId;
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 603,608 ****
--- 636,642 ----

              /* Fill in the correct relation OID in the copied tuple */
              attStruct.attrelid = new_rel_oid;
+             attStruct.attkind = relkind;

              /* Fill in correct inheritance info for the OID column */
              if (attStruct.attnum == ObjectIdAttributeNumber)
*************** AddNewAttributeTuples(Oid new_rel_oid,
*** 611,617 ****
                  attStruct.attinhcount = oidinhcount;
              }

!             InsertPgAttributeTuple(rel, &attStruct, indstate);
          }
      }

--- 645,651 ----
                  attStruct.attinhcount = oidinhcount;
              }

!             InsertPgAttributeTuple(rel, &attStruct, indstate, pgaceAttrList);
          }
      }

*************** void
*** 639,645 ****
  InsertPgClassTuple(Relation pg_class_desc,
                     Relation new_rel_desc,
                     Oid new_rel_oid,
!                    Datum reloptions)
  {
      Form_pg_class rd_rel = new_rel_desc->rd_rel;
      Datum        values[Natts_pg_class];
--- 673,680 ----
  InsertPgClassTuple(Relation pg_class_desc,
                     Relation new_rel_desc,
                     Oid new_rel_oid,
!                    Datum reloptions,
!                    List *pgaceAttrList)
  {
      Form_pg_class rd_rel = new_rel_desc->rd_rel;
      Datum        values[Natts_pg_class];
*************** InsertPgClassTuple(Relation pg_class_des
*** 686,697 ****
--- 721,736 ----
       * be embarrassing to do this sort of thing in polite company.
       */
      HeapTupleSetOid(tup, new_rel_oid);
+     pgaceCreateRelationCommon(pg_class_desc, tup, pgaceAttrList);

      /* finally insert the new tuple, update the indexes, and clean up */
      simple_heap_insert(pg_class_desc, tup);

      CatalogUpdateIndexes(pg_class_desc, tup);

+     /* temporary use for this tuple */
+     InsertSysCache(RelationGetRelid(pg_class_desc), tup);
+
      heap_freetuple(tup);
  }

*************** AddNewRelationTuple(Relation pg_class_de
*** 709,715 ****
                      Oid new_type_oid,
                      Oid relowner,
                      char relkind,
!                     Datum reloptions)
  {
      Form_pg_class new_rel_reltup;

--- 748,755 ----
                      Oid new_type_oid,
                      Oid relowner,
                      char relkind,
!                     Datum reloptions,
!                     List *pgaceAttrList)
  {
      Form_pg_class new_rel_reltup;

*************** AddNewRelationTuple(Relation pg_class_de
*** 769,775 ****
      new_rel_desc->rd_att->tdtypeid = new_type_oid;

      /* Now build and insert the tuple */
!     InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
  }


--- 809,816 ----
      new_rel_desc->rd_att->tdtypeid = new_type_oid;

      /* Now build and insert the tuple */
!     InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
!                        reloptions, pgaceAttrList);
  }


*************** heap_create_with_catalog(const char *rel
*** 838,844 ****
                           int oidinhcount,
                           OnCommitAction oncommit,
                           Datum reloptions,
!                          bool allow_system_table_mods)
  {
      Relation    pg_class_desc;
      Relation    new_rel_desc;
--- 879,886 ----
                           int oidinhcount,
                           OnCommitAction oncommit,
                           Datum reloptions,
!                          bool allow_system_table_mods,
!                          List *pgaceAttrList)
  {
      Relation    pg_class_desc;
      Relation    new_rel_desc;
*************** heap_create_with_catalog(const char *rel
*** 1012,1024 ****
                          new_type_oid,
                          ownerid,
                          relkind,
!                         reloptions);

      /*
       * now add tuples to pg_attribute for the attributes in our new relation.
       */
      AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind,
!                           oidislocal, oidinhcount);

      /*
       * Make a dependency link to force the relation to be deleted if its
--- 1054,1075 ----
                          new_type_oid,
                          ownerid,
                          relkind,
!                         reloptions,
!                         pgaceAttrList);

      /*
       * now add tuples to pg_attribute for the attributes in our new relation.
       */
      AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind,
!                           oidislocal, oidinhcount, pgaceAttrList);
!
!     /*
!      * Fixup rel->rd_att->tdhassecacl and rel->rd_att->tdhasseclabel
!      */
!     new_rel_desc->rd_att->tdhasrowacl
!         = pgaceTupleDescHasRowAcl(new_rel_desc, NIL);
!     new_rel_desc->rd_att->tdhasseclabel
!         = pgaceTupleDescHasSecLabel(new_rel_desc, NIL);

      /*
       * Make a dependency link to force the relation to be deleted if its
diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c
*** base/src/backend/catalog/index.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/catalog/index.c    Fri Jan 23 10:55:35 2009
***************
*** 48,53 ****
--- 48,54 ----
  #include "nodes/nodeFuncs.h"
  #include "optimizer/clauses.h"
  #include "optimizer/var.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/lmgr.h"
  #include "storage/procarray.h"
*************** InitializeAttributeOids(Relation indexRe
*** 315,321 ****
--- 316,325 ----
      tupleDescriptor = RelationGetDescr(indexRelation);

      for (i = 0; i < numatts; i += 1)
+     {
          tupleDescriptor->attrs[i]->attrelid = indexoid;
+         tupleDescriptor->attrs[i]->attkind = RELKIND_INDEX;
+     }
  }

  /* ----------------------------------------------------------------
*************** AppendAttributeTuples(Relation indexRela
*** 351,357 ****
          Assert(indexTupDesc->attrs[i]->attnum == i + 1);
          Assert(indexTupDesc->attrs[i]->attcacheoff == -1);

!         InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate);
      }

      CatalogCloseIndexes(indstate);
--- 355,361 ----
          Assert(indexTupDesc->attrs[i]->attnum == i + 1);
          Assert(indexTupDesc->attrs[i]->attcacheoff == -1);

!         InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate, NIL);
      }

      CatalogCloseIndexes(indstate);
*************** index_create(Oid heapRelationId,
*** 630,635 ****
--- 634,647 ----
      Assert(indexRelationId == RelationGetRelid(indexRelation));

      /*
+      * Fixup rel->rd_att->tdhassecXXX
+      */
+     indexRelation->rd_att->tdhasrowacl
+         = pgaceTupleDescHasRowAcl(indexRelation, NIL);
+     indexRelation->rd_att->tdhasseclabel
+         = pgaceTupleDescHasSecLabel(indexRelation, NIL);
+
+     /*
       * Obtain exclusive lock on it.  Although no other backends can see it
       * until we commit, this prevents deadlock-risk complaints from lock
       * manager in cases such as CLUSTER.
*************** index_create(Oid heapRelationId,
*** 652,658 ****
       */
      InsertPgClassTuple(pg_class, indexRelation,
                         RelationGetRelid(indexRelation),
!                        reloptions);

      /* done with pg_class */
      heap_close(pg_class, RowExclusiveLock);
--- 664,670 ----
       */
      InsertPgClassTuple(pg_class, indexRelation,
                         RelationGetRelid(indexRelation),
!                        reloptions, NIL);

      /* done with pg_class */
      heap_close(pg_class, RowExclusiveLock);
diff -Nrpc base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c
*** base/src/backend/catalog/pg_aggregate.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/catalog/pg_aggregate.c    Sat Jan  3 15:58:18 2009
*************** AggregateCreate(const char *aggName,
*** 231,237 ****
                                NIL,                        /* parameterDefaults */
                                PointerGetDatum(NULL),    /* proconfig */
                                1,    /* procost */
!                               0);        /* prorows */

      /*
       * Okay to create the pg_aggregate entry.
--- 231,238 ----
                                NIL,                        /* parameterDefaults */
                                PointerGetDatum(NULL),    /* proconfig */
                                1,    /* procost */
!                               0,        /* prorows */
!                               NULL);    /* PGACE opaque */

      /*
       * Okay to create the pg_aggregate entry.
diff -Nrpc base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c
*** base/src/backend/catalog/pg_largeobject.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/catalog/pg_largeobject.c    Sat Jan  3 15:58:18 2009
***************
*** 18,23 ****
--- 18,24 ----
  #include "access/heapam.h"
  #include "catalog/indexing.h"
  #include "catalog/pg_largeobject.h"
+ #include "security/pgace.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
  #include "utils/rel.h"
*************** LargeObjectCreate(Oid loid)
*** 59,64 ****
--- 60,67 ----

      ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls);

+     pgaceLargeObjectCreate(pg_largeobject, ntup);
+
      /*
       * Insert it
       */
*************** LargeObjectDrop(Oid loid)
*** 80,85 ****
--- 83,89 ----
      ScanKeyData skey[1];
      SysScanDesc sd;
      HeapTuple    tuple;
+     void       *pgaceItem = NULL;

      ScanKeyInit(&skey[0],
                  Anum_pg_largeobject_loid,
*************** LargeObjectDrop(Oid loid)
*** 93,98 ****
--- 97,103 ----

      while ((tuple = systable_getnext(sd)) != NULL)
      {
+         pgaceLargeObjectDrop(pg_largeobject, tuple, &pgaceItem);
          simple_heap_delete(pg_largeobject, &tuple->t_self);
          found = true;
      }
diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c
*** base/src/backend/catalog/pg_proc.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/catalog/pg_proc.c    Fri Jan 23 10:55:35 2009
***************
*** 29,34 ****
--- 29,35 ----
  #include "miscadmin.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/parse_type.h"
+ #include "security/pgace.h"
  #include "tcop/pquery.h"
  #include "tcop/tcopprot.h"
  #include "utils/acl.h"
*************** ProcedureCreate(const char *procedureNam
*** 78,84 ****
                  List *parameterDefaults,
                  Datum proconfig,
                  float4 procost,
!                 float4 prorows)
  {
      Oid            retval;
      int            parameterCount;
--- 79,86 ----
                  List *parameterDefaults,
                  Datum proconfig,
                  float4 procost,
!                 float4 prorows,
!                 void *pgaceItem)
  {
      Oid            retval;
      int            parameterCount;
*************** ProcedureCreate(const char *procedureNam
*** 474,479 ****
--- 476,482 ----

          /* Okay, do it... */
          tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
+         pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem);
          simple_heap_update(rel, &tup->t_self, tup);

          ReleaseSysCache(oldtup);
*************** ProcedureCreate(const char *procedureNam
*** 483,488 ****
--- 486,492 ----
      {
          /* Creating a new procedure */
          tup = heap_form_tuple(tupDesc, values, nulls);
+         pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem);
          simple_heap_insert(rel, tup);
          is_update = false;
      }
diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c
*** base/src/backend/catalog/toasting.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/catalog/toasting.c    Sat Jan  3 15:58:18 2009
*************** create_toast_table(Relation rel, Oid toa
*** 200,206 ****
                                             0,
                                             ONCOMMIT_NOOP,
                                             (Datum) 0,
!                                            true);

      /* make the toast relation visible, else index creation will fail */
      CommandCounterIncrement();
--- 200,207 ----
                                             0,
                                             ONCOMMIT_NOOP,
                                             (Datum) 0,
!                                            true,
!                                            NIL);

      /* make the toast relation visible, else index creation will fail */
      CommandCounterIncrement();
diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c
*** base/src/backend/commands/cluster.c    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/commands/cluster.c    Thu Jan 22 14:41:23 2009
*************** make_new_heap(Oid OIDOldHeap, const char
*** 711,717 ****
                                            0,
                                            ONCOMMIT_NOOP,
                                            reloptions,
!                                           allowSystemTableMods);

      ReleaseSysCache(tuple);

--- 711,718 ----
                                            0,
                                            ONCOMMIT_NOOP,
                                            reloptions,
!                                           allowSystemTableMods,
!                                           NIL);

      ReleaseSysCache(tuple);

*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl
*** 906,911 ****
--- 907,920 ----
          if (NewHeap->rd_rel->relhasoids)
              HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple));

+         /* Preserve RowACL, if any */
+         if (HeapTupleHasRowAcl(copiedTuple))
+             HeapTupleSetRowAcl(copiedTuple, HeapTupleGetRowAcl(tuple));
+
+         /* Preserve SecLabel, if any */
+         if (HeapTupleHasSecLabel(copiedTuple))
+             HeapTupleSetSecLabel(copiedTuple, HeapTupleGetSecLabel(tuple));
+
          /* The heap rewrite module does the rest */
          rewrite_heap_tuple(rwstate, tuple, copiedTuple);

diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c
*** base/src/backend/commands/copy.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/commands/copy.c    Wed Jan 14 15:02:53 2009
***************
*** 21,27 ****
--- 21,29 ----
  #include <arpa/inet.h>

  #include "access/heapam.h"
+ #include "access/sysattr.h"
  #include "access/xact.h"
+ #include "catalog/heap.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/copy.h"
***************
*** 34,39 ****
--- 36,42 ----
  #include "optimizer/planner.h"
  #include "parser/parse_relation.h"
  #include "rewrite/rewriteHandler.h"
+ #include "security/pgace.h"
  #include "storage/fd.h"
  #include "tcop/tcopprot.h"
  #include "utils/acl.h"
*************** typedef struct CopyStateData
*** 160,165 ****
--- 163,176 ----
      char       *raw_buf;
      int            raw_buf_index;    /* next byte to process */
      int            raw_buf_len;    /* total # of bytes stored */
+
+     /* dump/restore support for security_acl */
+     FmgrInfo    rowacl_out_function;
+     bool        rowacl_force_quot;
+
+     /* dump/restore support for security_label */
+     FmgrInfo    seclabel_out_function;
+     bool        seclabel_force_quot;
  } CopyStateData;

  typedef CopyStateData *CopyState;
*************** static const char BinarySignature[11] =
*** 243,249 ****
  /* non-export function prototypes */
  static void DoCopyTo(CopyState cstate);
  static void CopyTo(CopyState cstate);
! static void CopyOneRowTo(CopyState cstate, Oid tupleOid,
               Datum *values, bool *nulls);
  static void CopyFrom(CopyState cstate);
  static bool CopyReadLine(CopyState cstate);
--- 254,260 ----
  /* non-export function prototypes */
  static void DoCopyTo(CopyState cstate);
  static void CopyTo(CopyState cstate);
! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid rowAclId, Oid secLabelId,
               Datum *values, bool *nulls);
  static void CopyFrom(CopyState cstate);
  static bool CopyReadLine(CopyState cstate);
*************** DoCopy(const CopyStmt *stmt, const char
*** 1072,1077 ****
--- 1083,1090 ----
      /* Generate or convert list of attributes to process */
      cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);

+     pgaceCopyTable(cstate->rel, cstate->attnumlist, is_from);
+
      num_phys_attrs = tupDesc->natts;

      /* Convert FORCE QUOTE name list to per-column flags, check validity */
*************** DoCopy(const CopyStmt *stmt, const char
*** 1088,1098 ****
              int            attnum = lfirst_int(cur);

              if (!list_member_int(cstate->attnumlist, attnum))
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                     errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
!                           NameStr(tupDesc->attrs[attnum - 1]->attname))));
!             cstate->force_quote_flags[attnum - 1] = true;
          }
      }

--- 1101,1136 ----
              int            attnum = lfirst_int(cur);

              if (!list_member_int(cstate->attnumlist, attnum))
+             {
+                 Form_pg_attribute attForm;
+
+                 if (SystemAttributeIsWritable(attnum))
+                     attForm = SystemAttributeDefinition(attnum, true);
+                 else
+                     attForm = tupDesc->attrs[attnum - 1];
+
+                 Assert(attForm != NULL);
+
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                     errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
!                           NameStr(attForm->attname))));
!             }
!
!             switch (attnum)
!             {
!             case SecurityAclAttributeNumber:
!                 cstate->rowacl_force_quot = true;
!                 break;
!
!             case SecurityLabelAttributeNumber:
!                 cstate->seclabel_force_quot = true;
!                 break;
!
!             default:
!                 cstate->force_quote_flags[attnum - 1] = true;
!                 break;
!             }
          }
      }

*************** DoCopy(const CopyStmt *stmt, const char
*** 1110,1119 ****
              int            attnum = lfirst_int(cur);

              if (!list_member_int(cstate->attnumlist, attnum))
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                  errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY",
!                        NameStr(tupDesc->attrs[attnum - 1]->attname))));
              cstate->force_notnull_flags[attnum - 1] = true;
          }
      }
--- 1148,1171 ----
              int            attnum = lfirst_int(cur);

              if (!list_member_int(cstate->attnumlist, attnum))
+             {
+                 Form_pg_attribute attForm;
+
+                 if (SystemAttributeIsWritable(attnum))
+                     attForm = SystemAttributeDefinition(attnum, true);
+                 else
+                     attForm = tupDesc->attrs[attnum - 1];
+
+                 Assert(attForm != NULL);
+
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                  errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY",
!                        NameStr(attForm->attname))));
!             }
!             if (SystemAttributeIsWritable(attnum))
!                 continue;    /* ignore, if specified */
!
              cstate->force_notnull_flags[attnum - 1] = true;
          }
      }
*************** DoCopyTo(CopyState cstate)
*** 1242,1247 ****
--- 1294,1302 ----
              ereport(ERROR,
                      (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                       errmsg("\"%s\" is a directory", cstate->filename)));
+
+         pgaceCopyFile(cstate->rel, fileno(cstate->copy_file),
+                       cstate->filename, false);
      }

      PG_TRY();
*************** CopyTo(CopyState cstate)
*** 1305,1320 ****
          int            attnum = lfirst_int(cur);
          Oid            out_func_oid;
          bool        isvarlena;

          if (cstate->binary)
!             getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
                                      &out_func_oid,
                                      &isvarlena);
          else
!             getTypeOutputInfo(attr[attnum - 1]->atttypid,
                                &out_func_oid,
                                &isvarlena);
!         fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]);
      }

      /*
--- 1360,1393 ----
          int            attnum = lfirst_int(cur);
          Oid            out_func_oid;
          bool        isvarlena;
+         FmgrInfo    *out_fmgr;
+         Form_pg_attribute attForm;
+
+         switch (attnum)
+         {
+         case SecurityAclAttributeNumber:
+             attForm = SystemAttributeDefinition(attnum, true);
+             out_fmgr = &cstate->rowacl_out_function;
+             break;
+         case SecurityLabelAttributeNumber:
+             attForm = SystemAttributeDefinition(attnum, true);
+             out_fmgr = &cstate->seclabel_out_function;
+             break;
+         default:    /* user columns */
+             attForm = attr[attnum - 1];
+             out_fmgr = &cstate->out_functions[attnum - 1];
+             break;
+         }

          if (cstate->binary)
!             getTypeBinaryOutputInfo(attForm->atttypid,
                                      &out_func_oid,
                                      &isvarlena);
          else
!             getTypeOutputInfo(attForm->atttypid,
                                &out_func_oid,
                                &isvarlena);
!         fmgr_info(out_func_oid, out_fmgr);
      }

      /*
*************** CopyTo(CopyState cstate)
*** 1369,1375 ****
                      CopySendChar(cstate, cstate->delim[0]);
                  hdr_delim = true;

!                 colname = NameStr(attr[attnum - 1]->attname);

                  CopyAttributeOutCSV(cstate, colname, false,
                                      list_length(cstate->attnumlist) == 1);
--- 1442,1455 ----
                      CopySendChar(cstate, cstate->delim[0]);
                  hdr_delim = true;

!                 if (SystemAttributeIsWritable(attnum))
!                 {
!                     Form_pg_attribute attForm
!                         = SystemAttributeDefinition(attnum, true);
!                     colname = NameStr(attForm->attname);
!                 }
!                 else
!                     colname = NameStr(attr[attnum - 1]->attname);

                  CopyAttributeOutCSV(cstate, colname, false,
                                      list_length(cstate->attnumlist) == 1);
*************** CopyTo(CopyState cstate)
*** 1395,1405 ****
          {
              CHECK_FOR_INTERRUPTS();

              /* Deconstruct the tuple ... faster than repeated heap_getattr */
              heap_deform_tuple(tuple, tupDesc, values, nulls);

              /* Format and send the data */
!             CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
          }

          heap_endscan(scandesc);
--- 1475,1492 ----
          {
              CHECK_FOR_INTERRUPTS();

+             if (!pgaceCopyToTuple(cstate->rel, cstate->attnumlist, tuple))
+                 continue;
+
              /* Deconstruct the tuple ... faster than repeated heap_getattr */
              heap_deform_tuple(tuple, tupDesc, values, nulls);

              /* Format and send the data */
!             CopyOneRowTo(cstate,
!                          HeapTupleGetOid(tuple),
!                          HeapTupleGetRowAcl(tuple),
!                          HeapTupleGetSecLabel(tuple),
!                          values, nulls);
          }

          heap_endscan(scandesc);
*************** CopyTo(CopyState cstate)
*** 1425,1431 ****
   * Emit one row during CopyTo().
   */
  static void
! CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
  {
      bool        need_delim = false;
      FmgrInfo   *out_functions = cstate->out_functions;
--- 1512,1519 ----
   * Emit one row during CopyTo().
   */
  static void
! CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid rowAclId, Oid secLabelId,
!              Datum *values, bool *nulls)
  {
      bool        need_delim = false;
      FmgrInfo   *out_functions = cstate->out_functions;
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 1464,1471 ****
      foreach(cur, cstate->attnumlist)
      {
          int            attnum = lfirst_int(cur);
!         Datum        value = values[attnum - 1];
!         bool        isnull = nulls[attnum - 1];

          if (!cstate->binary)
          {
--- 1552,1561 ----
      foreach(cur, cstate->attnumlist)
      {
          int            attnum = lfirst_int(cur);
!         Datum        value;
!         bool        isnull;
!         bool        force_quot;
!         FmgrInfo    *out_fmgr;

          if (!cstate->binary)
          {
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 1474,1479 ****
--- 1564,1594 ----
              need_delim = true;
          }

+         switch (attnum)
+         {
+         case SecurityAclAttributeNumber:
+             value = PointerGetDatum(rowaclSidToSecurityAcl(rowAclId,
+                                             RelationGetForm(cstate->rel)->relowner));
+             isnull = false;
+             force_quot = cstate->rowacl_force_quot;
+             out_fmgr = &cstate->rowacl_out_function;
+             break;
+
+         case SecurityLabelAttributeNumber:
+             value = CStringGetTextDatum(pgaceSidToSecurityLabel(secLabelId));
+             isnull = false;
+             force_quot = cstate->seclabel_force_quot;
+             out_fmgr = &cstate->seclabel_out_function;
+             break;
+
+         default:
+             value = values[attnum - 1];
+             isnull = nulls[attnum - 1];
+             force_quot = cstate->force_quote_flags[attnum - 1];
+             out_fmgr = &out_functions[attnum - 1];
+             break;
+         }
+
          if (isnull)
          {
              if (!cstate->binary)
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 1485,1495 ****
          {
              if (!cstate->binary)
              {
!                 string = OutputFunctionCall(&out_functions[attnum - 1],
!                                             value);
                  if (cstate->csv_mode)
!                     CopyAttributeOutCSV(cstate, string,
!                                         cstate->force_quote_flags[attnum - 1],
                                          list_length(cstate->attnumlist) == 1);
                  else
                      CopyAttributeOutText(cstate, string);
--- 1600,1608 ----
          {
              if (!cstate->binary)
              {
!                 string = OutputFunctionCall(out_fmgr, value);
                  if (cstate->csv_mode)
!                     CopyAttributeOutCSV(cstate, string, force_quot,
                                          list_length(cstate->attnumlist) == 1);
                  else
                      CopyAttributeOutText(cstate, string);
*************** CopyOneRowTo(CopyState cstate, Oid tuple
*** 1498,1505 ****
              {
                  bytea       *outputbytes;

!                 outputbytes = SendFunctionCall(&out_functions[attnum - 1],
!                                                value);
                  CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
                  CopySendData(cstate, VARDATA(outputbytes),
                               VARSIZE(outputbytes) - VARHDRSZ);
--- 1611,1617 ----
              {
                  bytea       *outputbytes;

!                 outputbytes = SendFunctionCall(out_fmgr, value);
                  CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
                  CopySendData(cstate, VARDATA(outputbytes),
                               VARSIZE(outputbytes) - VARHDRSZ);
*************** CopyFrom(CopyState cstate)
*** 1633,1642 ****
--- 1745,1759 ----
                  num_defaults;
      FmgrInfo   *in_functions;
      FmgrInfo    oid_in_function;
+     FmgrInfo    rowacl_in_function;
+     FmgrInfo    seclabel_in_function;
      Oid           *typioparams;
      Oid            oid_typioparam;
+     Oid            rowacl_typioparam;
+     Oid            seclabel_typioparam;
      int            attnum;
      int            i;
+     ListCell   *l;
      Oid            in_func_oid;
      Datum       *values;
      bool       *nulls;
*************** CopyFrom(CopyState cstate)
*** 1737,1742 ****
--- 1854,1862 ----
              ereport(ERROR,
                      (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                       errmsg("\"%s\" is a directory", cstate->filename)));
+
+         pgaceCopyFile(cstate->rel, fileno(cstate->copy_file),
+                       cstate->filename, true);
      }

      tupDesc = RelationGetDescr(cstate->rel);
*************** CopyFrom(CopyState cstate)
*** 1872,1877 ****
--- 1992,2027 ----
          fmgr_info(in_func_oid, &oid_in_function);
      }

+     foreach (l, cstate->attnumlist)
+     {
+         switch (lfirst_int(l))
+         {
+         case SecurityAclAttributeNumber:
+             if (!cstate->binary)
+                 getTypeInputInfo(ACLITEMARRAYOID,
+                                  &in_func_oid,
+                                  &rowacl_typioparam);
+             else
+                 getTypeBinaryInputInfo(ACLITEMARRAYOID,
+                                        &in_func_oid,
+                                        &rowacl_typioparam);
+             fmgr_info(in_func_oid, &rowacl_in_function);
+             break;
+
+         case SecurityLabelAttributeNumber:
+             if (!cstate->binary)
+                 getTypeInputInfo(TEXTOID,
+                                  &in_func_oid,
+                                  &seclabel_typioparam);
+             else
+                 getTypeBinaryInputInfo(TEXTOID,
+                                        &in_func_oid,
+                                        &seclabel_typioparam);
+             fmgr_info(in_func_oid, &seclabel_in_function);
+             break;
+         }
+     }
+
      values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
      nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));

*************** CopyFrom(CopyState cstate)
*** 1906,1911 ****
--- 2056,2063 ----
      {
          bool        skip_tuple;
          Oid            loaded_oid = InvalidOid;
+         Datum        loaded_rowacl = PointerGetDatum(NULL);
+         Datum        loaded_seclabel = PointerGetDatum(NULL);

          CHECK_FOR_INTERRUPTS();

*************** CopyFrom(CopyState cstate)
*** 1980,1985 ****
--- 2132,2176 ----
                  int            attnum = lfirst_int(cur);
                  int            m = attnum - 1;

+                 if (SystemAttributeIsWritable(attnum))
+                 {
+                     Form_pg_attribute attForm
+                         = SystemAttributeDefinition(attnum, true);
+
+                     if (fieldno >= fldct)
+                         ereport(ERROR,
+                                 (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+                                  errmsg("missing data for column \"%s\"",
+                                         NameStr(attForm->attname))));
+                     string = field_strings[fieldno++];
+                     cstate->cur_attname = NameStr(attForm->attname);
+                     cstate->cur_attval = string;
+                     if (string)
+                     {
+                         switch (attnum)
+                         {
+                         case SecurityAclAttributeNumber:
+                             loaded_rowacl
+                                 = InputFunctionCall(&rowacl_in_function,
+                                                     string,
+                                                     rowacl_typioparam,
+                                                     attForm->atttypmod);
+                             break;
+
+                         case SecurityLabelAttributeNumber:
+                             loaded_seclabel
+                                 = InputFunctionCall(&seclabel_in_function,
+                                                     string,
+                                                     seclabel_typioparam,
+                                                     attForm->atttypmod);
+                             break;
+                         }
+                     }
+                     cstate->cur_attname = NULL;
+                     cstate->cur_attval = NULL;
+                     continue;
+                 }
+
                  if (fieldno >= fldct)
                      ereport(ERROR,
                              (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
*************** CopyFrom(CopyState cstate)
*** 2050,2055 ****
--- 2241,2281 ----
                  int            attnum = lfirst_int(cur);
                  int            m = attnum - 1;

+                 if (SystemAttributeIsWritable(attnum))
+                 {
+                     Form_pg_attribute attForm
+                         = SystemAttributeDefinition(attnum, false);
+                     Datum tmp;
+
+                     cstate->cur_attname = NameStr(attForm->attname);
+                     i++;
+
+                     switch (attnum)
+                     {
+                     case SecurityAclAttributeNumber:
+                         tmp = CopyReadBinaryAttribute(cstate, i,
+                                                       &rowacl_in_function,
+                                                       rowacl_typioparam,
+                                                       attForm->atttypmod,
+                                                       &isnull);
+                         if (!isnull)
+                             loaded_seclabel = tmp;
+                         break;
+
+                     case SecurityLabelAttributeNumber:
+                         tmp = CopyReadBinaryAttribute(cstate, i,
+                                                       &seclabel_in_function,
+                                                       seclabel_typioparam,
+                                                       attForm->atttypmod,
+                                                       &isnull);
+                         if (!isnull)
+                             loaded_seclabel = tmp;
+                         break;
+                     }
+                     cstate->cur_attname = NULL;
+                     continue;
+                 }
+
                  cstate->cur_attname = NameStr(attr[m]->attname);
                  i++;
                  values[m] = CopyReadBinaryAttribute(cstate,
*************** CopyFrom(CopyState cstate)
*** 2079,2084 ****
--- 2305,2322 ----
          if (cstate->oids && file_has_oids)
              HeapTupleSetOid(tuple, loaded_oid);

+         if (loaded_rowacl != PointerGetDatum(NULL))
+         {
+             Acl *acl = DatumGetAclP(loaded_rowacl);
+             HeapTupleSetRowAcl(tuple, rowaclSecurityAclToSid(acl));
+         }
+
+         if (loaded_seclabel != PointerGetDatum(NULL))
+         {
+             char *label = TextDatumGetCString(loaded_seclabel);
+             HeapTupleSetSecLabel(tuple, pgaceSecurityLabelToSid(label));
+         }
+
          /* Triggers and stuff need to be invoked in query context. */
          MemoryContextSwitchTo(oldcontext);

*************** CopyFrom(CopyState cstate)
*** 2101,2106 ****
--- 2339,2349 ----
              }
          }

+         if (!skip_tuple && !rowaclHeapTupleInsert(cstate->rel, tuple, false, false))
+             skip_tuple = true;
+         if (!skip_tuple && !pgaceHeapTupleInsert(cstate->rel, tuple, false, false))
+             skip_tuple = true;
+
          if (!skip_tuple)
          {
              /* Place tuple in tuple slot */
*************** CopyGetAttnums(TupleDesc tupDesc, Relati
*** 3379,3384 ****
--- 3622,3637 ----
                      break;
                  }
              }
+
+             /* Is it writable system column? */
+             if (attnum == InvalidAttrNumber)
+             {
+                 Form_pg_attribute attForm
+                     = SystemAttributeByName(name, tupDesc->tdhasoid);
+                 if (attForm && SystemAttributeIsWritable(attForm->attnum))
+                     attnum = attForm->attnum;
+             }
+
              if (attnum == InvalidAttrNumber)
              {
                  if (rel != NULL)
*************** copy_dest_receive(TupleTableSlot *slot,
*** 3428,3434 ****
      slot_getallattrs(slot);

      /* And send the data */
!     CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
  }

  /*
--- 3681,3689 ----
      slot_getallattrs(slot);

      /* And send the data */
!     CopyOneRowTo(cstate,
!                  InvalidOid, InvalidOid, InvalidOid,
!                  slot->tts_values, slot->tts_isnull);
  }

  /*
diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c
*** base/src/backend/commands/dbcommands.c    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/commands/dbcommands.c    Thu Jan 22 14:41:23 2009
***************
*** 41,46 ****
--- 41,47 ----
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "postmaster/bgwriter.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/fd.h"
  #include "storage/lmgr.h"
*************** createdb(const CreatedbStmt *stmt)
*** 119,124 ****
--- 120,126 ----
      DefElem    *dcollate = NULL;
      DefElem    *dctype = NULL;
      DefElem    *dconnlimit = NULL;
+     DefElem       *dpgace_item = NULL;
      char       *dbname = stmt->dbname;
      char       *dbowner = NULL;
      const char *dbtemplate = NULL;
*************** createdb(const CreatedbStmt *stmt)
*** 200,205 ****
--- 202,214 ----
                       errmsg("LOCATION is not supported anymore"),
                       errhint("Consider using tablespaces instead.")));
          }
+         else if (pgaceIsGramSecurityItem(defel)) {
+             if (dpgace_item)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SYNTAX_ERROR),
+                          errmsg("conflicting or redundant options")));
+             dpgace_item = defel;
+         }
          else
              elog(ERROR, "option \"%s\" not recognized",
                   defel->defname);
*************** createdb(const CreatedbStmt *stmt)
*** 532,537 ****
--- 541,547 ----
                             new_record, new_record_nulls);

      HeapTupleSetOid(tuple, dboid);
+     pgaceGramCreateDatabase(pg_database_rel, tuple, dpgace_item);

      simple_heap_insert(pg_database_rel, tuple);

*************** AlterDatabase(AlterDatabaseStmt *stmt, b
*** 1278,1283 ****
--- 1288,1294 ----
      int            connlimit = -1;
      DefElem    *dconnlimit = NULL;
      DefElem    *dtablespace = NULL;
+     DefElem       *dpgace_item = NULL;
      Datum        new_record[Natts_pg_database];
      bool        new_record_nulls[Natts_pg_database];
      bool        new_record_repl[Natts_pg_database];
*************** AlterDatabase(AlterDatabaseStmt *stmt, b
*** 1303,1308 ****
--- 1314,1327 ----
                           errmsg("conflicting or redundant options")));
              dtablespace = defel;
          }
+         else if (pgaceIsGramSecurityItem(defel))
+         {
+             if (dpgace_item)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SYNTAX_ERROR),
+                          errmsg("conflicting or redundant options")));
+             dpgace_item = defel;
+         }
          else
              elog(ERROR, "option \"%s\" not recognized",
                   defel->defname);
*************** AlterDatabase(AlterDatabaseStmt *stmt, b
*** 1358,1363 ****
--- 1377,1383 ----

      newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record,
                                  new_record_nulls, new_record_repl);
+     pgaceGramAlterDatabase(rel, newtuple, dpgace_item);
      simple_heap_update(rel, &tuple->t_self, newtuple);

      /* Update indexes */
diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c
*** base/src/backend/commands/functioncmds.c    Tue Jan  6 14:45:31 2009
--- sepgsql/src/backend/commands/functioncmds.c    Tue Jan  6 14:52:44 2009
***************
*** 53,58 ****
--- 53,59 ----
  #include "parser/parse_expr.h"
  #include "parser/parse_func.h"
  #include "parser/parse_type.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
*************** compute_attributes_sql_style(List *optio
*** 517,523 ****
                               bool *security_definer,
                               ArrayType **proconfig,
                               float4 *procost,
!                              float4 *prorows)
  {
      ListCell   *option;
      DefElem    *as_item = NULL;
--- 518,525 ----
                               bool *security_definer,
                               ArrayType **proconfig,
                               float4 *procost,
!                              float4 *prorows,
!                              DefElem **pgaceItem)
  {
      ListCell   *option;
      DefElem    *as_item = NULL;
*************** compute_attributes_sql_style(List *optio
*** 558,563 ****
--- 560,573 ----
                           errmsg("conflicting or redundant options")));
              windowfunc_item = defel;
          }
+         else if (pgaceIsGramSecurityItem(defel))
+         {
+             if (*pgaceItem)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SYNTAX_ERROR),
+                          errmsg("conflicting or redundant options")));
+             *pgaceItem = defel;
+         }
          else if (compute_common_attribute(defel,
                                            &volatility_item,
                                            &strict_item,
*************** CreateFunction(CreateFunctionStmt *stmt,
*** 765,770 ****
--- 775,781 ----
      HeapTuple    languageTuple;
      Form_pg_language languageStruct;
      List       *as_clause;
+     DefElem    *pgaceItem = NULL;

      /* Convert list of names to a name and namespace */
      namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
*************** CreateFunction(CreateFunctionStmt *stmt,
*** 790,796 ****
                                   &as_clause, &language,
                                   &isWindowFunc, &volatility,
                                   &isStrict, &security,
!                                  &proconfig, &procost, &prorows);

      /* Convert language name to canonical case */
      languageName = case_translate_language_name(language);
--- 801,807 ----
                                   &as_clause, &language,
                                   &isWindowFunc, &volatility,
                                   &isStrict, &security,
!                                  &proconfig, &procost, &prorows, &pgaceItem);

      /* Convert language name to canonical case */
      languageName = case_translate_language_name(language);
*************** CreateFunction(CreateFunctionStmt *stmt,
*** 926,932 ****
                      parameterDefaults,
                      PointerGetDatum(proconfig),
                      procost,
!                     prorows);
  }


--- 937,944 ----
                      parameterDefaults,
                      PointerGetDatum(proconfig),
                      procost,
!                     prorows,
!                     pgaceItem);
  }


*************** AlterFunction(AlterFunctionStmt *stmt)
*** 1276,1281 ****
--- 1288,1294 ----
      List       *set_items = NIL;
      DefElem    *cost_item = NULL;
      DefElem    *rows_item = NULL;
+     DefElem       *pgaceItem = NULL;

      rel = heap_open(ProcedureRelationId, RowExclusiveLock);

*************** AlterFunction(AlterFunctionStmt *stmt)
*** 1307,1312 ****
--- 1320,1334 ----
      {
          DefElem    *defel = (DefElem *) lfirst(l);

+         if (pgaceIsGramSecurityItem(defel)) {
+             if (pgaceItem)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SYNTAX_ERROR),
+                          errmsg("conflicting or redundant options")));
+             pgaceItem = defel;
+             continue;
+         }
+
          if (compute_common_attribute(defel,
                                       &volatility_item,
                                       &strict_item,
*************** AlterFunction(AlterFunctionStmt *stmt)
*** 1377,1382 ****
--- 1399,1405 ----
          tup = heap_modify_tuple(tup, RelationGetDescr(rel),
                                 repl_val, repl_null, repl_repl);
      }
+     pgaceGramAlterFunction(rel, tup, pgaceItem);

      /* Do the update */
      simple_heap_update(rel, &tup->t_self, tup);
diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c
*** base/src/backend/commands/lockcmds.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/commands/lockcmds.c    Tue Jan 13 09:39:35 2009
***************
*** 20,25 ****
--- 20,26 ----
  #include "miscadmin.h"
  #include "optimizer/prep.h"
  #include "parser/parse_clause.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
*************** LockTableCommand(LockStmt *lockstmt)
*** 71,76 ****
--- 72,79 ----
                  aclcheck_error(aclresult, ACL_KIND_CLASS,
                                 get_rel_name(childreloid));

+             pgaceLockTable(childreloid);
+
              if (lockstmt->nowait)
                  rel = relation_open_nowait(childreloid, lockstmt->mode);
              else
diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c
*** base/src/backend/commands/proclang.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/commands/proclang.c    Sat Jan  3 15:58:18 2009
*************** CreateProceduralLanguage(CreatePLangStmt
*** 151,157 ****
                                           NIL,
                                           PointerGetDatum(NULL),
                                           1,
!                                          0);
          }

          /*
--- 151,158 ----
                                           NIL,
                                           PointerGetDatum(NULL),
                                           1,
!                                          0,
!                                          NULL);
          }

          /*
*************** CreateProceduralLanguage(CreatePLangStmt
*** 186,192 ****
                                           NIL,
                                           PointerGetDatum(NULL),
                                           1,
!                                          0);
              }
          }
          else
--- 187,194 ----
                                           NIL,
                                           PointerGetDatum(NULL),
                                           1,
!                                          0,
!                                          NULL);
              }
          }
          else
diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c
*** base/src/backend/commands/tablecmds.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/commands/tablecmds.c    Fri Jan 23 10:55:35 2009
***************
*** 63,68 ****
--- 63,69 ----
  #include "parser/parser.h"
  #include "rewrite/rewriteDefine.h"
  #include "rewrite/rewriteHandler.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/lmgr.h"
  #include "storage/smgr.h"
*************** DefineRelation(CreateStmt *stmt, char re
*** 509,515 ****
                                            parentOidCount,
                                            stmt->oncommit,
                                            reloptions,
!                                           allowSystemTableMods);

      StoreCatalogInheritance(relationId, inheritOids);

--- 510,517 ----
                                            parentOidCount,
                                            stmt->oncommit,
                                            reloptions,
!                                           allowSystemTableMods,
!                                           pgaceRelationAttrList(stmt));

      StoreCatalogInheritance(relationId, inheritOids);

*************** ExecuteTruncate(TruncateStmt *stmt)
*** 856,861 ****
--- 858,865 ----
          heap_truncate_check_FKs(rels, false);
  #endif

+     pgaceExecTruncate(rels);
+
      /*
       * If we are asked to restart sequences, find all the sequences,
       * lock them (we only need AccessShareLock because that's all that
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2491,2496 ****
--- 2495,2501 ----
          case AT_DisableRule:
          case AT_AddInherit:        /* INHERIT / NO INHERIT */
          case AT_DropInherit:
+         case AT_SetSecurityLabel:
              ATSimplePermissions(rel, false);
              /* These commands never recurse */
              /* No command-specific prep needed */
*************** ATExecCmd(List **wqueue, AlteredTableInf
*** 2718,2723 ****
--- 2723,2731 ----
          case AT_DropInherit:
              ATExecDropInherit(rel, (RangeVar *) cmd->def);
              break;
+         case AT_SetSecurityLabel:
+             pgaceAlterRelationCommon(rel, cmd);
+             break;
          default:                /* oops */
              elog(ERROR, "unrecognized alter table type: %d",
                   (int) cmd->subtype);
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3056,3066 ****
--- 3064,3080 ----
              if (newrel)
              {
                  Oid            tupOid = InvalidOid;
+                 Oid            tupRowAcl = InvalidOid;
+                 Oid            tupSecLabel = InvalidOid;

                  /* Extract data from old tuple */
                  heap_deform_tuple(tuple, oldTupDesc, values, isnull);
                  if (oldTupDesc->tdhasoid)
                      tupOid = HeapTupleGetOid(tuple);
+                 if (HeapTupleHasRowAcl(tuple))
+                     tupRowAcl = HeapTupleGetRowAcl(tuple);
+                 if (HeapTupleHasSecLabel(tuple))
+                     tupSecLabel = HeapTupleGetSecLabel(tuple);

                  /* Set dropped attributes to null in new tuple */
                  foreach(lc, dropped_attrs)
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3092,3097 ****
--- 3106,3117 ----
                  /* Preserve OID, if any */
                  if (newTupDesc->tdhasoid)
                      HeapTupleSetOid(tuple, tupOid);
+                 /* Preserve RowAcl, if any */
+                 if (HeapTupleHasRowAcl(tuple))
+                     HeapTupleSetRowAcl(tuple, tupRowAcl);
+                 /* Preserve SecLabel, if any */
+                 if (HeapTupleHasSecLabel(tuple))
+                     HeapTupleSetSecLabel(tuple, tupSecLabel);
              }

              /* Now check any constraints on the possibly-changed tuple */
*************** ATExecAddColumn(AlteredTableInfo *tab, R
*** 3585,3590 ****
--- 3605,3611 ----
      attribute.attndims = list_length(colDef->typename->arrayBounds);
      attribute.attstorage = tform->typstorage;
      attribute.attalign = tform->typalign;
+     attribute.attkind = relkind;
      attribute.attnotnull = colDef->is_not_null;
      attribute.atthasdef = false;
      attribute.attisdropped = false;
*************** ATExecAddColumn(AlteredTableInfo *tab, R
*** 3594,3600 ****

      ReleaseSysCache(typeTuple);

!     InsertPgAttributeTuple(attrdesc, &attribute, NULL);

      heap_close(attrdesc, RowExclusiveLock);

--- 3615,3621 ----

      ReleaseSysCache(typeTuple);

!     InsertPgAttributeTuple(attrdesc, &attribute, NULL, NIL);

      heap_close(attrdesc, RowExclusiveLock);

diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c
*** base/src/backend/commands/trigger.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/commands/trigger.c    Fri Jan 23 10:55:35 2009
***************
*** 33,38 ****
--- 33,39 ----
  #include "nodes/makefuncs.h"
  #include "parser/parse_func.h"
  #include "pgstat.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
*************** ExecCallTriggerFunc(TriggerData *trigdat
*** 1560,1569 ****
--- 1561,1576 ----
       * call.
       */
      if (finfo->fn_oid == InvalidOid)
+     {
          fmgr_info(trigdata->tg_trigger->tgfoid, finfo);
+         pgaceCallFunction(finfo);
+     }

      Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);

+     if (!pgaceCallTriggerFunction(trigdata))
+         return (HeapTuple) NULL;
+
      /*
       * If doing EXPLAIN ANALYZE, start charging time to this trigger.
       */
*************** ExecBRUpdateTriggers(EState *estate, Res
*** 1984,1989 ****
--- 1991,2014 ----
      if (newSlot != NULL)
          intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);

+     /*
+      * The before-row-triggers are fired prior to transcribing system
+      * attributes from the old tuple to the new one. When no explicit
+      * new values are given, we have to preserve them, so the following
+      * code do it to avoid to make triggers get confusion.
+      */
+     if (HeapTupleHasOid(newtuple) &&
+         !OidIsValid(HeapTupleGetOid(newtuple)))
+         HeapTupleSetOid(newtuple, HeapTupleGetOid(trigtuple));
+
+     if (HeapTupleHasRowAcl(newtuple) &&
+         !OidIsValid(HeapTupleGetRowAcl(newtuple)))
+         HeapTupleSetRowAcl(newtuple, HeapTupleGetRowAcl(trigtuple));
+
+     if (HeapTupleHasSecLabel(newtuple) &&
+         !OidIsValid(HeapTupleGetSecLabel(newtuple)))
+         HeapTupleSetSecLabel(newtuple, HeapTupleGetSecLabel(trigtuple));
+
      LocTriggerData.type = T_TriggerData;
      LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
          TRIGGER_EVENT_ROW |
diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c
*** base/src/backend/executor/execJunk.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/execJunk.c    Sat Jan  3 15:58:18 2009
***************
*** 60,66 ****
   * An optional resultSlot can be passed as well.
   */
  JunkFilter *
! ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
  {
      JunkFilter *junkfilter;
      TupleDesc    cleanTupType;
--- 60,68 ----
   * An optional resultSlot can be passed as well.
   */
  JunkFilter *
! ExecInitJunkFilter(List *targetList,
!                    bool hasoid, bool hassecacl, bool hasseclabel,
!                    TupleTableSlot *slot)
  {
      JunkFilter *junkfilter;
      TupleDesc    cleanTupType;
*************** ExecInitJunkFilter(List *targetList, boo
*** 72,78 ****
      /*
       * Compute the tuple descriptor for the cleaned tuple.
       */
!     cleanTupType = ExecCleanTypeFromTL(targetList, hasoid);

      /*
       * Use the given slot, or make a new slot if we weren't given one.
--- 74,80 ----
      /*
       * Compute the tuple descriptor for the cleaned tuple.
       */
!     cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecacl, hasseclabel);

      /*
       * Use the given slot, or make a new slot if we weren't given one.
diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c
*** base/src/backend/executor/execMain.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/executor/execMain.c    Fri Jan 23 10:55:35 2009
***************
*** 50,55 ****
--- 50,56 ----
  #include "optimizer/clauses.h"
  #include "parser/parse_clause.h"
  #include "parser/parsetree.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/lmgr.h"
  #include "storage/smgr.h"
*************** standard_ExecutorStart(QueryDesc *queryD
*** 158,163 ****
--- 159,166 ----
      Assert(queryDesc != NULL);
      Assert(queryDesc->estate == NULL);

+     pgaceExecutorStart(queryDesc, eflags);
+
      /*
       * If the transaction is read-only, we need to check if any writes are
       * planned to non-temporary tables.  EXPLAIN is considered read-only.
*************** InitPlan(QueryDesc *queryDesc, int eflag
*** 901,916 ****
                  for (i = 0; i < as_nplans; i++)
                  {
                      PlanState  *subplan = appendplans[i];
                      JunkFilter *j;

                      if (operation == CMD_UPDATE)
!                         ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
!                                             subplan->plan->targetlist);

                      j = ExecInitJunkFilter(subplan->plan->targetlist,
!                             resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
!                                   ExecAllocTableSlot(estate->es_tupleTable));
!
                      /*
                       * Since it must be UPDATE/DELETE, there had better be a
                       * "ctid" junk attribute in the tlist ... but ctid could
--- 904,920 ----
                  for (i = 0; i < as_nplans; i++)
                  {
                      PlanState  *subplan = appendplans[i];
+                     Relation    resultRel = resultRelInfo->ri_RelationDesc;
                      JunkFilter *j;

                      if (operation == CMD_UPDATE)
!                         ExecCheckPlanOutput(resultRel, subplan->plan->targetlist);

                      j = ExecInitJunkFilter(subplan->plan->targetlist,
!                                            RelationGetDescr(resultRel)->tdhasoid,
!                                            RelationGetDescr(resultRel)->tdhasrowacl,
!                                            RelationGetDescr(resultRel)->tdhasseclabel,
!                                            ExecAllocTableSlot(estate->es_tupleTable));
                      /*
                       * Since it must be UPDATE/DELETE, there had better be a
                       * "ctid" junk attribute in the tlist ... but ctid could
*************** InitPlan(QueryDesc *queryDesc, int eflag
*** 952,959 ****
                                          planstate->plan->targetlist);

                  j = ExecInitJunkFilter(planstate->plan->targetlist,
!                                        tupType->tdhasoid,
!                                   ExecAllocTableSlot(estate->es_tupleTable));
                  estate->es_junkFilter = j;
                  if (estate->es_result_relation_info)
                      estate->es_result_relation_info->ri_junkFilter = j;
--- 956,963 ----
                                          planstate->plan->targetlist);

                  j = ExecInitJunkFilter(planstate->plan->targetlist,
!                                        tupType->tdhasoid, tupType->tdhasrowacl, tupType->tdhasseclabel,
!                                        ExecAllocTableSlot(estate->es_tupleTable));
                  estate->es_junkFilter = j;
                  if (estate->es_result_relation_info)
                      estate->es_result_relation_info->ri_junkFilter = j;
*************** InitPlan(QueryDesc *queryDesc, int eflag
*** 1023,1029 ****
           * We assume all the sublists will generate the same output tupdesc.
           */
          tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
!                                  false);

          /* Set up a slot for the output of the RETURNING projection(s) */
          slot = ExecAllocTableSlot(estate->es_tupleTable);
--- 1027,1033 ----
           * We assume all the sublists will generate the same output tupdesc.
           */
          tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
!                                  false, false, false);

          /* Set up a slot for the output of the RETURNING projection(s) */
          slot = ExecAllocTableSlot(estate->es_tupleTable);
*************** ExecContextForcesOids(PlanState *plansta
*** 1346,1351 ****
--- 1350,1417 ----
      return false;
  }

+ /*
+  * ExecContextForcesRowAcl
+  *
+  * We need to ensure that result tuples have space for row level ACLs.
+  * If row_level_acl = true on the given relation, it should be allocated.
+  */
+ bool ExecContextForcesRowAcl(PlanState *planstate, bool *hasrowacl)
+ {
+     if (planstate->state->es_select_into)
+     {
+         IntoClause *into = planstate->state->es_plannedstmt->intoClause;
+
+         Assert(into != NULL);
+
+         *hasrowacl = pgaceTupleDescHasRowAcl(NULL, into->options);
+         return true;
+     }
+     else
+     {
+         ResultRelInfo *ri = planstate->state->es_result_relation_info;
+
+         if (ri && ri->ri_RelationDesc)
+         {
+             *hasrowacl = pgaceTupleDescHasRowAcl(ri->ri_RelationDesc, NIL);
+             return true;
+         }
+     }
+
+     return false;
+ }
+
+ /*
+  * ExecContextForcesSecLabel
+  *
+  * We need to ensure that result tuples have space for security label,
+  * if the security feature need to store it within the given relation.
+  */
+ bool ExecContextForcesSecLabel(PlanState *planstate, bool *hassecurity)
+ {
+     if (planstate->state->es_select_into)
+     {
+         IntoClause *into = planstate->state->es_plannedstmt->intoClause;
+
+         Assert(into != NULL);
+
+         *hassecurity = pgaceTupleDescHasSecLabel(NULL, into->options);
+         return true;
+     }
+     else
+     {
+         ResultRelInfo *ri = planstate->state->es_result_relation_info;
+
+         if (ri && ri->ri_RelationDesc)
+         {
+             *hassecurity = pgaceTupleDescHasSecLabel(ri->ri_RelationDesc, NIL);
+             return true;
+         }
+     }
+
+     return false;
+ }
+
  /* ----------------------------------------------------------------
   *        ExecEndPlan
   *
*************** ExecEndPlan(PlanState *planstate, EState
*** 1426,1431 ****
--- 1492,1581 ----
      }
  }

+ /*
+  * fetchWritableSystemAttribute() fetches writable system column data
+  * using Junkfilter, and saves them at TupleTableSlot temporary.
+  *
+  * storeWritableSystemAttribute() copies these fetched data into
+  * header structure of HeapTuple.
+  */
+ static void
+ fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
+                              Datum *tts_rowacl, Datum *tts_seclabel)
+ {
+     AttrNumber attno;
+     Datum datum;
+     bool isnull;
+
+     /* for Row-level ACLs */
+     attno = ExecFindJunkAttribute(junkfilter, SecurityAclAttributeName);
+     if (attno != InvalidAttrNumber)
+     {
+         datum = ExecGetJunkAttribute(slot, attno, &isnull);
+         if (isnull)
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("setting NULL on \"%s\" system column is not supported",
+                             SecurityAclAttributeName)));
+         *tts_rowacl = datum;
+     }
+
+     /* for Security Label */
+     attno = ExecFindJunkAttribute(junkfilter, SecurityLabelAttributeName);
+     if (attno != InvalidAttrNumber)
+     {
+         datum = ExecGetJunkAttribute(slot, attno, &isnull);
+         if (isnull)
+             ereport(ERROR,
+                     (errcode(ERRCODE_PGACE_ERROR),
+                      errmsg("setting NULL on \"%s\" system column is not supported",
+                             SecurityLabelAttributeName)));
+         *tts_seclabel = datum;
+     }
+ }
+
+ static void
+ storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple)
+ {
+     /* for Row-level ACLs */
+     if (HeapTupleHasRowAcl(tuple))
+     {
+         if (!DatumGetPointer(slot->tts_rowacl))
+             HeapTupleSetRowAcl(tuple, InvalidOid);
+         else
+         {
+             Acl *acl = DatumGetAclP(slot->tts_rowacl);
+             HeapTupleSetRowAcl(tuple, rowaclSecurityAclToSid(acl));
+         }
+     }
+     else if (DatumGetPointer(slot->tts_rowacl))
+     {
+         ereport(ERROR,
+                 (errcode(ERRCODE_ROWACL_ERROR),
+                  errmsg("Row-level ACLs are unavailable for relation: %s",
+                         RelationGetRelationName(rel))));
+     }
+
+     /* for Security Label */
+     if (HeapTupleHasSecLabel(tuple))
+     {
+         if (!DatumGetPointer(slot->tts_seclabel))
+             HeapTupleSetSecLabel(tuple, InvalidOid);
+         else
+         {
+             char *label = TextDatumGetCString(slot->tts_seclabel);
+             HeapTupleSetSecLabel(tuple, pgaceSecurityLabelToSid(label));
+         }
+     }
+     else if (DatumGetPointer(slot->tts_seclabel))
+     {
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("Security Label is unavailable for relation: %s",
+                         RelationGetRelationName(rel))));
+     }
+ }
+
  /* ----------------------------------------------------------------
   *        ExecutePlan
   *
*************** ExecutePlan(EState *estate,
*** 1487,1492 ****
--- 1637,1645 ----
       */
      for (;;)
      {
+         Datum tts_rowacl = PointerGetDatum(NULL);
+         Datum tts_seclabel = PointerGetDatum(NULL);
+
          /* Reset the per-output-tuple exprcontext */
          ResetPerTupleExprContext(estate);

*************** lnext:    ;
*** 1631,1636 ****
--- 1784,1795 ----
              }

              /*
+              * extract writable system attribute
+              */
+             fetchWritableSystemAttribute(junkfilter, slot,
+                                          &tts_rowacl, &tts_seclabel);
+
+             /*
               * extract the 'ctid' junk attribute.
               */
              if (operation == CMD_UPDATE || operation == CMD_DELETE)
*************** lnext:    ;
*** 1657,1662 ****
--- 1816,1823 ----
              if (operation != CMD_DELETE)
                  slot = ExecFilterJunk(junkfilter, slot);
          }
+         slot->tts_rowacl = tts_rowacl;
+         slot->tts_seclabel = tts_seclabel;

          /*
           * now that we have a tuple, do the appropriate thing with it.. either
*************** ExecInsert(TupleTableSlot *slot,
*** 1766,1771 ****
--- 1927,1934 ----
      resultRelInfo = estate->es_result_relation_info;
      resultRelationDesc = resultRelInfo->ri_RelationDesc;

+     storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
+
      /* BEFORE ROW INSERT Triggers */
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
*************** ExecInsert(TupleTableSlot *slot,
*** 1802,1807 ****
--- 1965,1977 ----
          ExecConstraints(resultRelInfo, slot, estate);

      /*
+      * Mandatory access controls of the tuple
+      */
+     if (!pgaceHeapTupleInsert(resultRelationDesc, tuple,
+                               false, !!resultRelInfo->ri_projectReturning))
+         return;
+
+     /*
       * insert the tuple
       *
       * Note: heap_insert returns the tid (location) of the new tuple in the
*************** ExecDelete(ItemPointer tupleid,
*** 1867,1872 ****
--- 2037,2046 ----
              return;
      }

+     if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid,
+                               false, !!resultRelInfo->ri_projectReturning))
+         return;
+
      /*
       * delete the tuple
       *
*************** ExecUpdate(TupleTableSlot *slot,
*** 2003,2008 ****
--- 2177,2184 ----
      resultRelInfo = estate->es_result_relation_info;
      resultRelationDesc = resultRelInfo->ri_RelationDesc;

+     storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
+
      /* BEFORE ROW UPDATE Triggers */
      if (resultRelInfo->ri_TrigDesc &&
          resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
*************** lreplace:;
*** 2047,2052 ****
--- 2223,2235 ----
          ExecConstraints(resultRelInfo, slot, estate);

      /*
+      * Mandatory access controls of the tuple
+      */
+     if (!pgaceHeapTupleUpdate(resultRelationDesc, tupleid, tuple,
+                               false, !!resultRelInfo->ri_projectReturning))
+         return;
+
+     /*
       * replace the heap tuple
       *
       * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
*************** OpenIntoRel(QueryDesc *queryDesc)
*** 2911,2917 ****
                                                0,
                                                into->onCommit,
                                                reloptions,
!                                               allowSystemTableMods);

      FreeTupleDesc(tupdesc);

--- 3094,3101 ----
                                                0,
                                                into->onCommit,
                                                reloptions,
!                                               allowSystemTableMods,
!                                               NIL);

      FreeTupleDesc(tupdesc);

*************** intorel_receive(TupleTableSlot *slot, De
*** 3021,3026 ****
--- 3205,3217 ----
       */
      tuple = ExecMaterializeSlot(slot);

+     storeWritableSystemAttribute(myState->rel, slot, tuple);
+     if (!pgaceHeapTupleInsert(myState->rel, tuple, false, false))
+     {
+         heap_freetuple(tuple);
+         return;
+     }
+
      heap_insert(myState->rel,
                  tuple,
                  myState->estate->es_output_cid,
diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c
*** base/src/backend/executor/execQual.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/executor/execQual.c    Fri Jan 16 17:05:27 2009
***************
*** 47,52 ****
--- 47,53 ----
  #include "nodes/nodeFuncs.h"
  #include "optimizer/planner.h"
  #include "pgstat.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
*************** init_fcache(Oid foid, FuncExprState *fca
*** 1047,1052 ****
--- 1048,1054 ----
      /* Set up the primary fmgr lookup information */
      fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
      fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
+     pgaceCallFunction(&fcache->func);

      /* If function returns set, prepare expected tuple descriptor */
      if (fcache->func.fn_retset && needDescForSets)
*************** ExecEvalArrayCoerceExpr(ArrayCoerceExprS
*** 4014,4019 ****
--- 4016,4023 ----

          /* Initialize additional info */
          astate->elemfunc.fn_expr = (Node *) acoerce;
+
+         pgaceCallFunction(&astate->elemfunc);
      }

      /*
diff -Nrpc base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c
*** base/src/backend/executor/execScan.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/execScan.c    Sat Jan  3 15:58:18 2009
***************
*** 20,25 ****
--- 20,26 ----

  #include "executor/executor.h"
  #include "miscadmin.h"
+ #include "security/pgace.h"
  #include "utils/memutils.h"


*************** TupleTableSlot *
*** 48,54 ****
  ExecScan(ScanState *node,
           ExecScanAccessMtd accessMtd)    /* function returning a tuple */
  {
!     ExprContext *econtext;
      List       *qual;
      ProjectionInfo *projInfo;
      ExprDoneCond isDone;
--- 49,55 ----
  ExecScan(ScanState *node,
           ExecScanAccessMtd accessMtd)    /* function returning a tuple */
  {
!     ExprContext *econtext = node->ps.ps_ExprContext;
      List       *qual;
      ProjectionInfo *projInfo;
      ExprDoneCond isDone;
*************** ExecScan(ScanState *node,
*** 65,71 ****
       * all the overhead and return the raw scan tuple.
       */
      if (!qual && !projInfo)
!         return (*accessMtd) (node);

      /*
       * Check to see if we're still projecting out tuples from a previous scan
--- 66,87 ----
       * all the overhead and return the raw scan tuple.
       */
      if (!qual && !projInfo)
!     {
!         while (true)
!         {
!             resultSlot = (*accessMtd) (node);
!
!             if (TupIsNull(resultSlot))
!                 break;
!
!             if (pgaceExecScan((Scan *)node->ps.plan,
!                               node->ss_currentRelation, resultSlot))
!                 break;
!
!             ResetExprContext(econtext);
!         }
!         return resultSlot;
!     }

      /*
       * Check to see if we're still projecting out tuples from a previous scan
*************** ExecScan(ScanState *node,
*** 87,93 ****
       * storage allocated in the previous tuple cycle.  Note this can't happen
       * until we're done projecting out tuples from a scan tuple.
       */
-     econtext = node->ps.ps_ExprContext;
      ResetExprContext(econtext);

      /*
--- 103,108 ----
*************** ExecScan(ScanState *node,
*** 127,134 ****
           * check for non-nil qual here to avoid a function call to ExecQual()
           * when the qual is nil ... saves only a few cycles, but they add up
           * ...
           */
!         if (!qual || ExecQual(qual, econtext, false))
          {
              /*
               * Found a satisfactory scan tuple.
--- 142,152 ----
           * check for non-nil qual here to avoid a function call to ExecQual()
           * when the qual is nil ... saves only a few cycles, but they add up
           * ...
+          * And security check for tuple level access controls at the last.
           */
!         if ((!qual || ExecQual(qual, econtext, false))
!             && pgaceExecScan((Scan *)node->ps.plan,
!                              node->ss_currentRelation, slot))
          {
              /*
               * Found a satisfactory scan tuple.
*************** tlist_matches_tupdesc(PlanState *ps, Lis
*** 197,202 ****
--- 215,222 ----
      int            numattrs = tupdesc->natts;
      int            attrno;
      bool        hasoid;
+     bool        hasrowacl;
+     bool        hasseclabel;
      ListCell   *tlist_item = list_head(tlist);

      /* Check the tlist attributes */
*************** tlist_matches_tupdesc(PlanState *ps, Lis
*** 240,251 ****
          return false;            /* tlist too long */

      /*
!      * If the plan context requires a particular hasoid setting, then that has
!      * to match, too.
       */
      if (ExecContextForcesOids(ps, &hasoid) &&
          hasoid != tupdesc->tdhasoid)
          return false;

      return true;
  }
--- 260,279 ----
          return false;            /* tlist too long */

      /*
!      * If the plan context requires a particular hasoid/hassecurity setting,
!      * then they have to match, too.
       */
      if (ExecContextForcesOids(ps, &hasoid) &&
          hasoid != tupdesc->tdhasoid)
          return false;

+     if (ExecContextForcesRowAcl(ps, &hasrowacl) &&
+         hasrowacl != tupdesc->tdhasrowacl)
+         return false;
+
+     if (ExecContextForcesSecLabel(ps, &hasseclabel) &&
+         hasseclabel != tupdesc->tdhasseclabel)
+         return false;
+
      return true;
  }
diff -Nrpc base/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c
*** base/src/backend/executor/execTuples.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/execTuples.c    Sat Jan  3 15:58:18 2009
***************
*** 99,106 ****
  #include "utils/typcache.h"


! static TupleDesc ExecTypeFromTLInternal(List *targetList,
!                        bool hasoid, bool skipjunk);


  /* ----------------------------------------------------------------
--- 99,106 ----
  #include "utils/typcache.h"


! static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid,
!                                         bool hasrowacl, bool hasseclabel, bool skipjunk);


  /* ----------------------------------------------------------------
*************** ExecInitNullTupleSlot(EState *estate, Tu
*** 948,956 ****
   * ----------------------------------------------------------------
   */
  TupleDesc
! ExecTypeFromTL(List *targetList, bool hasoid)
  {
!     return ExecTypeFromTLInternal(targetList, hasoid, false);
  }

  /* ----------------------------------------------------------------
--- 948,957 ----
   * ----------------------------------------------------------------
   */
  TupleDesc
! ExecTypeFromTL(List *targetList, bool hasoid, bool hasrowacl, bool hasseclabel)
  {
!     return ExecTypeFromTLInternal(targetList,
!                                   hasoid, hasrowacl, hasseclabel, false);
  }

  /* ----------------------------------------------------------------
*************** ExecTypeFromTL(List *targetList, bool ha
*** 960,972 ****
   * ----------------------------------------------------------------
   */
  TupleDesc
! ExecCleanTypeFromTL(List *targetList, bool hasoid)
  {
!     return ExecTypeFromTLInternal(targetList, hasoid, true);
  }

  static TupleDesc
! ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
  {
      TupleDesc    typeInfo;
      ListCell   *l;
--- 961,975 ----
   * ----------------------------------------------------------------
   */
  TupleDesc
! ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasrowacl, bool hasseclabel)
  {
!     return ExecTypeFromTLInternal(targetList,
!                                   hasoid, hasrowacl, hasseclabel, true);
  }

  static TupleDesc
! ExecTypeFromTLInternal(List *targetList,
!                        bool hasoid, bool hasrowacl, bool hasseclabel, bool skipjunk)
  {
      TupleDesc    typeInfo;
      ListCell   *l;
*************** ExecTypeFromTLInternal(List *targetList,
*** 978,983 ****
--- 981,988 ----
      else
          len = ExecTargetListLength(targetList);
      typeInfo = CreateTemplateTupleDesc(len, hasoid);
+     typeInfo->tdhasrowacl = hasrowacl;
+     typeInfo->tdhasseclabel = hasseclabel;

      foreach(l, targetList)
      {
diff -Nrpc base/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c
*** base/src/backend/executor/execUtils.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/execUtils.c    Sat Jan  3 15:58:18 2009
*************** void
*** 504,509 ****
--- 504,511 ----
  ExecAssignResultTypeFromTL(PlanState *planstate)
  {
      bool        hasoid;
+     bool        hassecacl;
+     bool        hasseclabel;
      TupleDesc    tupDesc;

      if (ExecContextForcesOids(planstate, &hasoid))
*************** ExecAssignResultTypeFromTL(PlanState *pl
*** 516,527 ****
          hasoid = false;
      }

      /*
       * ExecTypeFromTL needs the parse-time representation of the tlist, not a
       * list of ExprStates.    This is good because some plan nodes don't bother
       * to set up planstate->targetlist ...
       */
!     tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
      ExecAssignResultType(planstate, tupDesc);
  }

--- 518,535 ----
          hasoid = false;
      }

+     if (!ExecContextForcesRowAcl(planstate, &hassecacl))
+         hassecacl = false;
+     if (!ExecContextForcesSecLabel(planstate, &hasseclabel))
+         hasseclabel = false;
+
      /*
       * ExecTypeFromTL needs the parse-time representation of the tlist, not a
       * list of ExprStates.    This is good because some plan nodes don't bother
       * to set up planstate->targetlist ...
       */
!     tupDesc = ExecTypeFromTL(planstate->plan->targetlist,
!                              hasoid, hassecacl, hasseclabel);
      ExecAssignResultType(planstate, tupDesc);
  }

diff -Nrpc base/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c
*** base/src/backend/executor/functions.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/functions.c    Sat Jan  3 15:58:18 2009
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1134,1140 ****

          /* Set up junk filter if needed */
          if (junkFilter)
!             *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
      }
      else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
      {
--- 1134,1140 ----

          /* Set up junk filter if needed */
          if (junkFilter)
!             *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL);
      }
      else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
      {
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1166,1172 ****
                                                           COERCE_DONTCARE);
                  /* Set up junk filter if needed */
                  if (junkFilter)
!                     *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
                  return false;    /* NOT returning whole tuple */
              }
          }
--- 1166,1172 ----
                                                           COERCE_DONTCARE);
                  /* Set up junk filter if needed */
                  if (junkFilter)
!                     *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL);
                  return false;    /* NOT returning whole tuple */
              }
          }
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1179,1185 ****
               * what the caller expects will happen at runtime.
               */
              if (junkFilter)
!                 *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
              return true;
          }
          Assert(tupdesc);
--- 1179,1185 ----
               * what the caller expects will happen at runtime.
               */
              if (junkFilter)
!                 *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL);
              return true;
          }
          Assert(tupdesc);
diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c
*** base/src/backend/executor/nodeAgg.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/nodeAgg.c    Fri Jan 16 17:05:27 2009
***************
*** 79,84 ****
--- 79,85 ----
  #include "parser/parse_agg.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_oper.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
*************** ExecInitAgg(Agg *node, EState *estate, i
*** 1430,1435 ****
--- 1431,1438 ----
              aclcheck_error(aclresult, ACL_KIND_PROC,
                             get_func_name(aggref->aggfnoid));

+         pgaceCallAggFunction(aggTuple);
+
          peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
          peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;

*************** ExecInitAgg(Agg *node, EState *estate, i
*** 1493,1503 ****
--- 1496,1508 ----

          fmgr_info(transfn_oid, &peraggstate->transfn);
          peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+         pgaceCallFunction(&peraggstate->transfn);

          if (OidIsValid(finalfn_oid))
          {
              fmgr_info(finalfn_oid, &peraggstate->finalfn);
              peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+             pgaceCallFunction(&peraggstate->finalfn);
          }

          get_typlenbyval(aggref->aggtype,
diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c
*** base/src/backend/executor/nodeMergejoin.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/nodeMergejoin.c    Fri Jan 16 17:05:27 2009
***************
*** 98,103 ****
--- 98,104 ----
  #include "executor/execdefs.h"
  #include "executor/nodeMergejoin.h"
  #include "miscadmin.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
*************** MJExamineQuals(List *mergeclauses,
*** 218,223 ****
--- 219,225 ----

          /* Set up the fmgr lookup information */
          fmgr_info(cmpproc, &(clause->cmpfinfo));
+         pgaceCallFunction(&clause->cmpfinfo);

          /* Fill the additional comparison-strategy flags */
          if (opstrategy == BTLessStrategyNumber)
diff -Nrpc base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c
*** base/src/backend/executor/nodeSubplan.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/nodeSubplan.c    Sat Jan  3 15:58:18 2009
*************** ExecInitSubPlan(SubPlan *subplan, PlanSt
*** 869,875 ****
           * (hack alert!).  The righthand expressions will be evaluated in our
           * own innerecontext.
           */
!         tupDesc = ExecTypeFromTL(leftptlist, false);
          slot = ExecAllocTableSlot(tupTable);
          ExecSetSlotDescriptor(slot, tupDesc);
          sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
--- 869,875 ----
           * (hack alert!).  The righthand expressions will be evaluated in our
           * own innerecontext.
           */
!         tupDesc = ExecTypeFromTL(leftptlist, false, false, false);
          slot = ExecAllocTableSlot(tupTable);
          ExecSetSlotDescriptor(slot, tupDesc);
          sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
*************** ExecInitSubPlan(SubPlan *subplan, PlanSt
*** 877,883 ****
                                                     slot,
                                                     NULL);

!         tupDesc = ExecTypeFromTL(rightptlist, false);
          slot = ExecAllocTableSlot(tupTable);
          ExecSetSlotDescriptor(slot, tupDesc);
          sstate->projRight = ExecBuildProjectionInfo(righttlist,
--- 877,883 ----
                                                     slot,
                                                     NULL);

!         tupDesc = ExecTypeFromTL(rightptlist, false, false, false);
          slot = ExecAllocTableSlot(tupTable);
          ExecSetSlotDescriptor(slot, tupDesc);
          sstate->projRight = ExecBuildProjectionInfo(righttlist,
diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c
*** base/src/backend/executor/nodeWindowAgg.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/executor/nodeWindowAgg.c    Fri Jan 16 17:05:27 2009
***************
*** 43,48 ****
--- 43,49 ----
  #include "optimizer/clauses.h"
  #include "parser/parse_agg.h"
  #include "parser/parse_coerce.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/datum.h"
*************** ExecInitWindowAgg(WindowAgg *node, EStat
*** 1231,1236 ****
--- 1232,1238 ----
          fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
                        tmpcontext->ecxt_per_query_memory);
          perfuncstate->flinfo.fn_expr = (Node *) wfunc;
+         pgaceCallFunction(&perfuncstate->flinfo);
          get_typlenbyval(wfunc->wintype,
                          &perfuncstate->resulttypeLen,
                          &perfuncstate->resulttypeByVal);
*************** initialize_peragg(WindowAggState *winsta
*** 1457,1467 ****
--- 1459,1471 ----

      fmgr_info(transfn_oid, &peraggstate->transfn);
      peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+     pgaceCallFunction(&peraggstate->transfn);

      if (OidIsValid(finalfn_oid))
      {
          fmgr_info(finalfn_oid, &peraggstate->finalfn);
          peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         pgaceCallFunction(&peraggstate->finalfn);
      }

      get_typlenbyval(wfunc->wintype,
diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c
*** base/src/backend/executor/spi.c    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/executor/spi.c    Thu Jan 22 14:41:23 2009
*************** SPI_modifytuple(Relation rel, HeapTuple
*** 705,710 ****
--- 705,714 ----
          mtuple->t_tableOid = tuple->t_tableOid;
          if (rel->rd_att->tdhasoid)
              HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
+         if (HeapTupleHasRowAcl(mtuple))
+             HeapTupleSetRowAcl(mtuple, HeapTupleGetRowAcl(tuple));
+         if (HeapTupleHasSecLabel(mtuple))
+             HeapTupleSetSecLabel(mtuple, HeapTupleGetSecLabel(tuple));
      }
      else
      {
diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c
*** base/src/backend/libpq/be-fsstubs.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/libpq/be-fsstubs.c    Sat Jan  3 15:58:18 2009
***************
*** 45,50 ****
--- 45,51 ----
  #include "libpq/be-fsstubs.h"
  #include "libpq/libpq-fs.h"
  #include "miscadmin.h"
+ #include "security/pgace.h"
  #include "storage/fd.h"
  #include "storage/large_object.h"
  #include "utils/builtins.h"
*************** lo_read(int fd, char *buf, int len)
*** 156,161 ****
--- 157,164 ----
                  (errcode(ERRCODE_UNDEFINED_OBJECT),
                   errmsg("invalid large-object descriptor: %d", fd)));

+     pgaceLargeObjectRead(cookies[fd], len);
+
      status = inv_read(cookies[fd], buf, len);

      return status;
*************** lo_write(int fd, const char *buf, int le
*** 177,182 ****
--- 180,187 ----
                errmsg("large object descriptor %d was not opened for writing",
                       fd)));

+     pgaceLargeObjectWrite(cookies[fd], len);
+
      status = inv_write(cookies[fd], buf, len);

      return status;
*************** lo_import_internal(text *filename, Oid l
*** 377,382 ****
--- 382,392 ----
      oid = inv_create(lobjOid);

      /*
+      * check permission to import a file into this object
+      */
+     pgaceLargeObjectImport(oid, FileRawDescriptor(fd), fnamebuf);
+
+     /*
       * read in from the filesystem and write to the inversion object
       */
      lobj = inv_open(oid, INV_WRITE, fscxt);
*************** lo_export(PG_FUNCTION_ARGS)
*** 447,452 ****
--- 457,466 ----
                  (errcode_for_file_access(),
                   errmsg("could not create server file \"%s\": %m",
                          fnamebuf)));
+     /*
+      * check permission to export this object into a file
+      */
+     pgaceLargeObjectExport(lobjId, FileRawDescriptor(fd), fnamebuf);

      /*
       * read in from the inversion file and write to the filesystem
*************** lo_truncate(PG_FUNCTION_ARGS)
*** 482,487 ****
--- 496,503 ----
                  (errcode(ERRCODE_UNDEFINED_OBJECT),
                   errmsg("invalid large-object descriptor: %d", fd)));

+     pgaceLargeObjectTruncate(cookies[fd], len);
+
      inv_truncate(cookies[fd], len);

      PG_RETURN_INT32(0);
diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c
*** base/src/backend/nodes/copyfuncs.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/nodes/copyfuncs.c    Fri Jan 23 10:55:35 2009
***************
*** 24,29 ****
--- 24,30 ----

  #include "nodes/plannodes.h"
  #include "nodes/relation.h"
+ #include "nodes/security.h"
  #include "utils/datum.h"


*************** _copyPlannedStmt(PlannedStmt *from)
*** 90,95 ****
--- 91,97 ----
      COPY_NODE_FIELD(relationOids);
      COPY_NODE_FIELD(invalItems);
      COPY_SCALAR_FIELD(nParamExec);
+     COPY_NODE_FIELD(pgaceItem);

      return newnode;
  }
*************** CopyScanFields(Scan *from, Scan *newnode
*** 259,264 ****
--- 261,267 ----
      CopyPlanFields((Plan *) from, (Plan *) newnode);

      COPY_SCALAR_FIELD(scanrelid);
+     COPY_SCALAR_FIELD(pgaceTuplePerms);
  }

  /*
*************** _copyRangeTblEntry(RangeTblEntry *from)
*** 1742,1747 ****
--- 1745,1751 ----
      COPY_SCALAR_FIELD(checkAsUser);
      COPY_BITMAPSET_FIELD(selectedCols);
      COPY_BITMAPSET_FIELD(modifiedCols);
+     COPY_SCALAR_FIELD(pgaceTuplePerms);

      return newnode;
  }
*************** _copyColumnDef(ColumnDef *from)
*** 2083,2088 ****
--- 2087,2093 ----
      COPY_NODE_FIELD(raw_default);
      COPY_STRING_FIELD(cooked_default);
      COPY_NODE_FIELD(constraints);
+     COPY_NODE_FIELD(pgaceItem);

      return newnode;
  }
*************** _copyQuery(Query *from)
*** 2180,2185 ****
--- 2185,2191 ----
      COPY_NODE_FIELD(limitCount);
      COPY_NODE_FIELD(rowMarks);
      COPY_NODE_FIELD(setOperations);
+     COPY_NODE_FIELD(pgaceItem);

      return newnode;
  }
*************** _copyCreateStmt(CreateStmt *from)
*** 2431,2436 ****
--- 2437,2443 ----
      COPY_NODE_FIELD(options);
      COPY_SCALAR_FIELD(oncommit);
      COPY_STRING_FIELD(tablespacename);
+     COPY_NODE_FIELD(pgaceItem);

      return newnode;
  }
*************** _copyValue(Value *from)
*** 3438,3443 ****
--- 3445,3470 ----
      return newnode;
  }

+ /* ****************************************************************
+  *                    nodes/security.h copy functions
+  * ****************************************************************
+  */
+ static SelinuxEvalItem *
+ _copySelinuxEvalItem(SelinuxEvalItem *from)
+ {
+     SelinuxEvalItem *newnode = makeNode(SelinuxEvalItem);
+     int n;
+
+     COPY_SCALAR_FIELD(relid);
+     COPY_SCALAR_FIELD(inh);
+
+     COPY_SCALAR_FIELD(relperms);
+     COPY_SCALAR_FIELD(nattrs);
+     COPY_POINTER_FIELD(attperms, from->nattrs * sizeof(uint32));
+
+     return newnode;
+ }
+
  /*
   * copyObject
   *
*************** copyObject(void *from)
*** 4115,4120 ****
--- 4142,4150 ----
          case T_XmlSerialize:
              retval = _copyXmlSerialize(from);
              break;
+         case T_SelinuxEvalItem:
+             retval = _copySelinuxEvalItem(from);
+             break;

          default:
              elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c
*** base/src/backend/nodes/equalfuncs.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/nodes/equalfuncs.c    Fri Jan 23 10:55:35 2009
***************
*** 30,35 ****
--- 30,36 ----
  #include "postgres.h"

  #include "nodes/relation.h"
+ #include "nodes/security.h"
  #include "utils/datum.h"


*************** _equalQuery(Query *a, Query *b)
*** 871,876 ****
--- 872,878 ----
      COMPARE_NODE_FIELD(limitCount);
      COMPARE_NODE_FIELD(rowMarks);
      COMPARE_NODE_FIELD(setOperations);
+     COMPARE_NODE_FIELD(pgaceItem);

      return true;
  }
*************** _equalCreateStmt(CreateStmt *a, CreateSt
*** 1086,1091 ****
--- 1088,1094 ----
      COMPARE_NODE_FIELD(options);
      COMPARE_SCALAR_FIELD(oncommit);
      COMPARE_STRING_FIELD(tablespacename);
+     COMPARE_NODE_FIELD(pgaceItem);

      return true;
  }
*************** _equalColumnDef(ColumnDef *a, ColumnDef
*** 2062,2067 ****
--- 2065,2071 ----
      COMPARE_NODE_FIELD(raw_default);
      COMPARE_STRING_FIELD(cooked_default);
      COMPARE_NODE_FIELD(constraints);
+     COMPARE_NODE_FIELD(pgaceItem);

      return true;
  }
*************** _equalXmlSerialize(XmlSerialize *a, XmlS
*** 2229,2234 ****
--- 2233,2253 ----
  }

  /*
+  * Stuff from nodes/security.h
+  */
+ static bool
+ _equalSelinuxEvalItem(SelinuxEvalItem *a, SelinuxEvalItem *b)
+ {
+     COMPARE_SCALAR_FIELD(relid);
+     COMPARE_SCALAR_FIELD(inh);
+     COMPARE_SCALAR_FIELD(relperms);
+     COMPARE_SCALAR_FIELD(nattrs);
+     COMPARE_POINTER_FIELD(attperms, a->nattrs * sizeof(uint32));
+
+     return true;
+ }
+
+ /*
   * Stuff from pg_list.h
   */

*************** equal(void *a, void *b)
*** 2891,2896 ****
--- 2910,2918 ----
          case T_XmlSerialize:
              retval = _equalXmlSerialize(a, b);
              break;
+         case T_SelinuxEvalItem:
+             retval = _equalSelinuxEvalItem(a, b);
+             break;

          default:
              elog(ERROR, "unrecognized node type: %d",
diff -Nrpc base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c
*** base/src/backend/nodes/outfuncs.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/nodes/outfuncs.c    Fri Jan 23 10:55:35 2009
***************
*** 26,31 ****
--- 26,32 ----
  #include "lib/stringinfo.h"
  #include "nodes/plannodes.h"
  #include "nodes/relation.h"
+ #include "nodes/security.h"
  #include "utils/datum.h"


*************** _outPlannedStmt(StringInfo str, PlannedS
*** 255,260 ****
--- 256,262 ----
      WRITE_NODE_FIELD(relationOids);
      WRITE_NODE_FIELD(invalItems);
      WRITE_INT_FIELD(nParamExec);
+     WRITE_NODE_FIELD(pgaceItem);
  }

  /*
*************** _outScanInfo(StringInfo str, Scan *node)
*** 285,290 ****
--- 287,293 ----
      _outPlanInfo(str, (Plan *) node);

      WRITE_UINT_FIELD(scanrelid);
+     WRITE_UINT_FIELD(pgaceTuplePerms);
  }

  /*
*************** _outRelOptInfo(StringInfo str, RelOptInf
*** 1526,1531 ****
--- 1529,1535 ----
      WRITE_BOOL_FIELD(has_eclass_joins);
      WRITE_BITMAPSET_FIELD(index_outer_relids);
      WRITE_NODE_FIELD(index_inner_paths);
+     WRITE_UINT_FIELD(pgaceTuplePerms);
  }

  static void
*************** _outCreateStmt(StringInfo str, CreateStm
*** 1718,1723 ****
--- 1722,1728 ----
      WRITE_NODE_FIELD(options);
      WRITE_ENUM_FIELD(oncommit, OnCommitAction);
      WRITE_STRING_FIELD(tablespacename);
+     WRITE_NODE_FIELD(pgaceItem);
  }

  static void
*************** _outColumnDef(StringInfo str, ColumnDef
*** 1838,1843 ****
--- 1843,1849 ----
      WRITE_NODE_FIELD(raw_default);
      WRITE_STRING_FIELD(cooked_default);
      WRITE_NODE_FIELD(constraints);
+     WRITE_NODE_FIELD(pgaceItem);
  }

  static void
*************** _outQuery(StringInfo str, Query *node)
*** 1932,1937 ****
--- 1938,1944 ----
      WRITE_NODE_FIELD(limitCount);
      WRITE_NODE_FIELD(rowMarks);
      WRITE_NODE_FIELD(setOperations);
+     WRITE_NODE_FIELD(pgaceItem);
  }

  static void
*************** _outRangeTblEntry(StringInfo str, RangeT
*** 2060,2065 ****
--- 2067,2073 ----
      WRITE_OID_FIELD(checkAsUser);
      WRITE_BITMAPSET_FIELD(selectedCols);
      WRITE_BITMAPSET_FIELD(modifiedCols);
+     WRITE_UINT_FIELD(pgaceTuplePerms);
  }

  static void
*************** _outFkConstraint(StringInfo str, FkConst
*** 2332,2337 ****
--- 2340,2368 ----
      WRITE_BOOL_FIELD(skip_validation);
  }

+ /*****************************************************************************
+  *
+  *    Stuff from nodes/security.h
+  *
+  *****************************************************************************/
+ static void
+ _outSelinuxEvalItem(StringInfo str, SelinuxEvalItem *node)
+ {
+     int i;
+
+     WRITE_NODE_TYPE("SELINUXEVALITEM");
+
+     WRITE_OID_FIELD(relid);
+     WRITE_BOOL_FIELD(inh);
+
+     WRITE_UINT_FIELD(relperms);
+     WRITE_UINT_FIELD(nattrs);
+
+     appendStringInfo(str, " :attperms [");
+     for (i = 0; i < node->nattrs; i++)
+         appendStringInfo(str, " %u", node->attperms[i]);
+     appendStringInfo(str, " ]");
+ }

  /*
   * _outNode -
*************** _outNode(StringInfo str, void *obj)
*** 2776,2781 ****
--- 2807,2815 ----
              case T_XmlSerialize:
                  _outXmlSerialize(str, obj);
                  break;
+             case T_SelinuxEvalItem:
+                 _outSelinuxEvalItem(str, obj);
+                 break;

              default:

diff -Nrpc base/src/backend/nodes/readfuncs.c sepgsql/src/backend/nodes/readfuncs.c
*** base/src/backend/nodes/readfuncs.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/nodes/readfuncs.c    Fri Jan 23 10:55:35 2009
***************
*** 30,35 ****
--- 30,36 ----

  #include "nodes/parsenodes.h"
  #include "nodes/readfuncs.h"
+ #include "nodes/security.h"


  /*
*************** _readQuery(void)
*** 216,221 ****
--- 217,223 ----
      READ_NODE_FIELD(limitCount);
      READ_NODE_FIELD(rowMarks);
      READ_NODE_FIELD(setOperations);
+     READ_NODE_FIELD(pgaceItem);

      READ_DONE();
  }
*************** _readRangeTblEntry(void)
*** 1149,1158 ****
--- 1151,1199 ----
      READ_OID_FIELD(checkAsUser);
      READ_BITMAPSET_FIELD(selectedCols);
      READ_BITMAPSET_FIELD(modifiedCols);
+     READ_UINT_FIELD(pgaceTuplePerms);

      READ_DONE();
  }

+ /*
+  * Stuff from nodes/security.h
+  */
+ static SelinuxEvalItem *
+ _readSelinuxEvalItem(void)
+ {
+     int i;
+
+     READ_LOCALS(SelinuxEvalItem);
+
+     READ_OID_FIELD(relid);
+     READ_BOOL_FIELD(inh);
+
+     READ_UINT_FIELD(relperms);
+     READ_UINT_FIELD(nattrs);
+
+     /*
+      * TODO: This part should be moved to readArray() ?
+      */
+     local_node->attperms = palloc0(local_node->nattrs * sizeof(uint32));
+
+     token = pg_strtok(&length);    /* skip :attperms */
+     token = pg_strtok(&length);    /* read '[' */
+     if (token == NULL || strcmp(token, "[") != 0)
+         elog(ERROR, "expected \"[\" to start array, but got \"%s\"",
+              token ? (const char *) token : "[NULL]");
+     for (i = 0; i < local_node->nattrs; i++)
+     {
+         token = pg_strtok(&length);
+         local_node->attperms[i] = atoui(token);
+     }
+     token = pg_strtok(&length);    /* read ']' */
+     if (token == NULL || strcmp(token, "[") != 0)
+         elog(ERROR, "expected \"[\" to end array, but got \"%s\"",
+              token ? (const char *) token : "[NULL]");
+
+     READ_DONE();
+ }

  /*
   * parseNodeString
*************** parseNodeString(void)
*** 1274,1279 ****
--- 1315,1322 ----
          return_value = _readNotifyStmt();
      else if (MATCH("DECLARECURSOR", 13))
          return_value = _readDeclareCursorStmt();
+     else if (MATCH("SELINUXEVALITEM", 15))
+         return_value = _readSelinuxEvalItem();
      else
      {
          elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff -Nrpc base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c
*** base/src/backend/optimizer/plan/createplan.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/optimizer/plan/createplan.c    Sat Jan  3 15:58:18 2009
*************** create_scan_plan(PlannerInfo *root, Path
*** 302,307 ****
--- 302,313 ----
      }

      /*
+      * The guest of PGACE can refer plan->pgaceTuplePerms to apply
+      * tuple level access control in the pgaceExecScan() hook.
+      */
+     ((Scan *)plan)->pgaceTuplePerms = rel->pgaceTuplePerms;
+
+     /*
       * If there are any pseudoconstant clauses attached to this node, insert a
       * gating Result node that evaluates the pseudoconstants as one-time
       * quals.
diff -Nrpc base/src/backend/optimizer/plan/planner.c sepgsql/src/backend/optimizer/plan/planner.c
*** base/src/backend/optimizer/plan/planner.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/optimizer/plan/planner.c    Tue Jan 13 09:39:35 2009
*************** standard_planner(Query *parse, int curso
*** 230,235 ****
--- 230,236 ----
      result->relationOids = glob->relationOids;
      result->invalItems = glob->invalItems;
      result->nParamExec = list_length(glob->paramlist);
+     result->pgaceItem = parse->pgaceItem;

      return result;
  }
diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c
*** base/src/backend/optimizer/util/clauses.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/optimizer/util/clauses.c    Tue Jan 13 09:39:35 2009
***************
*** 38,43 ****
--- 38,44 ----
  #include "parser/parse_coerce.h"
  #include "parser/parse_func.h"
  #include "rewrite/rewriteManip.h"
+ #include "security/pgace.h"
  #include "tcop/tcopprot.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
*************** inline_function(Oid funcid, Oid result_t
*** 3548,3553 ****
--- 3549,3557 ----
      if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
          return NULL;

+     if (!pgaceAllowFunctionInlined(funcid, func_tuple))
+         return NULL;
+
      /*
       * Setup error traceback support for ereport().  This is so that we can
       * finger the function that bad information came from.
*************** inline_set_returning_function(PlannerInf
*** 4005,4010 ****
--- 4009,4015 ----
          funcform->prosecdef ||
          !funcform->proretset ||
          !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
+         !pgaceAllowFunctionInlined(fexpr->funcid, func_tuple) ||
          funcform->pronargs != list_length(fexpr->args))
      {
          ReleaseSysCache(func_tuple);
diff -Nrpc base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c
*** base/src/backend/optimizer/util/relnode.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/optimizer/util/relnode.c    Sat Jan  3 15:58:18 2009
*************** build_simple_rel(PlannerInfo *root, int
*** 91,96 ****
--- 91,97 ----
      rel->has_eclass_joins = false;
      rel->index_outer_relids = NULL;
      rel->index_inner_paths = NIL;
+     rel->pgaceTuplePerms = rte->pgaceTuplePerms;

      /* Check type of rtable entry */
      switch (rte->rtekind)
diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c
*** base/src/backend/parser/analyze.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/parser/analyze.c    Fri Jan 23 10:55:35 2009
***************
*** 25,30 ****
--- 25,31 ----
  #include "postgres.h"

  #include "access/sysattr.h"
+ #include "catalog/heap.h"
  #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
***************
*** 39,44 ****
--- 40,46 ----
  #include "parser/parse_target.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteManip.h"
+ #include "security/pgace.h"
  #include "utils/rel.h"


*************** transformInsertStmt(ParseState *pstate,
*** 651,657 ****
          tle = makeTargetEntry(expr,
                                attr_num,
                                col->name,
!                               false);
          qry->targetList = lappend(qry->targetList, tle);

          rte->modifiedCols = bms_add_member(rte->modifiedCols,
--- 653,659 ----
          tle = makeTargetEntry(expr,
                                attr_num,
                                col->name,
!                               attr_num < 0 ? true : false);
          qry->targetList = lappend(qry->targetList, tle);

          rte->modifiedCols = bms_add_member(rte->modifiedCols,
*************** transformInsertRow(ParseState *pstate, L
*** 766,771 ****
--- 768,814 ----
      return result;
  }

+ static void
+ transformSelectIntoSystemColumn(ParseState *pstate, Query *qry)
+ {
+     ListCell *l;
+     uint32 system_attrs = 0;
+     bool relhasoids
+         = interpretOidsOption(qry->intoClause->options);
+
+     foreach (l, qry->targetList) {
+         Form_pg_attribute attr;
+         TargetEntry *tle = lfirst(l);
+
+         if (tle->resjunk)
+             continue;
+
+         attr = SystemAttributeByName(tle->resname, relhasoids);
+         if (attr && SystemAttributeIsWritable(attr->attnum))
+         {
+             uint32 mask = (1<<(-attr->attnum));
+
+             /* duplication checks */
+             if (system_attrs & mask)
+                 continue;
+             system_attrs |= mask;
+
+             if (exprType((Node *) tle->expr) != attr->atttypid)
+             {
+                 tle->expr =
+                     (Expr *) coerce_to_target_type(pstate,
+                                                    (Node *) tle->expr,
+                                                    exprType((Node *) tle->expr),
+                                                    attr->atttypid,
+                                                    attr->atttypmod,
+                                                    COERCION_IMPLICIT,
+                                                    COERCE_IMPLICIT_CAST,
+                                                    -1);
+             }
+             tle->resjunk = true;
+         }
+     }
+ }

  /*
   * transformSelectStmt -
*************** transformSelectStmt(ParseState *pstate,
*** 869,874 ****
--- 912,918 ----
      if (stmt->intoClause)
      {
          qry->intoClause = stmt->intoClause;
+         transformSelectIntoSystemColumn(pstate, qry);
          if (stmt->intoClause->colNames)
              applyColumnNames(qry->targetList, stmt->intoClause->colNames);
      }
diff -Nrpc base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y
*** base/src/backend/parser/gram.y    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/parser/gram.y    Fri Jan 23 10:55:35 2009
***************
*** 57,62 ****
--- 57,63 ----
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/gramparse.h"
+ #include "security/pgace.h"
  #include "storage/lmgr.h"
  #include "utils/date.h"
  #include "utils/datetime.h"
*************** static TypeName *TableFuncTypeName(List
*** 402,407 ****
--- 403,410 ----
  %type <str>        OptTableSpace OptConsTableSpace OptTableSpaceOwner
  %type <list>    opt_check_option

+ %type <defelt> OptSecurityItem SecurityItem
+
  %type <target>    xml_attribute_el
  %type <list>    xml_attribute_list xml_attributes
  %type <node>    xml_root_version opt_xml_root_standalone
*************** alter_table_cmd:
*** 1796,1801 ****
--- 1799,1822 ----
                      n->def = (Node *)$2;
                      $$ = (Node *)n;
                  }
+             /* ALTER TABLE <relation> CONTEXT = '...' */
+             | SecurityItem
+                 {
+                     AlterTableCmd *n = makeNode(AlterTableCmd);
+                     n->subtype = AT_SetSecurityLabel;
+                     n->name = NULL;
+                     n->def = (Node *) $1;
+                     $$ = (Node *) n;
+                 }
+             /* ALTER TABLE <relation> ALTER [COLUMN] <colname> CONTEXT = '...' */
+             | ALTER opt_column ColId SecurityItem
+                 {
+                     AlterTableCmd *n = makeNode(AlterTableCmd);
+                     n->subtype = AT_SetSecurityLabel;
+                     n->name = $3;
+                     n->def = (Node *) $4;
+                     $$ = (Node *) n;
+                 }
          ;

  alter_column_default:
*************** opt_using:
*** 1997,2003 ****
   *****************************************************************************/

  CreateStmt:    CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
!             OptInherit OptWith OnCommitOption OptTableSpace
                  {
                      CreateStmt *n = makeNode(CreateStmt);
                      $4->istemp = $2;
--- 2018,2024 ----
   *****************************************************************************/

  CreateStmt:    CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
!             OptInherit OptWith OnCommitOption OptTableSpace OptSecurityItem
                  {
                      CreateStmt *n = makeNode(CreateStmt);
                      $4->istemp = $2;
*************** CreateStmt:    CREATE OptTemp TABLE qualifi
*** 2008,2017 ****
                      n->options = $9;
                      n->oncommit = $10;
                      n->tablespacename = $11;
                      $$ = (Node *)n;
                  }
          | CREATE OptTemp TABLE qualified_name OF qualified_name
!             '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace
                  {
                      /* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
                       * by our inheritance capabilities. Let's try it...
--- 2029,2039 ----
                      n->options = $9;
                      n->oncommit = $10;
                      n->tablespacename = $11;
+                     n->pgaceItem = (Node *) $12;
                      $$ = (Node *)n;
                  }
          | CREATE OptTemp TABLE qualified_name OF qualified_name
!             '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityItem
                  {
                      /* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
                       * by our inheritance capabilities. Let's try it...
*************** CreateStmt:    CREATE OptTemp TABLE qualifi
*** 2025,2030 ****
--- 2047,2053 ----
                      n->options = $10;
                      n->oncommit = $11;
                      n->tablespacename = $12;
+                     n->pgaceItem = (Node *) $13;
                      $$ = (Node *)n;
                  }
          ;
*************** TableElement:
*** 2067,2079 ****
              | TableConstraint                    { $$ = $1; }
          ;

! columnDef:    ColId Typename ColQualList
                  {
                      ColumnDef *n = makeNode(ColumnDef);
                      n->colname = $1;
                      n->typename = $2;
                      n->constraints = $3;
                      n->is_local = true;
                      $$ = (Node *)n;
                  }
          ;
--- 2090,2103 ----
              | TableConstraint                    { $$ = $1; }
          ;

! columnDef:    ColId Typename ColQualList OptSecurityItem
                  {
                      ColumnDef *n = makeNode(ColumnDef);
                      n->colname = $1;
                      n->typename = $2;
                      n->constraints = $3;
                      n->is_local = true;
+                     n->pgaceItem = (Node *) $4;
                      $$ = (Node *)n;
                  }
          ;
*************** common_func_opt_item:
*** 4834,4839 ****
--- 4858,4867 ----
                      /* we abuse the normal content of a DefElem here */
                      $$ = makeDefElem("set", (Node *)$1);
                  }
+             | SecurityItem
+                 {
+                     $$ = $1;
+                 }
          ;

  createfunc_opt_item:
*************** createdb_opt_item:
*** 5997,6002 ****
--- 6025,6034 ----
                  {
                      $$ = makeDefElem("owner", NULL);
                  }
+             | SecurityItem
+                 {
+                     $$ = $1;
+                 }
          ;

  /*
*************** alterdb_opt_item:
*** 6053,6058 ****
--- 6085,6094 ----
                  {
                      $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
                  }
+             | SecurityItem
+                 {
+                     $$ = $1;
+                 }
          ;


*************** target_el:    a_expr AS ColLabel
*** 9840,9845 ****
--- 9876,9903 ----
                  }
          ;

+ /*****************************************************************************
+  *
+  * PGACE Security Items
+  *
+  *****************************************************************************/
+
+ OptSecurityItem:
+             SecurityItem                { $$ = $1; }
+             | /* EMPTY */                { $$ = NULL; }
+             ;
+
+ SecurityItem:
+             IDENT '=' Sconst
+                 {
+                     DefElem *node = makeDefElem($1, (Node *) makeString($3));
+
+                     if (!pgaceIsGramSecurityItem(node))
+                         yyerror("syntax error");
+
+                     $$ = node;
+                 }
+             ;

  /*****************************************************************************
   *
diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c
*** base/src/backend/parser/parse_target.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/parser/parse_target.c    Fri Jan 23 10:55:35 2009
***************
*** 14,19 ****
--- 14,20 ----
   */
  #include "postgres.h"

+ #include "catalog/heap.h"
  #include "catalog/pg_type.h"
  #include "commands/dbcommands.h"
  #include "funcapi.h"
***************
*** 27,32 ****
--- 28,34 ----
  #include "parser/parse_relation.h"
  #include "parser/parse_target.h"
  #include "parser/parse_type.h"
+ #include "security/pgace.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/typcache.h"
*************** transformAssignedExpr(ParseState *pstate
*** 361,376 ****
      Oid            attrtype;        /* type of target column */
      int32        attrtypmod;
      Relation    rd = pstate->p_target_relation;

      Assert(rd != NULL);
!     if (attrno <= 0)
!         ereport(ERROR,
!                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                  errmsg("cannot assign to system column \"%s\"",
!                         colname),
!                  parser_errposition(pstate, location)));
!     attrtype = attnumTypeId(rd, attrno);
!     attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;

      /*
       * If the expression is a DEFAULT placeholder, insert the attribute's
--- 363,395 ----
      Oid            attrtype;        /* type of target column */
      int32        attrtypmod;
      Relation    rd = pstate->p_target_relation;
+     bool        relhasoids = RelationGetForm(rd)->relhasoids;

      Assert(rd != NULL);
!     if (attrno > 0)
!     {
!         attrtype = attnumTypeId(rd, attrno);
!         attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
!     }
!     else
!     {
!         Form_pg_attribute attr
!             = SystemAttributeDefinition(attrno, relhasoids);
!         if (attr && SystemAttributeIsWritable(attrno))
!         {
!             attrtype = attr->atttypid;
!             attrtypmod = attr->atttypmod;
!         }
!         else
!         {
!             ereport(ERROR,
!                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                      errmsg("cannot assign to system column \"%s\"",
!                             colname),
!                      parser_errposition(pstate, location)));
!             return NULL;    /* compiler kindness */
!         }
!     }

      /*
       * If the expression is a DEFAULT placeholder, insert the attribute's
*************** updateTargetListEntry(ParseState *pstate
*** 515,520 ****
--- 534,542 ----
       */
      tle->resno = (AttrNumber) attrno;
      tle->resname = colname;
+
+     if (SystemAttributeIsWritable(attrno))
+         tle->resjunk = true;
  }


*************** checkInsertTargets(ParseState *pstate, L
*** 789,794 ****
--- 811,817 ----
          Bitmapset  *wholecols = NULL;
          Bitmapset  *partialcols = NULL;
          ListCell   *tl;
+         uint32        system_attrs = 0;

          foreach(tl, cols)
          {
*************** checkInsertTargets(ParseState *pstate, L
*** 797,810 ****
              int            attrno;

              /* Lookup column name, ereport on failure */
!             attrno = attnameAttNum(pstate->p_target_relation, name, false);
              if (attrno == InvalidAttrNumber)
                  ereport(ERROR,
                          (errcode(ERRCODE_UNDEFINED_COLUMN),
                      errmsg("column \"%s\" of relation \"%s\" does not exist",
                             name,
                           RelationGetRelationName(pstate->p_target_relation)),
                           parser_errposition(pstate, col->location)));

              /*
               * Check for duplicates, but only of whole columns --- we allow
--- 820,856 ----
              int            attrno;

              /* Lookup column name, ereport on failure */
!             attrno = attnameAttNum(pstate->p_target_relation, name, true);
              if (attrno == InvalidAttrNumber)
+             {
                  ereport(ERROR,
                          (errcode(ERRCODE_UNDEFINED_COLUMN),
                      errmsg("column \"%s\" of relation \"%s\" does not exist",
                             name,
                           RelationGetRelationName(pstate->p_target_relation)),
                           parser_errposition(pstate, col->location)));
+             }
+             else if (attrno < 0)
+             {
+                 if (SystemAttributeIsWritable(attrno))
+                 {
+                     uint32    mask = (1<<(-attrno));
+
+                     if ((system_attrs & mask) != 0)
+                         ereport(ERROR,
+                                 (errcode(ERRCODE_DUPLICATE_COLUMN),
+                                  errmsg("column \"%s\" specified more than once", name),
+                                  parser_errposition(pstate, col->location)));
+                     system_attrs |= mask;
+                     *attrnos = lappend_int(*attrnos, attrno);
+                     continue;
+                 }
+                 ereport(ERROR,
+                         (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+                          errmsg("column \"%s\" of relation \"%s\" is system column",
+                                 name, RelationGetRelationName(pstate->p_target_relation)),
+                                                  parser_errposition(pstate, col->location)));
+             }

              /*
               * Check for duplicates, but only of whole columns --- we allow
diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c
*** base/src/backend/postmaster/postmaster.c    Mon Jan  5 17:36:07 2009
--- sepgsql/src/backend/postmaster/postmaster.c    Mon Jan  5 17:41:04 2009
***************
*** 108,113 ****
--- 108,114 ----
  #include "postmaster/pgarch.h"
  #include "postmaster/postmaster.h"
  #include "postmaster/syslogger.h"
+ #include "security/pgace.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
  #include "storage/pg_shmem.h"
*************** static pid_t StartupPID = 0,
*** 215,221 ****
              AutoVacPID = 0,
              PgArchPID = 0,
              PgStatPID = 0,
!             SysLoggerPID = 0;

  /* Startup/shutdown state */
  #define            NoShutdown        0
--- 216,223 ----
              AutoVacPID = 0,
              PgArchPID = 0,
              PgStatPID = 0,
!             SysLoggerPID = 0,
!             pgaceWorkerPID = 0;

  /* Startup/shutdown state */
  #define            NoShutdown        0
*************** ServerLoop(void)
*** 1330,1335 ****
--- 1332,1341 ----
          if (PgStatPID == 0 && pmState == PM_RUN)
              PgStatPID = pgstat_start();

+         /* If we have lost the pgace worker (if needed), try to start a new one */
+         if (pgaceWorkerPID == 0 && pmState == PM_RUN)
+             pgaceWorkerPID = pgaceStartupWorkerProcess();
+
          /*
           * Touch the socket and lock file every 58 minutes, to ensure that
           * they are not removed by overzealous /tmp-cleaning tasks.  We assume
*************** SIGHUP_handler(SIGNAL_ARGS)
*** 1933,1938 ****
--- 1939,1946 ----
              signal_child(SysLoggerPID, SIGHUP);
          if (PgStatPID != 0)
              signal_child(PgStatPID, SIGHUP);
+         if (pgaceWorkerPID != 0)
+             signal_child(pgaceWorkerPID, SIGHUP);

          /* Reload authentication config files too */
          if (!load_hba())
*************** pmdie(SIGNAL_ARGS)
*** 1992,1997 ****
--- 2000,2008 ----
                  /* and the walwriter too */
                  if (WalWriterPID != 0)
                      signal_child(WalWriterPID, SIGTERM);
+                 /* and the pgace worker too */
+                 if (pgaceWorkerPID != 0)
+                     signal_child(pgaceWorkerPID, SIGTERM);
                  pmState = PM_WAIT_BACKUP;
              }

*************** pmdie(SIGNAL_ARGS)
*** 2031,2036 ****
--- 2042,2050 ----
                  /* and the walwriter too */
                  if (WalWriterPID != 0)
                      signal_child(WalWriterPID, SIGTERM);
+                 /* and the pgaceWorker too */
+                 if (pgaceWorkerPID != 0)
+                     signal_child(pgaceWorkerPID, SIGTERM);
                  pmState = PM_WAIT_BACKENDS;
              }

*************** pmdie(SIGNAL_ARGS)
*** 2064,2069 ****
--- 2078,2085 ----
                  signal_child(PgArchPID, SIGQUIT);
              if (PgStatPID != 0)
                  signal_child(PgStatPID, SIGQUIT);
+             if (pgaceWorkerPID != 0)
+                 signal_child(pgaceWorkerPID, SIGQUIT);
              ExitPostmaster(0);
              break;
      }
*************** reaper(SIGNAL_ARGS)
*** 2312,2317 ****
--- 2328,2343 ----
              continue;
          }

+         /* Was it the PGACE worker process? */
+         if (pid == pgaceWorkerPID)
+         {
+             pgaceWorkerPID = 0;
+             if (!EXIT_STATUS_0(exitstatus))
+                 LogChildExit(LOG, _("PGACE worker process"),
+                              pid, exitstatus);
+             continue;
+         }
+
          /*
           * Else do standard backend child cleanup.
           */
*************** HandleChildCrash(int pid, int exitstatus
*** 2479,2484 ****
--- 2505,2522 ----
          signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
      }

+     /* Take care of the pgace worker too */
+     if (pid == pgaceWorkerPID)
+         pgaceWorkerPID = 0;
+     else if (pgaceWorkerPID != 0 && !FatalError)
+     {
+         ereport(DEBUG2,
+                 (errmsg_internal("sending %s to process %d",
+                                  (SendStop ? "SIGSTOP" : "SIGQUIT"),
+                                  (int) pgaceWorkerPID)));
+         signal_child(pgaceWorkerPID, (SendStop ? SIGSTOP : SIGQUIT));
+     }
+
      /*
       * Force a power-cycle of the pgarch process too.  (This isn't absolutely
       * necessary, but it seems like a good idea for robustness, and it
*************** PostmasterStateMachine(void)
*** 2609,2615 ****
              StartupPID == 0 &&
              (BgWriterPID == 0 || !FatalError) &&
              WalWriterPID == 0 &&
!             AutoVacPID == 0)
          {
              if (FatalError)
              {
--- 2647,2654 ----
              StartupPID == 0 &&
              (BgWriterPID == 0 || !FatalError) &&
              WalWriterPID == 0 &&
!             AutoVacPID == 0 &&
!             pgaceWorkerPID == 0)
          {
              if (FatalError)
              {
diff -Nrpc base/src/backend/rewrite/rewriteHandler.c sepgsql/src/backend/rewrite/rewriteHandler.c
*** base/src/backend/rewrite/rewriteHandler.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/rewrite/rewriteHandler.c    Fri Jan 23 10:55:35 2009
***************
*** 23,28 ****
--- 23,29 ----
  #include "rewrite/rewriteDefine.h"
  #include "rewrite/rewriteHandler.h"
  #include "rewrite/rewriteManip.h"
+ #include "security/pgace.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "commands/trigger.h"
*************** QueryRewrite(Query *parsetree)
*** 1972,1976 ****
      if (!foundOriginalQuery && lastInstead != NULL)
          lastInstead->canSetTag = true;

!     return results;
  }
--- 1973,1977 ----
      if (!foundOriginalQuery && lastInstead != NULL)
          lastInstead->canSetTag = true;

!     return pgacePostQueryRewrite(results);
  }
diff -Nrpc base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile
*** base/src/backend/security/Makefile    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/Makefile    Wed Jan 14 15:02:53 2009
***************
*** 0 ****
--- 1,23 ----
+ #
+ # src/backend/security/Makefile
+ #   Makefile for Security Purpose Extensions
+ #
+ # Copyright (c) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ #
+ subdir = src/backend/security
+ top_builddir = ../../..
+ include $(top_builddir)/src/Makefile.global
+
+ # common facilities for enhanced security features
+ OBJS := pgaceCommon.o pgaceHooks.o
+
+ # DAC feature : Row-level Database ACLs
+ OBJS += rowacl/rowacl.o
+
+ # MAC feature : Security-Enhanced PostgreSQL
+ ifeq ($(enable_selinux), yes)
+ OBJS += sepgsql/avc.o sepgsql/core.o sepgsql/hooks.o    \
+     sepgsql/permissions.o sepgsql/proxy.o
+ endif
+
+ include $(top_builddir)/src/backend/common.mk
diff -Nrpc base/src/backend/security/pgaceCommon.c sepgsql/src/backend/security/pgaceCommon.c
*** base/src/backend/security/pgaceCommon.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/pgaceCommon.c    Mon Dec 29 18:34:29 2008
***************
*** 0 ****
--- 1,729 ----
+
+ /*
+  * src/backend/security/pgaceCommon.c
+  *      common framework of security modules
+  *
+  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  */
+ #include "postgres.h"
+
+ #include "access/genam.h"
+ #include "access/hash.h"
+ #include "access/heapam.h"
+ #include "access/xact.h"
+ #include "catalog/catalog.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_attribute.h"
+ #include "catalog/pg_largeobject.h"
+ #include "catalog/pg_security.h"
+ #include "catalog/pg_type.h"
+ #include "executor/executor.h"
+ #include "libpq/be-fsstubs.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "nodes/parsenodes.h"
+ #include "parser/parse_expr.h"
+ #include "security/pgace.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ #include <unistd.h>
+ #include <sys/file.h>
+
+ /*****************************************************************************
+  *     GUC Parameter Support
+  *****************************************************************************/
+
+ int pgace_feature;
+
+ /*****************************************************************************
+  *     Extended SQL statements support
+  *****************************************************************************/
+
+ /*
+  * PGACE enables to create a new table labed as explicitly specified security
+  * attribute. It is implemented as an extension of SQL statement like:
+  *   CREATE TABLE memo (
+  *       id   integer primary key,
+  *       msg  TEXT
+  *   ) CONTEXT = 'system_u:object_r:sepgsql_secret_table_t';
+  *
+  * The specified security attribute is chained as a list of DefElem object,
+  * at CreateStmt->pgaceItem for a table, ColumnDef->pgaceItem for a column.
+  *
+  * These items are generated at pgaceGramSecurityItem() hook invoked from
+  * parser/gram.y. Then, pgaceRelationAttrList() pick them up and re-organize
+  * as a list, to pass it as an argument of heap_create_with_catalog().
+  *
+  * When the list is not NIL, it means user specifies a security attribute
+  * explicitly for a newly created table or column.
+  * pgaceGramCreateRelation() and pgaceGramCreateAttribute() are invoked
+  * just before inserting a new tuple into system catalog, and PGACE
+  * framework invokes pgaceGramCreateRelation() and/or pgaceGramCreateAttribute()
+  * hooks to give a chance the gurst to attach proper security attributes.
+  */
+
+ List *
+ pgaceRelationAttrList(CreateStmt *stmt)
+ {
+     List       *result = NIL;
+     ListCell   *l;
+     DefElem    *defel, *newel;
+
+     if (stmt->pgaceItem)
+     {
+         defel = (DefElem *) stmt->pgaceItem;
+
+         Assert(IsA(defel, DefElem));
+
+         if (!pgaceIsGramSecurityItem(defel))
+             elog(ERROR, "node is not a pgace security item");
+         newel = makeDefElem(NULL, (Node *) copyObject(defel));
+         result = lappend(result, newel);
+     }
+
+     foreach(l, stmt->tableElts)
+     {
+         ColumnDef  *cdef = (ColumnDef *) lfirst(l);
+
+         defel = (DefElem *) cdef->pgaceItem;
+
+         if (defel)
+         {
+             Assert(IsA(defel, DefElem));
+
+             if (!pgaceIsGramSecurityItem(defel))
+                 elog(ERROR, "node is not a pgace security item");
+             newel = makeDefElem(pstrdup(cdef->colname),
+                                 (Node *) copyObject(defel));
+             result = lappend(result, newel);
+         }
+     }
+     return result;
+ }
+
+ void
+ pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgaceAttrList)
+ {
+     ListCell   *l;
+
+     foreach(l, pgaceAttrList)
+     {
+         DefElem    *defel = (DefElem *) lfirst(l);
+
+         if (!defel->defname)
+         {
+             Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg));
+             pgaceGramCreateRelation(rel, tuple, (DefElem *) defel->arg);
+             break;
+         }
+     }
+ }
+
+ void
+ pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple,
+                            List *pgaceAttrList)
+ {
+     Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple);
+     ListCell   *l;
+
+     foreach(l, pgaceAttrList)
+     {
+         DefElem    *defel = lfirst(l);
+
+         if (!defel->defname)
+             continue;            /* for table */
+         if (strcmp(defel->defname, NameStr(attr->attname)) == 0)
+         {
+             Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg));
+             pgaceGramCreateAttribute(rel, tuple, (DefElem *) defel->arg);
+             break;
+         }
+     }
+ }
+
+ /*
+  * pgaceAlterRelationCommon()
+  *
+  * This function is invoked when a user requires to change security attribute
+  * of table/column with "ALTER TABLE" statement.
+  *
+  * When a user attempt to relabel a table, PGACE invokes alterRelationCommon()
+  * and it gives the guest module a chance to set a new security attribute of
+  * specified table.
+  * When a user attempt to relabel a column, PGACE invokes alterAttributeCommon()
+  * and it gives the guest module a chance to set a new security attribute of
+  * specified column.
+  */
+
+ static void
+ alterRelationCommon(Relation rel, DefElem *defel)
+ {
+     Relation    pg_class;
+     HeapTuple    tuple;
+
+     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+     tuple = SearchSysCacheCopy(RELOID,
+                                ObjectIdGetDatum(RelationGetRelid(rel)),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "cache lookup failed for relation '%s'",
+              RelationGetRelationName(rel));
+     pgaceGramAlterRelation(rel, tuple, defel);
+
+     simple_heap_update(pg_class, &tuple->t_self, tuple);
+     CatalogUpdateIndexes(pg_class, tuple);
+
+     heap_freetuple(tuple);
+     heap_close(pg_class, RowExclusiveLock);
+ }
+
+ static void
+ alterAttributeCommon(Relation rel, char *colName, DefElem *defel)
+ {
+     Relation    pg_attr;
+     HeapTuple    tuple;
+
+     pg_attr = heap_open(AttributeRelationId, RowExclusiveLock);
+
+     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "cache lookup failed for attribute '%s' of relation '%s'",
+              colName, RelationGetRelationName(rel));
+     pgaceGramAlterAttribute(rel, tuple, defel);
+
+     simple_heap_update(pg_attr, &tuple->t_self, tuple);
+     CatalogUpdateIndexes(pg_attr, tuple);
+
+     heap_freetuple(tuple);
+     heap_close(pg_attr, RowExclusiveLock);
+ }
+
+ void
+ pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd)
+ {
+     DefElem    *defel = (DefElem *) cmd->def;
+
+     Assert(IsA(defel, DefElem));
+
+     if (!pgaceIsGramSecurityItem(defel))
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("PGACE: unsupported security item")));
+
+     if (!cmd->name)
+     {
+         alterRelationCommon(rel, defel);
+     }
+     else
+     {
+         alterAttributeCommon(rel, cmd->name, defel);
+     }
+ }
+
+ /*****************************************************************************
+  *    security attribute management
+  *****************************************************************************/
+
+ /*
+  * The following functions enables to manage security attribute of each tuple
+  * (including ones within system catalog).
+  *
+  * Security attribute has these features:
+  * 1. It is imported/exported with text representation, like
+  *    'system_u:object_r:sepgsql_table_t:s0'
+  * 2. In generally, many tuples share a same security attribute.
+  *    (They are grouped by security attribute in other word.)
+  * 3. A object can have one security attribute at most.
+  *    (It can have a state of unlabeled.)
+  *
+  * PGACE utilizes a newly added system catalog of pg_security to store text
+  * representation of security attribute efficiently. Any tuple has a object id
+  * of a tuple within pg_security system catalog, we call it as a security id.
+  *
+  * Users can show security attribute as if it stored text data, but any tuple
+  * has a security id which has a length of sizeof(Oid), without text data.
+  * It is translated each other when it is exported/imported.
+  *
+  * pgaceSidToSecurityLabel() returns a text representation for a given security,
+  * id, and pgaceSecurityLabelToSid() returns a security id for a give text
+  * representation. (If a given text representation was not found on pg_security
+  * system catalog, PGACE inserts a new entry automatically.)
+  *
+  * In the very early phase (invoked by initdb), pg_security system catalos is
+  * not available yet. The earlySecurityLabelToSid() and earlySidToSecurityLabel()
+  * is used to hold relationships between security id and text representation.
+  * These relationships are stored at the end of bootstraping mode by
+  * pgacePostBootstrapingMode(). It write any cached relationships into pg_security
+  * system catalog.
+  */
+
+ typedef struct earlySeclabel
+ {
+     struct earlySeclabel *next;
+     Oid            sid;
+     char        label[1];
+ } earlySeclabel;
+
+ static earlySeclabel *earlySeclabelList = NULL;
+
+ static Oid
+ earlySecurityLabelToSid(char *label)
+ {
+     earlySeclabel *es;
+     Oid            minsid = SecurityRelationId;
+
+     for (es = earlySeclabelList; es != NULL; es = es->next)
+     {
+         if (!strcmp(label, es->label))
+             return es->sid;
+         if (es->sid < minsid)
+             minsid = es->sid;
+     }
+     /*
+      * not found
+      */
+     es = malloc(sizeof(earlySeclabel) + strlen(label));
+     es->next = earlySeclabelList;
+     es->sid = minsid - 1;
+     strcpy(es->label, label);
+     earlySeclabelList = es;
+
+     return es->sid;
+ }
+
+ static char *
+ earlySidToSecurityLabel(Oid sid)
+ {
+     earlySeclabel *es;
+
+     for (es = earlySeclabelList; es != NULL; es = es->next)
+     {
+         if (es->sid == sid)
+             return pstrdup(es->label);
+     }
+
+     return NULL;    /* not found */
+ }
+
+ void
+ pgacePostBootstrapingMode(void)
+ {
+     Relation    rel;
+     CatalogIndexState ind;
+     HeapTuple    tuple;
+     earlySeclabel *es, *_es;
+     Oid            meta_sid;
+     Datum        value;
+     bool        isnull;
+
+     if (!earlySeclabelList)
+         return;
+
+     StartTransactionCommand();
+
+     meta_sid = earlySecurityLabelToSid(pgaceSecurityLabelOfLabel());
+
+     rel = heap_open(SecurityRelationId, RowExclusiveLock);
+     ind = CatalogOpenIndexes(rel);
+
+     for (es = earlySeclabelList; es != NULL; es = _es)
+     {
+         _es = es->next;
+
+         value = DirectFunctionCall1(textin, CStringGetDatum(es->label));
+         isnull = false;
+         tuple = heap_form_tuple(RelationGetDescr(rel), &value, &isnull);
+
+         HeapTupleSetOid(tuple, es->sid);
+         if (HeapTupleHasSecLabel(tuple))
+             HeapTupleSetSecLabel(tuple, meta_sid);
+
+         simple_heap_insert(rel, tuple);
+         CatalogIndexInsert(ind, tuple);
+
+         heap_freetuple(tuple);
+
+         free(es);
+     }
+     CatalogCloseIndexes(ind);
+     heap_close(rel, RowExclusiveLock);
+
+     CommitTransactionCommand();
+ }
+
+ /*
+  * pgaceLookupSecurityId()
+  *
+  * The PGACE guest subsystem can use this interface to get a security id
+  * for a given text representation.
+  */
+ Oid
+ pgaceLookupSecurityId(char *raw_label)
+ {
+     Oid            labelOid, labelSid;
+     HeapTuple    tuple;
+
+     if (IsBootstrapProcessingMode())
+         return earlySecurityLabelToSid(raw_label);
+
+     /*
+      * lookup syscache at first
+      */
+     tuple = SearchSysCache(SECURITYLABEL,
+                            CStringGetTextDatum(raw_label),
+                            0, 0, 0);
+     if (HeapTupleIsValid(tuple))
+     {
+         labelOid = HeapTupleGetOid(tuple);
+         ReleaseSysCache(tuple);
+     }
+     else
+     {
+         /*
+          * not found, insert a new one into pg_security
+          */
+         Relation    rel;
+         CatalogIndexState ind;
+         char       *slabel;
+         Datum        labelTxt;
+         bool        isnull;
+
+         rel = heap_open(SecurityRelationId, RowExclusiveLock);
+
+         slabel = pgaceSecurityLabelOfLabel();
+
+         if (!slabel)
+         {
+             labelSid = InvalidOid;
+             labelOid = GetNewOid(rel);
+         }
+         else if (!strcmp(raw_label, slabel))
+         {
+             labelOid = labelSid = GetNewOid(rel);
+         }
+         else
+         {
+             labelSid = pgaceLookupSecurityId(slabel);
+             labelOid = GetNewOid(rel);
+         }
+
+         ind = CatalogOpenIndexes(rel);
+
+         labelTxt = CStringGetTextDatum(raw_label);
+         isnull = false;
+         tuple = heap_form_tuple(RelationGetDescr(rel),
+                                 &labelTxt, &isnull);
+         if (HeapTupleHasSecLabel(tuple))
+             HeapTupleSetSecLabel(tuple, labelSid);
+         HeapTupleSetOid(tuple, labelOid);
+
+         simple_heap_insert(rel, tuple);
+         CatalogIndexInsert(ind, tuple);
+
+         /*
+          * NOTE:
+          * We also have to insert a cache entry of new tuple of
+          * pg_security for temporary usage.
+          * If user tries to apply same security attribute twice
+          * or more within same command id, PGACE cannot decide
+          * whether it should be inserted, or not, because it
+          * cannot scan the prior one with SnapshotNow.
+          *
+          * A cache entry inserted will be invalidated on the
+          * next CommandIdIncrement().
+          * The purpose of InsertSysCache() here is to prevent
+          * duplicate insertion
+          */
+         InsertSysCache(RelationGetRelid(rel), tuple);
+
+         CatalogCloseIndexes(ind);
+         heap_close(rel, RowExclusiveLock);
+     }
+
+     return labelOid;
+ }
+
+ Oid
+ pgaceSecurityLabelToSid(char *label)
+ {
+     char *raw_label = pgaceTranslateSecurityLabelIn(label);
+
+     if (!pgaceCheckValidSecurityLabel(raw_label))
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("PGACE: invalid security label: %s", raw_label)));
+
+     return pgaceLookupSecurityId(raw_label);
+ }
+
+ /*
+  * pgaceLookupSecurityLabel()
+  *
+  * The PGACE guest module can use this interface to get a text representation
+  * in raw-format, without cosmetic translation.
+  */
+ char *
+ pgaceLookupSecurityLabel(Oid sid)
+ {
+     HeapTuple    tuple;
+     Datum        labelTxt;
+     char       *label;
+     bool        isnull;
+
+     if (!OidIsValid(sid))
+         return NULL;
+
+     if (IsBootstrapProcessingMode())
+         return earlySidToSecurityLabel(sid);
+
+     tuple = SearchSysCache(SECURITYOID,
+                            ObjectIdGetDatum(sid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         return NULL;
+
+     labelTxt = SysCacheGetAttr(SECURITYOID,
+                                tuple, Anum_pg_security_seclabel, &isnull);
+     Assert(!isnull);
+     label = TextDatumGetCString(labelTxt);
+     ReleaseSysCache(tuple);
+
+     return label;
+ }
+
+ char *
+ pgaceSidToSecurityLabel(Oid sid)
+ {
+     char *label;
+
+     label = pgaceLookupSecurityLabel(sid);
+     if (!label || !pgaceCheckValidSecurityLabel(label))
+         label = pgaceUnlabeledSecurityLabel();
+
+     if (!label)
+         return pstrdup("");
+
+     return pgaceTranslateSecurityLabelOut(label);
+ }
+
+ Datum
+ pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple)
+ {
+     Oid sid = HeapTupleGetSecLabel(tuple);
+
+     return CStringGetTextDatum(pgaceSidToSecurityLabel(sid));
+ }
+
+ /*****************************************************************************
+  *     Set/Get security attribute of Large Object
+  *****************************************************************************/
+
+ /*
+  * lo_get_security()
+  *
+  * This function returns a security attribute of large object
+  * in TEXT representation.
+  *
+  * It assumes the first page means the whole of large object.
+  * The guest of PGACE should pay effort to keep its consistency.
+  */
+ Datum
+ lo_get_security(PG_FUNCTION_ARGS)
+ {
+     Oid            loid = PG_GETARG_OID(0);
+     Relation    rel;
+     ScanKeyData skey;
+     SysScanDesc scan;
+     HeapTuple    tuple;
+     Oid            sid;
+
+     rel = heap_open(LargeObjectRelationId, AccessShareLock);
+
+     ScanKeyInit(&skey,
+                 Anum_pg_largeobject_loid,
+                 BTEqualStrategyNumber, F_OIDEQ,
+                 ObjectIdGetDatum(loid));
+
+     scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
+                               SnapshotNow, 1, &skey);
+     tuple = systable_getnext(scan);
+     if (!HeapTupleIsValid(tuple))
+         ereport(ERROR,
+                 (errcode(ERRCODE_UNDEFINED_OBJECT),
+                  errmsg("large object %u does not exist", loid)));
+     pgaceLargeObjectGetSecurity(rel, tuple);
+     sid = HeapTupleGetSecLabel(tuple);
+
+     systable_endscan(scan);
+     heap_close(rel, AccessShareLock);
+
+     return CStringGetTextDatum(pgaceSidToSecurityLabel(sid));
+ }
+
+ /*
+  * lo_set_security()
+  *
+  * This function set a new security attribute of a large object.
+  * It scans pg_largeobject system catalog with a given loid,
+  * and invokes pgaceLargeObjectSetSecurity() for each page frame.
+  */
+ Datum
+ lo_set_security(PG_FUNCTION_ARGS)
+ {
+     Oid            loid = PG_GETARG_OID(0);
+     Datum        labelTxt = PG_GETARG_DATUM(1);
+     Relation    rel;
+     ScanKeyData skey;
+     SysScanDesc sd;
+     HeapTuple    oldtup, newtup;
+     CatalogIndexState indstate;
+     Oid            sid;
+     List       *okList = NIL;
+     bool        found = false;
+
+     sid = pgaceSecurityLabelToSid(TextDatumGetCString(labelTxt));
+
+     ScanKeyInit(&skey,
+                 Anum_pg_largeobject_loid,
+                 BTEqualStrategyNumber,
+                 F_OIDEQ, ObjectIdGetDatum(loid));
+
+     rel = heap_open(LargeObjectRelationId, RowExclusiveLock);
+
+     indstate = CatalogOpenIndexes(rel);
+
+     sd = systable_beginscan(rel,
+                             LargeObjectLOidPNIndexId, true,
+                             SnapshotNow, 1, &skey);
+
+     while ((oldtup = systable_getnext(sd)) != NULL)
+     {
+         ListCell *l;
+
+         newtup = heap_copytuple(oldtup);
+         HeapTupleSetSecLabel(newtup, sid);
+
+         foreach (l, okList)
+         {
+             if (HeapTupleGetSecLabel(oldtup) == lfirst_oid(l))
+                 goto skip;        /* already checked */
+         }
+         okList = lappend_oid(okList, HeapTupleGetSecLabel(oldtup));
+
+         pgaceLargeObjectSetSecurity(rel, newtup, oldtup);
+     skip:
+         simple_heap_update(rel, &newtup->t_self, newtup);
+         CatalogUpdateIndexes(rel, newtup);
+         found = true;
+     }
+     systable_endscan(sd);
+     CatalogCloseIndexes(indstate);
+     heap_close(rel, RowExclusiveLock);
+
+     CommandCounterIncrement();
+
+     if (!found)
+         ereport(ERROR,
+                 (errcode(ERRCODE_UNDEFINED_OBJECT),
+                  errmsg("large object %u does not exist", loid)));
+
+     PG_RETURN_BOOL(true);
+ }
+
+ /******************************************************************
+  * Function stubs related to security modules
+  ******************************************************************/
+
+ /*
+  * If the guest of PGACE added its specific functions, it has to put
+  * function stubs on the following section, because the guest modules
+  * are not compiled and linked when it is disabled.
+  * It can cause a build problem in other environments.
+  */
+ #ifndef HAVE_SELINUX
+
+ static Datum
+ unavailable_function(const char *fn_name, int error_code)
+ {
+     ereport(ERROR,
+             (errcode(error_code),
+              errmsg("%s is not available", fn_name)));
+     PG_RETURN_VOID();
+ }
+
+ Datum
+ sepgsql_getcon(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_getservcon(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_get_user(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_get_role(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_get_type(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_get_range(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_set_user(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_set_role(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_set_type(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ Datum
+ sepgsql_set_range(PG_FUNCTION_ARGS)
+ {
+     return unavailable_function(__FUNCTION__,
+                                 ERRCODE_SELINUX_ERROR);
+ }
+
+ #endif   /* HAVE_SELINUX */
diff -Nrpc base/src/backend/security/pgaceHooks.c sepgsql/src/backend/security/pgaceHooks.c
*** base/src/backend/security/pgaceHooks.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/pgaceHooks.c    Wed Jan 21 17:22:37 2009
***************
*** 0 ****
--- 1,1547 ----
+ /*
+  * src/backend/security/pgaceHooks.c
+  *    Security hooks in PostgreSQL Access Control Extension (PGACE)
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  */
+ #include "postgres.h"
+
+ #include "security/pgace.h"
+
+ /*
+  * GUC parameter: pgace_feature
+  * It allows users to choose an enhanced security feature.
+  * It has a state of 'none' in the default, so you should
+  * specify it explicitly with initdb --pgace-feature=FEATURE.
+  */
+ int pgace_feature;
+
+ /*
+  * PGACE (PostgreSQL Access Control Extension)
+  *
+  * It provides a set of security hooks at strategic points and
+  * common facilities to manage security attribute of database
+  * objects. Its purpose is to allow various kind of enhanced
+  * security features with minimum impact to the core PostgreSQL
+  * codes.
+  * In generally, individual security feature has its own access
+  * control model, policy and granuality, however, they also have
+  * facilities to be shared commonly.
+  *
+  * The one is a set of security hooks. All the enhanced security
+  * codes have to be invoked via the hooks, and return a proper
+  * value or raise an error, if necessary.
+  * When you add a new security feature, you need the following steps.
+  *   1. add a option to 'pgace_feature' parameter.
+  *   2. modify hooks to invoke your security feature.
+  *      Please note that you don't need to modify all the hooks.
+  *      If you don't provide any feature, please keep it as is.
+  *
+  * Example: pgaceHeapTupleInsert() hook
+  * ------------------------------------
+  * bool
+  * pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+  *                      bool is_internal, bool with_returning)
+  * {
+  *     if (!rowaclHeapTupleInsert(rel, tuple,
+  *                                   is_internal,
+  *                                with_returning))
+  *         return false;
+  *
+  *     switch (pgace_feature)
+  *     {
+  * #ifdef HAVE_SELINUX
+  *     case PGACE_FEATURE_SELINUX:
+  *         if (sepgsqlIsEnabled())
+  *             return sepgsqlHeapTupleInsert(rel, tuple,
+  *                                           is_internal,
+  *                                           with_returning);
+  *     break;
+  * #endif
+  * #ifdef HAVE_FOO_SECURITY
+  *     case PGACE_FEATURE_FOO_SECURITY:
+  *         return fooSecurityHeapTupleInsert(rel, tuple,
+  *                                           is_internal,
+  *                                           with_returning);
+  *     break;
+  * #endif
+  *     default:
+  *         break;
+  *     }
+  * return true;
+  * }
+  * ------------------------------------
+  * If your security feature has platform dependency, related code
+  * should be enclosed by #ifdef ... #endif block.
+  * (In this case, it is named as FOO_SECURITY.)
+  * The pgace_feature shows what enhanced security feature is activated
+  * in this system. If your security feature is chosen, it can be invoked
+  * via pgaceHeapTupleInsert() just before a new tuple is inserted on
+  * the target relation. Your fooSecurityHeapTupleInsert() can make its
+  * decision based on its policy and given informations.
+  * This hook requires to return 'true' or 'false'. If it returns 'false',
+  * it will be skipped to insert the given tuple.
+  * You can notice the Row-level database ACLs feature is hardwired.
+  * It is an application of traditional DAC policy in Row-level.
+  *
+  * The other is facilities to manage security attribtue of database
+  * objects. They have text representation as most of secure operating
+  * system doing, but it is not stored in each tuples directly, to reduce
+  * storage comsumption.
+  * We can fetch them via HeapTupleGetSecLabel(tuple) macro. It is stored
+  * as a Oid value (called as security identifier) which indicates pg_security
+  * system catalog. It holds mapping between security identifier and security
+  * attribute in text representation.
+  * User can see/set security attribute of database objects via security_label
+  * system column.
+  */
+
+ /******************************************************************
+  * Initialization hooks
+  ******************************************************************/
+
+ /*
+  * pgaceShmemSize
+  *
+  * This hook has to return the size of shared memory required
+  * by the guest. If it needs no shared memory region, it should
+  * return 0.
+  */
+ Size
+ pgaceShmemSize(void)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlShmemSize();
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     return (Size) 0;
+ }
+
+ /*
+  * pgaceInitialize
+  *
+  * This hook is invoked when a new PostgreSQL instance is created.
+  * The guest can use this hook to initialize itself.
+  *
+  * is_bootstrap is true, if bootstraping mode.
+  */
+ void
+ pgaceInitialize(bool is_bootstrap)
+ {
+     /* Hardwired DAC initialization */
+     rowaclInitialize(is_bootstrap);
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlInitialize(is_bootstrap);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceStartupWorkerProcess
+  *
+  * The guest can create a worker process in this hook, if necessary.
+  * (currently, PGACE does not support multiple worker processes.)
+  *
+  * This hooks has to return the PID of child process. It is managed
+  * by postmaster in the same way to manage the other children.
+  * So, the worker process has to be available to handle signals.
+  *
+  * If unnecessary, it has to return (pid_t) 0.
+  */
+ pid_t
+ pgaceStartupWorkerProcess(void)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlStartupWorkerProcess();
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     return (pid_t) 0;
+ }
+
+ /******************************************************************
+  * SQL proxy hooks
+  ******************************************************************/
+
+ /*
+  * pgacePostQueryRewrite
+  *
+  * This hook is invoked just after query is rewritten.
+  *
+  * The guest can check/modify/replace given query trees in this
+  * hook, if necessary.
+  * queryList is a list of Query object processes by rewriter.
+  */
+ List *
+ pgacePostQueryRewrite(List *queryList)
+ {
+     /* Hardwired DAC checks */
+     queryList = rowaclPostQueryRewrite(queryList);
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlPostQueryRewrite(queryList);
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     return queryList;
+ }
+
+ /*
+  * pgaceExecutorStart
+  *
+  * This hook is invoked on the head of ExecutorStart().
+  *
+  * The arguments of this hook are come from the ones of ExecutorStart
+  * as is.
+  */
+ void
+ pgaceExecutorStart(QueryDesc *queryDesc, int eflags)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlExecutorStart(queryDesc, eflags);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceExecScan
+  *
+  * This hook is invoked on ExecScan for each tuple fetched.
+  * The guest can check its visibility, and can skip to scan the given
+  * tuple. If this hook returns false, the tuple is filtered from the
+  * result set or the target of updates/deletion.
+  *
+  * Otherwise, it has to return true.
+  *
+  * The guest can refer Scan::pgaceTuplePerms (declared as uint32).
+  * It is a copy come from RangeTblEntry::pgaceTuplePerms set in
+  * the previous phase. It can be used to mark what permissions are
+  * required to scanned tuples.
+  */
+ bool
+ pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot)
+ {
+     /* Hardwired DAC checks */
+     if (!rowaclExecScan(scan, rel, slot))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlExecScan(scan, rel, slot);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /*
+  * pgaceProcessUtility
+  *
+  * This hooks is invoked on the head of ProcessUtility().
+  */
+ void
+ pgaceProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlProcessUtility(parsetree, params, isTopLevel);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /******************************************************************
+  * HeapTuple modification hooks
+  ******************************************************************/
+
+ /*
+  * pgaceHeapTupleInsert
+  *
+  * This hooks is invoked just before a new tuple is inserted.
+  * If it returns false, inserting the given tuple is skipped.
+  * (or generates an error, if we cannot skip it simply.)
+  *
+  * The guest has to set a security attribute of a newly inserted
+  * tuple, if necessary and when user does not specify it explicitly.
+  *
+  * arguments:
+  * - rel is the target relation to be inserted.
+  * - tuple is the new tuple to be inserted.
+  * - is_internal is a bool to show whether it directly come from
+  *   user's query, or not.
+  * - with_returning is a bool to show whether this INSERT statement
+  *   has RETURNING clause, or not.
+  */
+ bool
+ pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+                      bool is_internal, bool with_returning)
+ {
+     /* Hardwired  DAC check */
+     if (!rowaclHeapTupleInsert(rel, tuple,
+                                is_internal,
+                                with_returning))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlHeapTupleInsert(rel, tuple,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /*
+  * pgaceHeapTupleUpdate
+  *
+  * This hook is invoked just before a tuple is updated.
+  * If it returns false, updating the given tuple is skipped.
+  * (or generates an error, if we cannot skip it simply.)
+  *
+  * The guest has to preserve a security attribute of the updated
+  * tuple, if necessary and when user specify its new security
+  * attribute explicitly.
+  *
+  * arguments:
+  * - rel is the target relation to be updated.
+  * - otid is the ItemPointer of the tuple with older version.
+  * - newtup is the tuple to be updated.
+  * - is_internal is a bool to show whether it directly come from
+  *   user's query, or not.
+  * - with_returning is a bool to show whether this INSERT statement
+  *   has RETURNING clause, or not.
+  */
+ bool
+ pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+                      bool is_internal, bool with_returning)
+ {
+     /* Hardwired DAC check */
+     if (!rowaclHeapTupleUpdate(rel, otid, newtup,
+                                is_internal,
+                                with_returning))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlHeapTupleUpdate(rel, otid, newtup,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /*
+  * pgaceHeapTupleDelete
+  *
+  * This hook is invoked just before a tuple is deleted.
+  * If it returns false, deleting the given tuple is skipped.
+  * (or generates an error, if we cannot skip it simply.)
+  *
+  * arguments:
+  * - rel is the target relation to be deleted.
+  * - otid is the ItemPointer of the tuple to be deleted.
+  * - is_internal is a bool to show whether it directly come from
+  *   user's query, or not.
+  * - with_returning is a bool to show whether this INSERT statement
+  *   has RETURNING clause, or not.
+  */
+ bool
+ pgaceHeapTupleDelete(Relation rel, ItemPointer otid,
+                      bool is_internal, bool with_returning)
+ {
+     /* Hardwired DAC check */
+     if (!rowaclHeapTupleDelete(rel, otid,
+                                is_internal,
+                                with_returning))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlHeapTupleDelete(rel, otid,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /******************************************************************
+  * Extended SQL statement hooks
+  ******************************************************************/
+
+ /*
+  * pgaceIsGramSecurityItem
+  *
+  * PGACE framework provides its guest to manage security attribute
+  * for some kind of database obejcts, using an enhanced SQL statement.
+  *
+  * For example:
+  *   CREATE TABLE tbl (
+  *       x  integer,
+  *       y  text
+  *   ) security_label = 'system_u:object_r:sepgsql_table_t:Classified';
+  *
+  * This hook is invoked during parsing given queries at parser/gram.y.
+  * It generates a DefElem object which holds explicitly specified
+  * security attribute. If working guest support the feature and the
+  * given DefElem has correct pair of defname and argument string,
+  * this hook should return true.
+  * In ths above example, the given DefElem has "security_label" as
+  * defname, and "system_u:object_r:sepgsql_table_t:Classified" as
+  * its argument string.
+  */
+ bool
+ pgaceIsGramSecurityItem(DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlIsGramSecurityItem(defel);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return false;
+ }
+
+ /*
+  * The series of following hooks has three arguments.
+  * - rel is an opened relation of the target system catalog.
+  * - tuple is a new tuple to be inserted/updated.
+  * - defel is a DefElem object checked in pgaceIsGramSecurityItem().
+  */
+
+ /*
+  * pgaceGramCreateRelation
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before inserting a new tuple into pg_class system catalog on
+  * the processing of CREATE TABLE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a new relation.
+  */
+ void
+ pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramCreateRelation(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of table "
+                         "via CREATE TABLE")));
+ }
+
+ /*
+  * pgaceGramCreateAttribute
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before inserting a new tuple into pg_attribute system catalog on
+  * the processing of CREATE TABLE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a new column.
+  */
+ void
+ pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramCreateAttribute(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of column "
+                         "via CREATE TABLE")));
+ }
+
+ /*
+  * pgaceGramAlterRelation
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before updating an older tuple of pg_class system catalog on
+  * the processing of ALTER TABLE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a table.
+  */
+ void
+ pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramAlterRelation(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of table "
+                         "via ALTER TABLE")));
+ }
+
+ /*
+  * pgaceGramAlterAttribute
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before updating an older tuple of pg_attribute system catalog on
+  * the processing of ALTER TABLE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a column.
+  */
+ void
+ pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramAlterAttribute(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of column "
+                         "via ALTER TABLE")));
+ }
+
+ /*
+  * pgaceGramCreateDatabase
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before inserting a new tuple into pg_database system catalog on
+  * the processing of CREATE DATABASE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a database.
+  */
+ void
+ pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramCreateDatabase(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of database "
+                         "via CREATE DATABASE")));
+ }
+
+ /*
+  * pgaceGramAlterDatabase
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before updating an older tuple of pg_database system catalog on
+  * the processing of ALTER DATABASE.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a database.
+  */
+ void
+ pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramAlterDatabase(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of database "
+                         "via ALTER DATABASE")));
+ }
+
+ /*
+  * pgaceGramCreateFunction
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before inserting a new tuple into pg_proc system catalog on
+  * the processing of CREATE FUNCTION.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a function.
+  */
+ void
+ pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramCreateFunction(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of function "
+                         "via CREATE FUNCTION")));
+ }
+
+ /*
+  * pgaceGramAlterFunction
+  *
+  * This hook invoked to apply an explicitly specified security attribute
+  * just before updating an older tuple of pg_proc system catalog on
+  * the processing of ALTER FUNCTION.
+  * The guest can attach the required security attribute for the given
+  * tuple which means a function.
+  */
+ void
+ pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlGramAlterFunction(rel, tuple, defel);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+
+     if (defel)
+         ereport(ERROR,
+                 (errcode(ERRCODE_PGACE_ERROR),
+                  errmsg("unable to set security attribute of function "
+                         "via ALTER FUNCTION")));
+ }
+
+ /******************************************************************
+  * DATABASE related hooks
+  ******************************************************************/
+
+ /*
+  * pgaceSetDatabaseParam
+  *
+  * This hook is invoked just before putting a new value on a GUC
+  * variable.
+  *
+  * arguments:
+  * - name is a name of GUC variable.
+  * - argstring is its new value. NULL means user tries to reset
+  *   the given GUC variable.
+  */
+ void
+ pgaceSetDatabaseParam(const char *name, char *argstring)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlSetDatabaseParam(name, argstring);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceGetDatabaseParam
+  *
+  * This hook is invoked just before reffering a GUC variable.
+  *
+  * arguments:
+  * - name is a name of GUC variable.
+  */
+ void
+ pgaceGetDatabaseParam(const char *name)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlGetDatabaseParam(name);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /******************************************************************
+  * FUNCTION related hooks
+  ******************************************************************/
+
+ /*
+  * pgaceCallFunction
+  *
+  * This hook is invoked when a function is invoked as a part
+  * of the given query. It provides a FmgrInfo object of the
+  * function, so the guest can store its opaque data within
+  * FmgrInfo::fn_pgaceItem.
+  */
+ void
+ pgaceCallFunction(FmgrInfo *finfo)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlCallFunction(finfo);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceCallAggFunction
+  *
+  * This hook is invoked when an aggregate function is invoked
+  * in the given query. pgaceCallFunction() is also invoked for
+  * its transate function and finalize function.
+  *
+  * arguments:
+  * - aggTuple is the tuple of target aggregate function stored
+  *   in pg_aggregate system catalog.
+  */
+ void
+ pgaceCallAggFunction(HeapTuple aggTuple)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlCallAggFunction(aggTuple);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceCallFunctionTrigger
+  *
+  * This hook is invoked just before executing trigger function.
+  * If it returns false, the trigger function is not invoked and
+  * caller receives a NULL tuple as a result.
+  * (It also means skip to update/delete the tuple in BR-triggers.)
+  */
+ bool
+ pgaceCallTriggerFunction(TriggerData *tgdata)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlCallTriggerFunction(tgdata);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /*
+  * pgaceBeginPerformCheckFK
+  *
+  * This hook is invoked just before performing FK constraint checks.
+  * The guest can change its internal state during the checks.
+  * The major purpose of this function is to prevent violation of
+  * integrity consistentency violation due to row-level access control.
+  * If the guest requires an opaque data, it should be returned then
+  * it will be delivered via pgaceEndPerformCheckFK().
+  */
+ void
+ pgaceBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid,
+                          Datum *rowacl_private, Datum *pgace_private)
+ {
+     /* A wired DAC state change */
+     *rowacl_private = rowaclBeginPerformCheckFK(rel, is_primary, save_userid);
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             *pgace_private = sepgsqlBeginPerformCheckFK(rel, is_primary, save_userid);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceEndPerformCheckFK
+  *
+  * This hook is invoked just after performing FK constraint checks.
+  * The guest can restore its internal state using this hook.
+  */
+ void
+ pgaceEndPerformCheckFK(Relation rel, Datum rowacl_private, Datum pgace_private)
+ {
+     /* A wired DAC state restore */
+     rowaclEndPerformCheckFK(rel, rowacl_private);
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlEndPerformCheckFK(rel, pgace_private);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceAllowInlineFunction
+  *
+  * This hook gives guest a chance to make decision just before
+  * a set-returning function is inlined.
+  *
+  * arguments:
+  * - fnoid is oid of the function to be inlined.
+  * - func_tuple is tuple of the function stored in pg_proc.
+  */
+ bool
+ pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlAllowFunctionInlined(fnoid, func_tuple);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /******************************************************************
+  * TABLE related hooks
+  ******************************************************************/
+
+ /*
+  * pgaceLockTable
+  *
+  * This hook is invoked when user tries to LOCK a table explicitly.
+  * The argument of relid shows the target relation id.
+  */
+ void
+ pgaceLockTable(Oid relid)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLockTable(relid);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceExecTruncate
+  *
+  * This hook is invoked just before it truncate tables.
+  * The argument is a list of already opened relations with
+  * AccessExclusiveLock.
+  */
+ void
+ pgaceExecTruncate(List *trunk_rels)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlExecTruncate(trunk_rels);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /******************************************************************
+  * COPY TO/COPY FROM statement hooks
+  ******************************************************************/
+
+ /*
+  * pgaceCopyTable
+  *
+  * This hook is invoked before executing COPY TO/COPY FROM statement,
+  * to give the guest a chance to check tables/columns appeared in.
+  *
+  * arguments:
+  * - rel is the target relation of this COPY TO/FROM statement.
+  *   It can be NULL, when COPY (SELECT ...) TO ... is given.
+  * - attNumList is a list of attribute number
+  * - isFrom is a bool to show the direction of the COPY
+  */
+ void
+ pgaceCopyTable(Relation rel, List *attNumList, bool isFrom)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlCopyTable(rel, attNumList, isFrom);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceCopyFile
+  *
+  * This hook is invoked just after a target file is opened
+  * at COPY TO/COPY FROM statement to give the guest a chance to
+  * check whether it allows to read/write the file.
+  *
+  * arguments:
+  * - rel is the target relation of this COPY TO/FROM statement.
+  *   It can be NULL, when COPY (SELECT ...) TO ... is given.
+  * - isFrom is a bool to show the direction of the COPY
+  * - fdesc is the file descriptor of the target file opened.
+  * - filename is the filename of fdesc
+  */
+ void
+ pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlCopyFile(rel, fdesc, filename, isFrom);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceCopyToTuple
+  *
+  * This hook is invoked just before output of a fetched tuple on
+  * processing COPY TO statement, to give the guest a chance to make
+  * a decision whether the given tuple is visible, or not.
+  * If it returns false, the given tuple is not exported, as if it
+  * does not exist on the target relation.
+  * Elsewhere,
+  *
+  * arguments:
+  * - rel is the target relation of this
+  * - attNumList is a list of attribute number
+  * - tuple is a tuple to be checked
+  */
+ bool
+ pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple)
+ {
+     /* A wired DAC check */
+     if (!rowaclCopyToTuple(rel, attNumList, tuple))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlCopyToTuple(rel, attNumList, tuple);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+
+ /******************************************************************
+  * Loadable shared library module hooks
+  ******************************************************************/
+
+ /*
+  * pgaceLoadSharedModule
+  *
+  * This hook is invoked before loading a shared library module,
+  * to give the guest a change to confirm whether the required
+  * module is safe, or not.
+  *
+  * This hook can be also invoked implicitly when a user tries
+  * to call a function implemented within external modules.
+  */
+ void
+ pgaceLoadSharedModule(const char *filename)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLoadSharedModule(filename);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /******************************************************************
+  * Binary Large Object (BLOB) hooks
+  ******************************************************************/
+
+ /*
+  * pgaceLargeObjectCreate
+  *
+  * This hooks is invoked just before the first tuple of a new large
+  * object is inserted, to give the guest a change to make its
+  * decision and attach proper security context for the tuple.
+  *
+  * The argument of rel is the opened pg_largeobject system catalog.
+  */
+ void
+ pgaceLargeObjectCreate(Relation rel, HeapTuple tuple)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectCreate(rel, tuple);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectDrop
+  *
+  * This hook is invoked just before each tuple of a large object
+  * are deleted, to give the guest a change to make its decision.
+  *
+  * The argument of pgaceItem is an opaque data, the guest can
+  * use it discreationally.
+  */
+ void
+ pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectDrop(rel, tuple, pgaceItem);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectRead
+  *
+  * This hook is invoked at the head of lo_read().
+  * If the guest allows a large object to have non-uniform security
+  * attributes (not a unique one for each page frame), using HeapTuple
+  * related hooks are more recommendable.
+  */
+ void
+ pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectRead(lodesc, length);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectWrite
+  *
+  * This hook is invoked at the head of lo_write().
+  */
+ void
+ pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectWrite(lodesc, length);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectTruncate
+  *
+  * This hook is invoked at the head of lo_truncate().
+  */
+ void
+ pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectTruncate(lodesc, offset);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectImport
+  *
+  * This hook is invoked just before importing the given file.
+  */
+ void
+ pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectImport(loid, fdesc, filename);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectExport
+  *
+  * This hook is invoked just before exporting the given large object.
+  */
+ void
+ pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             sepgsqlLargeObjectExport(loid, fdesc, filename);
+         break;
+ #endif
+     default:
+         break;
+     }
+ }
+
+ /*
+  * pgaceLargeObjectGetSecurity
+  *
+  * This hook is invoked when user requires to run lo_get_security()
+  * Note that PGACE assumes the security attribute of first page frame
+  * of large object represents its security attribute.
+  */
+ void
+ pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlLargeObjectGetSecurity(rel, tuple);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+     ereport(ERROR,
+             (errcode(ERRCODE_PGACE_ERROR),
+              errmsg("no enhanced security feature is available.")));
+ }
+
+ /*
+  * pgaceLargeObjectSetSecurity
+  *
+  * This hook is invoked when user requires to run lo_set_security(),
+  * for each tuple within a given large object, which have unchecked
+  * security attribute. In other word, PGACE does not require the guest
+  * to check permission toward same security attribute twice, or more.
+  */
+ void
+ pgaceLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+         {
+             sepgsqlLargeObjectSetSecurity(rel, newtup, oldtup);
+             return;
+         }
+         break;
+ #endif
+     default:
+         break;
+     }
+     ereport(ERROR,
+             (errcode(ERRCODE_PGACE_ERROR),
+              errmsg("no enhanced security feature is available.")));
+ }
+
+ /******************************************************************
+  * Security Label hooks
+  ******************************************************************/
+
+ /*
+  * pgaceTupleDescHasRowAcl
+  *
+  * This hook enables to control the value of TupleDesc->tdhasrowacl.
+  *
+  */
+ bool
+ pgaceTupleDescHasRowAcl(Relation rel, List *relopts)
+ {
+     return rowaclTupleDescHasRowAcl(rel, relopts);
+ }
+
+ /*
+  * pgaceTupleDescHasSecurity
+  *
+  * This hook enables to control the value in TupleDesc->tdhasseclabel.
+  * If it returns true, sizeof(Oid) bytes are allocated at the header
+  * of HeapTupleHeader structure.
+  *
+  * The 'rel' argument can be NULL, when we make a decision for newly
+  * created relation via SELECT INTO/CREATE TABLE AS. In this case,
+  * unparsed relation options are delivered.
+  */
+ bool
+ pgaceTupleDescHasSecLabel(Relation rel, List *relopts)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlTupleDescHasSecLabel(rel, relopts);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return false;
+ }
+
+ /*
+  * pgaceTranslateSecurityLabelIn
+  *
+  * This hook enables the guest to translate a text representation
+  * of a given security attribute in external format into internal
+  * raw-format. It is invoked when user specifies security attribute
+  * explicitly in INSERT/UPDATE statement, to translate it into
+  * raw-internal format.
+  *
+  * It has to return a palloc()'ed Cstring, as a raw-internal format.
+  *
+  * In SE-PostgreSQL it supports translation in MLS/MCS labels like:
+  *   "system_u:object_r:sepgsql_table_t:SystemHigh"
+  *     <-->  "system_u:object_r:sepgsql_table_t:s0:c0.c1023"
+  */
+ char *
+ pgaceTranslateSecurityLabelIn(char *seclabel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlTranslateSecurityLabelIn(seclabel);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return seclabel;
+ }
+
+ /*
+  * pgaceTranslateSecurityLabelOut
+  *
+  * This hook enables the guest to translate a text representation
+  * of a given security attribute in internal format into cosmetic
+  * external format.
+  */
+ char *
+ pgaceTranslateSecurityLabelOut(char *seclabel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlTranslateSecurityLabelOut(seclabel);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return seclabel;
+ }
+
+ /*
+  * pgaceValidateSecurityLabel
+  *
+  * This hook enables the guest to validate the given security attribute
+  * in raw-internal format.
+  */
+ bool
+ pgaceCheckValidSecurityLabel(char *seclabel)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlCheckValidSecurityLabel(seclabel);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return false;
+ }
+
+ /*
+  * pgaceUnlabeledSecurityLabel
+  *
+  * This hooks allows the guest to provide an alternative security
+  * attribute, when no valid text representation found on pg_security.
+  * The hooks has to return an alternative attribute palloc()'ed.
+  */
+ char *
+ pgaceUnlabeledSecurityLabel(void)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlUnlabeledSecurityLabel();
+         break;
+ #endif
+     default:
+         break;
+     }
+     return NULL;
+ }
+
+ /*
+  * pgaceSecurityLabelOfLabel
+  *
+  * This hook has to return the security attribute of a newly inserted
+  * tuple withing pg_security system catalog. Note that we need a special
+  * handling in the case of pg_security. If a new tuple requires a quite
+  * new security attribute which is not on pg_security, its insertion
+  * invokes one more insertion into pg_security. In the result, it makes
+  * infinite function invocation.
+  * This hook is used to avoid such a situation. The guest has to return
+  * a text represented security attribute.
+  */
+ char *
+ pgaceSecurityLabelOfLabel(void)
+ {
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlSecurityLabelOfLabel();
+         break;
+ #endif
+     default:
+         break;
+     }
+     return NULL;
+ }
diff -Nrpc base/src/backend/security/rowacl/rowacl.c sepgsql/src/backend/security/rowacl/rowacl.c
*** base/src/backend/security/rowacl/rowacl.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/rowacl/rowacl.c    Fri Jan 16 10:31:05 2009
***************
*** 0 ****
--- 1,721 ----
+ /*
+  * src/backend/rowacl/rowacl.c
+  *   Row-level Database ACLs support
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "access/reloptions.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
+ #include "commands/defrem.h"
+ #include "miscadmin.h"
+ #include "nodes/nodeFuncs.h"
+ #include "parser/parsetree.h"
+ #include "security/pgace.h"
+ #include "storage/bufmgr.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/guc.h"
+ #include "utils/inval.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/syscache.h"
+
+ #define ROWACL_ALL_PRIVS    (ACL_SELECT | ACL_UPDATE | ACL_DELETE | ACL_REFERENCES)
+
+ static void walkOnQueryTree(Query *query);
+
+ /******************************************************************
+  * Mark appeared Query/Sub-Query
+  ******************************************************************/
+
+ static bool walkOnNodeTree(Node *node, Query *query)
+ {
+     if (!node)
+         return false;
+
+     if (IsA(node, RangeTblRef))
+     {
+         RangeTblRef *rtr = (RangeTblRef *) node;
+         RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+         if (rte->rtekind == RTE_RELATION &&
+             rtr->rtindex != query->resultRelation)
+         {
+             rte->pgaceTuplePerms |= ACL_SELECT;
+         }
+         else if (rte->rtekind == RTE_SUBQUERY)
+         {
+             walkOnQueryTree(rte->subquery);
+         }
+     }
+     else if (IsA(node, Query))
+     {
+         walkOnQueryTree((Query *) node);
+     }
+     else if (IsA(node, SortGroupClause))
+     {
+         /*
+          * expression_tree_walker() does not understand
+          * T_SortGroupClause node, so we have to avoid it
+          * walking on the node type.
+          */
+         return false;
+     }
+
+     return expression_tree_walker(node, walkOnNodeTree, (void *) query);
+ }
+
+ static void walkOnQueryTree(Query *query)
+ {
+     RangeTblEntry *rte;
+
+     if (query->commandType == CMD_UPDATE)
+     {
+         rte = rt_fetch(query->resultRelation, query->rtable);
+         rte->pgaceTuplePerms |= ACL_UPDATE;
+         if (query->returningList)
+             rte->pgaceTuplePerms |= ACL_SELECT;
+     }
+     else if (query->commandType == CMD_DELETE)
+     {
+         rte = rt_fetch(query->resultRelation, query->rtable);
+         rte->pgaceTuplePerms |= ACL_DELETE;
+         if (query->returningList)
+             rte->pgaceTuplePerms |= ACL_SELECT;
+     }
+     query_tree_walker(query,
+                       walkOnNodeTree,
+                       (void *) query, 0);
+ }
+
+ List *rowaclPostQueryRewrite(List *queryList)
+ {
+     ListCell *l;
+
+     foreach (l, queryList)
+     {
+         Query *query = (Query *) lfirst(l);
+
+         Assert(IsA(query, Query));
+
+         if (query->commandType == CMD_SELECT ||
+             query->commandType == CMD_UPDATE ||
+             query->commandType == CMD_DELETE)
+             walkOnQueryTree(query);
+     }
+
+     return queryList;
+ }
+
+ /******************************************************************
+  * Cache boost row-level ACLs checks
+  ******************************************************************/
+
+ static MemoryContext RowAclMemCtx;
+
+ #define ROWACL_CACHE_SLOT_NUM       128
+ static List *rowaclCacheSlot[ROWACL_CACHE_SLOT_NUM];
+
+ static void rowaclCacheReset(void)
+ {
+     int i;
+
+     MemoryContextReset(RowAclMemCtx);
+
+     for (i=0; i < ROWACL_CACHE_SLOT_NUM; i++)
+         rowaclCacheSlot[i] = NIL;
+ }
+
+ typedef struct {
+     Oid        relid;
+     Oid        userid;
+     Oid        aclid;
+     AclMode    privs;
+ } rowaclCacheItem;
+
+ static int rowaclCacheHash(Oid relid, Oid userid, Oid aclid)
+ {
+     Oid keys[3] = { relid, userid, aclid };
+
+     return tag_hash(keys, sizeof(keys)) % ROWACL_CACHE_SLOT_NUM;
+ }
+
+ static void rowaclCacheInsert(Oid relid, Oid userid, Oid aclid, AclMode privs)
+ {
+     MemoryContext oldctx;
+     rowaclCacheItem *aci;
+     int index = rowaclCacheHash(relid, userid, aclid);
+
+     oldctx = MemoryContextSwitchTo(RowAclMemCtx);
+
+     aci = palloc0(sizeof(rowaclCacheItem));
+     aci->relid = relid;
+     aci->userid = userid;
+     aci->aclid = aclid;
+     aci->privs = privs;
+
+     rowaclCacheSlot[index] = lappend(rowaclCacheSlot[index], aci);
+
+     MemoryContextSwitchTo(oldctx);
+ }
+
+ static bool rowaclCacheLookup(Oid relid, Oid userid, Oid aclid, AclMode *privs)
+ {
+     ListCell *l;
+     int index = rowaclCacheHash(relid, userid, aclid);
+
+     foreach (l, rowaclCacheSlot[index])
+     {
+         rowaclCacheItem *aci = lfirst(l);
+
+         if (aci->relid == relid &&
+             aci->userid == userid &&
+             aci->aclid == aclid)
+         {
+             *privs = aci->privs;
+             return true;
+         }
+     }
+
+     return false;
+ }
+
+ static void
+ rowaclSyscacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
+ {
+     rowaclCacheReset();
+ }
+
+ void rowaclInitialize(bool is_bootstrap)
+ {
+     RowAclMemCtx = AllocSetContextCreate(TopMemoryContext,
+                                          "Row-level ACL result cache",
+                                          ALLOCSET_DEFAULT_MINSIZE,
+                                          ALLOCSET_DEFAULT_INITSIZE,
+                                          ALLOCSET_DEFAULT_MAXSIZE);
+
+     CacheRegisterSyscacheCallback(AUTHOID,
+                                   rowaclSyscacheCallback, 0);
+     CacheRegisterSyscacheCallback(RELOID,
+                                   rowaclSyscacheCallback, 0);
+     rowaclCacheReset();
+ }
+
+ /******************************************************************
+  * Row-level access controls
+  ******************************************************************/
+
+ struct rowaclUserInfoType {
+     Oid userid;
+     bool abort_on_error;
+ };
+ struct rowaclUserInfoType *rowaclUserInfo = NULL;
+
+ Datum rowaclBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid)
+ {
+     Datum save_pgace = PointerGetDatum(rowaclUserInfo);
+     struct rowaclUserInfoType *uinfo
+         = palloc0(sizeof(struct rowaclUserInfoType));
+     uinfo->userid = save_userid;
+     uinfo->abort_on_error = is_primary;
+
+     rowaclUserInfo = uinfo;
+
+     return save_pgace;
+ }
+
+ void rowaclEndPerformCheckFK(Relation rel, Datum save_pgace)
+ {
+     rowaclUserInfo = (struct rowaclUserInfoType *) DatumGetPointer(save_pgace);
+ }
+
+ static bool
+ rowaclCheckPermission(Relation rel, HeapTuple tuple, AclMode required)
+ {
+     Oid relid = RelationGetRelid(rel);
+     Oid ownerid = RelationGetForm(rel)->relowner;
+     Oid userid = GetUserId();
+     Oid aclid = HeapTupleGetRowAcl(tuple);
+     AclMode privs;
+
+     if (rowaclUserInfo)
+     {
+         userid = rowaclUserInfo->userid;
+
+         /*
+          * If ACL_SELECT is given within FK constraint checks,
+          * its privilege is replaced to ACL_REFERENCES.
+          */
+         if (required & ACL_SELECT)
+         {
+             required &= ~ACL_SELECT;
+             required |= ACL_REFERENCES;
+         }
+     }
+
+     if (!rowaclCacheLookup(relid, userid, aclid, &privs))
+     {
+         /* Superusers/Owner bypass all permission checking */
+         if (superuser_arg(userid) || userid == ownerid)
+         {
+             privs = ROWACL_ALL_PRIVS;
+         }
+         else
+         {
+             Acl *acl = rowaclSidToSecurityAcl(aclid, ownerid);
+
+             privs = aclmask(acl, userid, ownerid, ROWACL_ALL_PRIVS, ACLMASK_ALL);
+         }
+         rowaclCacheInsert(relid, userid, aclid, privs);
+     }
+
+     if ((privs & required) == required)
+         return true;
+
+     if (rowaclUserInfo && rowaclUserInfo->abort_on_error)
+         ereport(ERROR,
+                 (errcode(ERRCODE_ROWACL_ERROR),
+                  errmsg("access violation in row-level acl")));
+
+     return false;
+ }
+
+ bool rowaclExecScan(Scan *scan, Relation rel, TupleTableSlot *slot)
+ {
+     AclMode required = scan->pgaceTuplePerms & ROWACL_ALL_PRIVS;
+     HeapTuple tuple;
+
+     if (required==0 || !RelationGetRowLevelAcl(rel))
+         return true;
+
+     tuple = ExecMaterializeSlot(slot);
+
+     return rowaclCheckPermission(rel, tuple, required);
+ }
+
+ bool rowaclCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple)
+ {
+     if (!RelationGetRowLevelAcl(rel))
+         return true;
+
+     return rowaclCheckPermission(rel, tuple, ACL_SELECT);
+ }
+
+ /******************************************************************
+  * Check ownership of tuples
+  ******************************************************************/
+ bool rowaclHeapTupleInsert(Relation rel, HeapTuple tuple,
+                            bool is_internal, bool with_returning)
+ {
+     if (!HeapTupleHasRowAcl(tuple))
+         return true;
+
+     if (OidIsValid(HeapTupleGetRowAcl(tuple)))
+     {
+         if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("only general relation can have row-level ACL")));
+         if (!is_internal &&
+             !pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("Only owner or superuser can set ACL")));
+     }
+     else
+     {
+         char *default_row_acl = RelationGetDefaultRowAcl(rel);
+
+         if (default_row_acl)
+         {
+             FmgrInfo finfo;
+             Datum aclDat;
+             Oid sid;
+
+             fmgr_info(F_ARRAY_IN, &finfo);
+             aclDat = FunctionCall3(&finfo,
+                                    CStringGetDatum(default_row_acl),
+                                    ObjectIdGetDatum(ACLITEMOID),
+                                    Int32GetDatum(-1));
+             sid = rowaclSecurityAclToSid(DatumGetAclP(aclDat));
+             HeapTupleSetRowAcl(tuple, sid);
+         }
+     }
+
+     return true;
+ }
+
+ static HeapTuple
+ getHeapTupleFromItemPointer(Relation rel, ItemPointer tid)
+ {
+     /*
+      * obtain an old tuple
+      */
+     Buffer      buffer;
+     PageHeader  dp;
+     ItemId      lp;
+     HeapTupleData tuple;
+     HeapTuple   oldtup;
+
+     buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid));
+     LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+     dp = (PageHeader) BufferGetPage(buffer);
+     lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
+
+     Assert(ItemIdIsNormal(lp));
+
+     tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+     tuple.t_len = ItemIdGetLength(lp);
+     tuple.t_self = *tid;
+     tuple.t_tableOid = RelationGetRelid(rel);
+     oldtup = heap_copytuple(&tuple);
+
+     LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+     ReleaseBuffer(buffer);
+
+     return oldtup;
+ }
+
+ bool rowaclHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+                            bool is_internal, bool with_returning)
+ {
+     HeapTuple oldtup = getHeapTupleFromItemPointer(rel, otid);
+
+     if (!HeapTupleHasRowAcl(newtup))
+         return true;
+
+     if (!OidIsValid(HeapTupleGetRowAcl(newtup)))
+     {
+         /* preserve old ACL */
+         HeapTupleSetRowAcl(newtup, HeapTupleGetRowAcl(oldtup));
+     }
+     else if (HeapTupleGetRowAcl(newtup) != HeapTupleGetRowAcl(oldtup))
+     {
+         if (!is_internal &&
+             !pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("Only owner or superuser can set ACL")));
+     }
+     return true;
+ }
+
+ bool rowaclHeapTupleDelete(Relation rel, ItemPointer otid,
+                            bool is_internal, bool with_returning)
+ {
+     /*
+      * Do nothing here
+      */
+     return true;
+ }
+
+ /******************************************************************
+  * Relation Options
+  ******************************************************************/
+ void
+ rawaclValidateDefaultRowAclRelopt(const char *value)
+ {
+     FmgrInfo finfo;
+     Datum acldat;
+
+     /*
+      * If given default row-acl in reloptions is not valid,
+      * aclitemin can raise an error.
+      */
+     fmgr_info(F_ARRAY_IN, &finfo);
+     acldat = FunctionCall3(&finfo,
+                            CStringGetDatum(value),
+                            ObjectIdGetDatum(ACLITEMOID),
+                            Int32GetDatum(-1));
+     pfree(DatumGetAclP(acldat));
+ }
+
+ /******************************************************************
+  * Row-level ACLs management
+  ******************************************************************/
+
+ bool rowaclTupleDescHasRowAcl(Relation rel, List *relopts)
+ {
+     ListCell *l;
+
+     if (rel)
+         return RelationGetRowLevelAcl(rel);
+
+     /* SELECT INTO cases */
+     foreach (l, relopts)
+     {
+         DefElem *def = (DefElem *) lfirst(l);
+
+         if (pg_strcasecmp(def->defname, "row_level_acl") == 0)
+             return defGetBoolean(def);
+     }
+
+     return false;
+ }
+
+ static Acl *
+ rowaclDefaultAclArray(Oid ownerId)
+ {
+     Acl *acl;
+     AclItem *aip;
+
+     /*
+      * All permissions to public in default
+      */
+     acl = allocacl(1);
+     aip = ACL_DAT(acl);
+     aip->ai_grantee = ACL_ID_PUBLIC;
+     aip->ai_grantor = ownerId;
+     aip->ai_privs = ROWACL_ALL_PRIVS;
+
+     return acl;
+ }
+
+ static bool
+ rowaclCheckValidSecurityAcl(const char *raw_acl)
+ {
+     char *copy, *tok, *sv = NULL;
+     AclItem ai;
+
+     if (strncmp(raw_acl, "acl:", 4) != 0)
+         return false;
+
+     copy = pstrdup(raw_acl + 4);
+     for (tok = strtok_r(copy, ",", &sv);
+          tok;
+          tok = strtok_r(NULL, ",", &sv))
+     {
+         if (sscanf(tok, "%x=%x/%x",
+                    &ai.ai_grantee,
+                    &ai.ai_privs,
+                    &ai.ai_grantor) != 3)
+             return false;
+     }
+
+     return true;
+ }
+
+ static Acl *
+ rawAclTextToAclArray(const char *raw_acl)
+ {
+     Acl *acl;
+     AclItem *aip;
+     char *copy, *tok, *sv = NULL;
+     int num = 0;
+
+     Assert(strncmp(raw_acl, "acl:", 4) == 0);
+
+     copy = pstrdup(raw_acl + 4);
+     aip = palloc(strlen(copy) * sizeof(AclItem) / 4);
+     for (tok = strtok_r(copy, ",", &sv);
+          tok;
+          tok = strtok_r(NULL, ",", &sv))
+     {
+         if (sscanf(tok, "%x=%x/%x",
+                    &aip[num].ai_grantee,
+                    &aip[num].ai_privs,
+                    &aip[num].ai_grantor) != 3)
+             continue;
+         num++;
+     }
+
+     acl = allocacl(num);
+     memcpy(ACL_DAT(acl), aip, num * sizeof(AclItem));
+
+     pfree(aip);
+     pfree(copy);
+
+     check_acl(acl);
+
+     return acl;
+ }
+
+ static char *
+ rawAclTextFromAclArray(Acl *acl)
+ {
+     AclItem *aip = ACL_DAT(acl);
+     char *raw_acl = palloc0(ACL_NUM(acl) * 30 + 10); /* enough length */
+     int i, ofs;
+
+     ofs = sprintf(raw_acl, "acl:");
+
+     for (i = 0; i < ACL_NUM(acl); i++)
+     {
+         if ((aip[i].ai_privs & ROWACL_ALL_PRIVS) != aip[i].ai_privs)
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("unsupported privileges in row_acl: %04x",
+                             aip[i].ai_privs & ~ROWACL_ALL_PRIVS)));
+
+         ofs += sprintf(raw_acl + ofs,
+                        "%s%x=%x/%x",
+                        (i == 0 ? "" : ","),
+                        aip[i].ai_grantee,
+                        aip[i].ai_privs,
+                        aip[i].ai_grantor);
+     }
+
+     return raw_acl;
+ }
+
+ Acl *rowaclSidToSecurityAcl(Oid sid, Oid ownerId)
+ {
+     char *raw_acl
+         = pgaceLookupSecurityLabel(sid);
+
+     if (!raw_acl || !rowaclCheckValidSecurityAcl(raw_acl))
+         return rowaclDefaultAclArray(ownerId);
+
+     return rawAclTextToAclArray(raw_acl);
+ }
+
+ Oid rowaclSecurityAclToSid(Acl *acl)
+ {
+     char *raw_acl
+         = rawAclTextFromAclArray(acl);
+
+     return pgaceLookupSecurityId(raw_acl);
+ }
+
+ Datum rowaclHeapGetSecurityAclSysattr(HeapTuple tuple)
+ {
+     HeapTuple rtup;
+     Oid        rowaclSid = InvalidOid;
+     Oid        relowner;
+     char    relkind;
+     Datum    reloptions;
+     bool    isnull;
+
+     rtup = SearchSysCache(RELOID,
+                           ObjectIdGetDatum(tuple->t_tableOid),
+                           0, 0, 0);
+     if (!HeapTupleIsValid(rtup))
+         elog(ERROR, "cache lookup failed for relation %u", tuple->t_tableOid);
+
+     relowner = ((Form_pg_class) GETSTRUCT(rtup))->relowner;
+     relkind = ((Form_pg_class) GETSTRUCT(rtup))->relkind;
+     reloptions = SysCacheGetAttr(RELOID, rtup,
+                                  Anum_pg_class_reloptions,
+                                  &isnull);
+     if (!isnull)
+     {
+         StdRdOptions *RdOpts
+             = (StdRdOptions *) heap_reloptions(relkind, reloptions, false);
+
+         if (RdOpts && RdOpts->row_level_acl)
+             rowaclSid = HeapTupleGetRowAcl(tuple);
+     }
+
+     ReleaseSysCache(rtup);
+
+     return PointerGetDatum(rowaclSidToSecurityAcl(rowaclSid, relowner));
+ }
+
+ /******************************************************************
+  * SQL functions
+  ******************************************************************/
+
+ static Datum rowacl_grant_revoke(PG_FUNCTION_ARGS, bool grant, bool cascade)
+ {
+     char *input, *tok, *sv = NULL;
+     HeapTuple tuple;
+     Acl *acl;
+     Oid ownerid;
+     List *grantees = NIL;
+     AclMode privileges = 0;
+
+     /*
+      * Get owner Id;
+      */
+     tuple = SearchSysCache(RELOID,
+                            PG_GETARG_DATUM(0), 0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "cache lookup failed for relation %u",
+              PG_GETARG_OID(0));
+
+     ownerid = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
+     ReleaseSysCache(tuple);
+
+     /*
+      * Extract Acl array
+      */
+     acl = PG_GETARG_ACL_P(1);
+
+     /*
+      * Extract usernames
+      */
+     input = TextDatumGetCString(PG_GETARG_TEXT_P(2));
+     for (tok = strtok_r(input, ",", &sv);
+          tok;
+          tok = strtok_r(NULL, ",", &sv))
+     {
+         if (strcasecmp(tok, "public") == 0)
+             grantees = lappend_oid(grantees, ACL_ID_PUBLIC);
+         else
+         {
+             Oid roleid = get_roleid(tok);
+
+             if (roleid == InvalidOid)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_ROWACL_ERROR),
+                          errmsg("%s is not a valid identifier", tok)));
+
+             grantees = lappend_oid(grantees, roleid);
+         }
+     }
+     /*
+      * Extract permission names
+      */
+     input = TextDatumGetCString(PG_GETARG_TEXT_P(3));
+     for (tok = strtok_r(input, ",", &sv);
+          tok;
+          tok = strtok_r(NULL, ",", &sv))
+     {
+         if (strcasecmp(tok, "all") == 0)
+             privileges |= ROWACL_ALL_PRIVS;
+         else if (strcasecmp(tok, "select") == 0)
+             privileges |= ACL_SELECT;
+         else if (strcasecmp(tok, "update") == 0)
+             privileges |= ACL_UPDATE;
+         else if (strcasecmp(tok, "delete") == 0)
+             privileges |= ACL_DELETE;
+         else if (strcasecmp(tok, "references") == 0)
+             privileges |= ACL_REFERENCES;
+         else
+             ereport(ERROR,
+                     (errcode(ERRCODE_ROWACL_ERROR),
+                      errmsg("%s is not a valid permission", tok)));
+     }
+
+     /*
+      * Merge ACL
+      */
+     acl = merge_acl_with_grant(acl, grant, false, cascade,
+                                grantees, privileges,
+                                GetUserId(), ownerid);
+
+     PG_RETURN_ACL_P(acl);
+ }
+
+ Datum
+ rowacl_grant(PG_FUNCTION_ARGS)
+ {
+     return rowacl_grant_revoke(fcinfo, true, false);
+ }
+
+ Datum
+ rowacl_revoke(PG_FUNCTION_ARGS)
+ {
+     return rowacl_grant_revoke(fcinfo, false, false);
+ }
+
+ Datum
+ rowacl_revoke_cascade(PG_FUNCTION_ARGS)
+ {
+     return rowacl_grant_revoke(fcinfo, false, true);
+ }
diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c
*** base/src/backend/security/sepgsql/avc.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/sepgsql/avc.c    Thu Jan 22 14:09:37 2009
***************
*** 0 ****
--- 1,1200 ----
+ /*
+  * src/backend/security/sepgsql/avc.c
+  *      SE-PostgreSQL userspace access vector cache
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "access/hash.h"
+ #include "libpq/pqsignal.h"
+ #include "postmaster/postmaster.h"
+ #include "security/pgace.h"
+ #include "storage/ipc.h"
+ #include "storage/lwlock.h"
+ #include "utils/memutils.h"
+ #include "utils/syscache.h"
+ #include <linux/netlink.h>
+ #include <linux/selinux_netlink.h>
+ #include <signal.h>
+ #include <unistd.h>
+
+ /*
+  * uAVC: userspace Access Vector Cache
+  *
+  * SE-PostgreSQL makes inqueries for SELinux to check whether the security
+  * policy allows the required action, or not. However, it need to invoke
+  * system call because SELinux is a kernel feature and it hold its security
+  * policy in the kernel memory.
+  *
+  * uAVC enables to reduce the number of kernel invocation, with caching
+  * the result of inquiries. When we have to make a decision based on the
+  * security policy of SELinux, it tries to find up an appropriate cache
+  * entry on the uAVC. If exist, we don't need to invoke a system call
+  * and can reduce unnecessary overhead.
+  *
+  * If not exist, SE-PostgreSQL makes a new cache entry based on the
+  * result of inquiries, and chains it on uAVC to prepare the following
+  * decision makings.
+  *
+  * uAVC has a version number to check whether it is now valid, or not.
+  * Not need to say, uAVC cache entry has to be invalid just after
+  * policy reloaded or state change.
+  * If it is not match the latest one, updated by the policy state
+  * monitoring process, uAVC has to be reseted.
+  */
+
+ /*
+  * Dynamic object class/access vector mapping
+  *
+  * SELinux exports the list of object classes (it means kind of object, like
+  * file or table) and access vectors (it means permission set, like read,
+  * select, ...) under /selinux/class.
+  * It enables to provide userspace object managers a interface to get what
+  * codes should be used to ask SELinux.
+  *
+  * libselinux provides an API to translate a string expression and a code
+  * used by the loaded security policy. These correspondences are not assured
+  * over the bound of policy loading, so we have to reload the mapping after
+  * in-kernel policy is reloaded, or its state is changed.
+  */
+ static struct
+ {
+     struct
+     {
+         const char *name;
+         security_class_t internal;
+     } tclass;
+     struct
+     {
+         char       *name;
+         access_vector_t internal;
+     } av_perms[sizeof(access_vector_t) * 8];
+ } selinux_catalog[] = {
+     {
+         { "db_database", SECCLASS_DB_DATABASE},
+         {
+             { "create",            DB_DATABASE__CREATE },
+             { "drop",            DB_DATABASE__DROP },
+             { "getattr",        DB_DATABASE__GETATTR },
+             { "setattr",        DB_DATABASE__SETATTR },
+             { "relabelfrom",    DB_DATABASE__RELABELFROM },
+             { "relabelto",        DB_DATABASE__RELABELTO },
+             { "access",            DB_DATABASE__ACCESS },
+             { "install_module",    DB_DATABASE__INSTALL_MODULE },
+             { "load_module",    DB_DATABASE__LOAD_MODULE },
+             { "get_param",        DB_DATABASE__GET_PARAM },
+             { "set_param",        DB_DATABASE__SET_PARAM },
+             { NULL, 0UL },
+         }
+     },
+     {
+         { "db_table", SECCLASS_DB_TABLE},
+         {
+             { "create",            DB_TABLE__CREATE },
+             { "drop",            DB_TABLE__DROP },
+             { "getattr",        DB_TABLE__GETATTR },
+             { "setattr",        DB_TABLE__SETATTR },
+             { "relabelfrom",    DB_TABLE__RELABELFROM },
+             { "relabelto",        DB_TABLE__RELABELTO },
+             { "use",               DB_TABLE__USE },
+             { "select",            DB_TABLE__SELECT },
+             { "update",            DB_TABLE__UPDATE },
+             { "insert",            DB_TABLE__INSERT },
+             { "delete",            DB_TABLE__DELETE },
+             { "lock",            DB_TABLE__LOCK },
+             { NULL, 0UL },
+         }
+     },
+     {
+         { "db_procedure", SECCLASS_DB_PROCEDURE},
+         {
+             { "create",            DB_PROCEDURE__CREATE },
+             { "drop",            DB_PROCEDURE__DROP },
+             { "getattr",        DB_PROCEDURE__GETATTR },
+             { "setattr",        DB_PROCEDURE__SETATTR },
+             { "relabelfrom",    DB_PROCEDURE__RELABELFROM },
+             { "relabelto",        DB_PROCEDURE__RELABELTO },
+             { "execute",        DB_PROCEDURE__EXECUTE },
+             { "entrypoint",        DB_PROCEDURE__ENTRYPOINT },
+             { "install",        DB_PROCEDURE__INSTALL },
+             { NULL, 0UL },
+         }
+     },
+     {
+         { "db_column", SECCLASS_DB_COLUMN},
+         {
+             { "create",            DB_COLUMN__CREATE },
+             { "drop",            DB_COLUMN__DROP },
+             { "getattr",        DB_COLUMN__GETATTR },
+             { "setattr",        DB_COLUMN__SETATTR },
+             { "relabelfrom",    DB_COLUMN__RELABELFROM },
+             { "relabelto",        DB_COLUMN__RELABELTO },
+             { "use",            DB_COLUMN__USE },
+             { "select",            DB_COLUMN__SELECT },
+             { "update",            DB_COLUMN__UPDATE },
+             { "insert",            DB_COLUMN__INSERT },
+             { NULL, 0UL },
+         }
+     },
+     {
+         { "db_tuple", SECCLASS_DB_TUPLE },
+         {
+             { "relabelfrom",    DB_TUPLE__RELABELFROM},
+             { "relabelto",        DB_TUPLE__RELABELTO},
+             { "use",            DB_TUPLE__USE},
+             { "select",            DB_TUPLE__SELECT},
+             { "update",            DB_TUPLE__UPDATE},
+             { "insert",            DB_TUPLE__INSERT},
+             { "delete",            DB_TUPLE__DELETE},
+             { NULL, 0UL},
+         }
+     },
+     {
+         { "db_blob", SECCLASS_DB_BLOB },
+         {
+             { "create",            DB_BLOB__CREATE},
+             { "drop",            DB_BLOB__DROP},
+             { "getattr",        DB_BLOB__GETATTR},
+             { "setattr",        DB_BLOB__SETATTR},
+             { "relabelfrom",    DB_BLOB__RELABELFROM},
+             { "relabelto",        DB_BLOB__RELABELTO},
+             { "read",            DB_BLOB__READ},
+             { "write",            DB_BLOB__WRITE},
+             { "import",            DB_BLOB__IMPORT},
+             { "export",            DB_BLOB__EXPORT},
+             { NULL, 0UL},
+         }
+     },
+ };
+
+ static MemoryContext AvcMemCtx;
+
+ #define AVC_HASH_NUM_SLOTS        256
+ #define AVC_HASH_NUM_NODES        600
+
+ static sig_atomic_t avc_version;
+ static bool avc_enforcing;
+
+ typedef struct
+ {
+     uint32    hash_key;
+
+     security_class_t tclass;
+     Oid        tsid;    /* target security context */
+
+     security_context_t ncontext;    /* newcontext */
+     Oid        nsid;    /* newly created security context */
+
+     access_vector_t allowed;
+     access_vector_t decided;
+     access_vector_t auditallow;
+     access_vector_t auditdeny;
+
+     bool    hot_cache;
+ } avc_datum;
+
+ typedef struct avc_page
+ {
+     struct avc_page *prev;
+     struct avc_page *next;
+
+     security_context_t    scontext;
+
+     List *slot[AVC_HASH_NUM_SLOTS];
+
+     uint32 lru_hint;
+ } avc_page;
+
+ static avc_page *current_avc_page = NULL;
+ static uint32 avc_datum_count = 0;
+
+ /*
+  * selinux_state
+  *
+  * This structure shows the global state of SELinux and its security
+  * policy, and it is assigned on shared memory region.
+  *
+  * The most significant variable is selinux_state->version.
+  * Any instance can refer this variable to confirm current sequence
+  * number of policy state, without locking.
+  *
+  * The only process able to update this variable is policy state
+  * monitoring process forked by postmaster. It can receive notifications
+  * from the kernel via netlink socket, and it update selinux_state->version
+  * to encourage any instance to reflush its uAVC.
+  *
+  * When we read rest of variable, we have to hold SepgsqlAvcLock LWlock
+  * as a reader. enforceing shows the current SELinux working mode.
+  * catalog shows the mapping set of security classes and access vectors.
+  */
+ struct
+ {
+     /*
+      * only state monitoring process can update version.
+      * any other process can read it without locks.
+      */
+     volatile sig_atomic_t version;
+
+     bool        enforcing;
+
+     struct
+     {
+         struct
+         {
+             security_class_t internal;
+             security_class_t external;
+         } tclass;
+         struct
+         {
+             access_vector_t internal;
+             access_vector_t external;
+         } av_perms[sizeof(access_vector_t) * 8];
+     } catalog[lengthof(selinux_catalog)];
+ }    *selinux_state = NULL;
+
+ Size
+ sepgsqlShmemSize(void)
+ {
+     return sizeof(*selinux_state);
+ }
+
+ /*
+  * load_class_av_mapping
+  *
+  * This function rebuild the mapping set of security classes and access
+  * vectors on selinux_state. It has to be invoked by the policy state
+  * monitoring process with SepgsqlAvcLock in LW_EXCLUSIVE.
+  */
+ static void
+ load_class_av_mapping(void)
+ {
+     int            i, j;
+
+     memset(selinux_state->catalog, 0, sizeof(selinux_state->catalog));
+
+     for (i = 0; i < lengthof(selinux_catalog); i++)
+     {
+         selinux_state->catalog[i].tclass.internal
+             = selinux_catalog[i].tclass.internal;
+         selinux_state->catalog[i].tclass.external
+             = string_to_security_class(selinux_catalog[i].tclass.name);
+
+         for (j = 0; selinux_catalog[i].av_perms[j].name; j++)
+         {
+             selinux_state->catalog[i].av_perms[j].internal
+                 = selinux_catalog[i].av_perms[j].internal;
+             selinux_state->catalog[i].av_perms[j].external
+                 = string_to_av_perm(selinux_state->catalog[i].tclass.external,
+                                     selinux_catalog[i].av_perms[j].name);
+         }
+     }
+ }
+
+ /*
+  * trans_to_external_tclass
+  *   translates internal object class number into external one
+  *   needed to communicate with in-kernel SELinux.
+  */
+ static security_class_t
+ trans_to_external_tclass(security_class_t i_tclass)
+ {
+     /* have to hold SepgsqlAvcLock with LW_SHARED */
+     int            i;
+
+     for (i = 0; i < lengthof(selinux_catalog); i++)
+     {
+         if (selinux_state->catalog[i].tclass.internal == i_tclass)
+             return selinux_state->catalog[i].tclass.external;
+     }
+     return i_tclass;            /* use it as is for kernel classes */
+ }
+
+ /*
+  * trans_to_internal_perms
+  *   translates external permission bits into internal ones
+  *   needed to understand the answer from in-kernel SELinux.
+  *   If in-kernel SELinux doesn't define required permissions,
+  *   it sets/clears undefined bits based on caller's preference.
+  *   It enables SE-PostgreSQL to work on legacy security policy.
+  */
+ static access_vector_t
+ trans_to_internal_perms(security_class_t e_tclass, access_vector_t e_perms,
+                         bool set_if_undefined)
+ {
+     /* have to hold SepgsqlAvcLock with LW_SHARED */
+     access_vector_t i_perms = 0UL;
+     access_vector_t undef_mask = 0UL;
+     int            i, j;
+
+     for (i = 0; i < lengthof(selinux_catalog); i++)
+     {
+         if (selinux_state->catalog[i].tclass.external != e_tclass)
+             continue;
+
+         for (j = 0; j < sizeof(access_vector_t) * 8; j++)
+         {
+             if (selinux_state->catalog[i].av_perms[j].external == 0)
+                 undef_mask |= (1UL << j);
+             else if (selinux_state->catalog[i].av_perms[j].external & e_perms)
+                 i_perms |= selinux_state->catalog[i].av_perms[j].internal;
+         }
+
+         if (set_if_undefined)
+             i_perms |= undef_mask;
+         else
+             i_perms &= ~undef_mask;
+
+         return i_perms;
+     }
+     return e_perms;                /* use it as is for kernel classes */
+ }
+
+ /*
+  * sepgsql_class_to_string
+  * sepgsql_av_perm_to_string
+  *   returns string representation of given object class and permission.
+  *   Please note that given code have internal ones, so we cannot use
+  *   libselinux's facility, because it assumes 'external code'.
+  *   (Kernel object classes are ABI, so these are stable.)
+  */
+ static const char *
+ sepgsql_class_to_string(security_class_t tclass)
+ {
+     int            i;
+
+     for (i = 0; i < lengthof(selinux_catalog); i++)
+     {
+         if (selinux_catalog[i].tclass.internal == tclass)
+             return selinux_catalog[i].tclass.name;
+     }
+     /*
+      * tclass is stable for kernel object classes.
+      */
+     return security_class_to_string(tclass);
+ }
+
+ static const char *
+ sepgsql_av_perm_to_string(security_class_t tclass, access_vector_t perm)
+ {
+     int            i, j;
+
+     for (i = 0; i < lengthof(selinux_catalog); i++)
+     {
+         if (selinux_catalog[i].tclass.internal == tclass)
+         {
+             char       *perm_name;
+
+             for (j = 0; (perm_name = selinux_catalog[i].av_perms[j].name); j++)
+             {
+                 if (selinux_catalog[i].av_perms[j].internal == perm)
+                     return perm_name;
+             }
+             return "unknown";
+         }
+     }
+     /*
+      * tclass/perms are stable for kernel object classes.
+      */
+     return security_av_perm_to_string(tclass, perm);
+ }
+
+ /*
+  * sepgsql_avc_reset
+  *   clears all uAVC entries and update its version.
+  */
+ static void
+ sepgsql_avc_reset(void)
+ {
+     MemoryContextReset(AvcMemCtx);
+
+     LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+
+     avc_version = selinux_state->version;
+     switch (sepostgresql_mode)
+     {
+     case SEPGSQL_MODE_DEFAULT:
+         avc_enforcing = selinux_state->enforcing;
+         break;
+     case SEPGSQL_MODE_PERMISSIVE:
+         avc_enforcing = false;
+         break;
+     case SEPGSQL_MODE_ENFORCING:
+         avc_enforcing = false;
+         break;
+     default:
+         elog(FATAL, "SELinux: undefined state in SE-PostgreSQL");
+         break;
+     }
+
+     current_avc_page = NULL;
+
+     avc_datum_count = 0;
+
+     LWLockRelease(SepgsqlAvcLock);
+
+     sepgsqlAvcSwitchClientContext(sepgsqlGetClientContext());
+ }
+
+ /*
+  * sepgsql_avc_reclaim
+  *   reclaims recently unused uAVC entries, when the number of
+  *   caches overs AVC_HASH_NUM_NODES.
+  */
+ static void
+ sepgsql_avc_reclaim(void)
+ {
+     ListCell *l;
+     avc_page *avp;
+     avc_datum *cache;
+     int loop;
+
+     Assert(current_avc_page != NULL);
+
+     for (avp = current_avc_page->next; true; avp = avp->next)
+     {
+         for (loop = 0; loop < AVC_HASH_NUM_SLOTS; loop++)
+         {
+             if (avc_datum_count < AVC_HASH_NUM_NODES)
+                 return;
+
+             avp->lru_hint = (avp->lru_hint + 1) % AVC_HASH_NUM_SLOTS;
+             foreach (l, avp->slot[avp->lru_hint])
+             {
+                 cache = lfirst(l);
+
+                 if (cache->hot_cache)
+                 {
+                     cache->hot_cache = false;
+                     continue;
+                 }
+
+                 list_delete_ptr(avp->slot[avp->lru_hint], cache);
+                 pfree(cache);
+                 avc_datum_count--;
+             }
+         }
+     }
+ }
+
+ /*
+  * avc_audit_common
+  *   generates an audit message on the give string buffer based on
+  *   the given av_decision which means the resutl of permission checks.
+  */
+ static bool
+ avc_audit_common(char *buffer, uint32 buflen,
+                  avc_datum *cache, access_vector_t perms,
+                  const char *scontext, const char *tcontext, const char *objname)
+ {
+     access_vector_t denied, audited, mask;
+     security_context_t svcon, tvcon;
+     uint32 ofs = 0;
+
+     denied = perms & ~cache->allowed;
+     audited = denied ? (denied & cache->auditdeny) : (perms & cache->auditallow);
+
+     if (audited == 0)
+         return false;
+
+     ofs += snprintf(buffer + ofs, buflen - ofs, "%s {",
+                     denied ? "denied" : "granted");
+     for (mask = 1; mask != 0; mask <<= 1)
+     {
+         if ((audited & mask) != 0)
+             ofs += snprintf(buffer + ofs, buflen - ofs, " %s",
+                             sepgsql_av_perm_to_string(cache->tclass, mask));
+     }
+     ofs += snprintf(buffer + ofs, buflen - ofs, " } ");
+
+     if (!scontext)
+         svcon = sepgsqlTranslateSecurityLabelOut(current_avc_page->scontext);
+     else
+         svcon = sepgsqlTranslateSecurityLabelOut(scontext);
+
+     if (!tcontext)
+         tvcon = pgaceSidToSecurityLabel(cache->tsid);
+     else
+         tvcon = sepgsqlTranslateSecurityLabelOut(tcontext);
+
+     ofs += snprintf(buffer + ofs, buflen - ofs,
+                     "scontext=%s tcontext=%s tclass=%s",
+                     svcon, tvcon, sepgsql_class_to_string(cache->tclass));
+
+     pfree(svcon);
+     pfree(tvcon);
+     if (objname)
+         ofs += snprintf(buffer + ofs, buflen - ofs, " name=%s", objname);
+
+     return true;
+ }
+
+ /*
+  * avc_permission_common
+  *   makes decision and output audit messages based on given avc_datum.
+  *   If required permissions are not completely allowed, it raises an
+  *   error or returns 'false' when permissive mode.
+  */
+ static bool
+ avc_permission_common(avc_datum *cache, access_vector_t perms, bool abort,
+                       const char *scontext, const char *tcontext, const char *objname)
+ {
+     char audit_buffer[2048];
+     access_vector_t denied;
+     bool audit;
+     bool rc = true;
+
+     audit = avc_audit_common(audit_buffer, sizeof(audit_buffer),
+                              cache, perms, scontext, tcontext, objname);
+
+     denied = perms & ~cache->allowed;
+     if (!perms || denied)
+     {
+         if (avc_enforcing)
+             rc = false;
+         else
+         {
+             /*
+              * In permissive mode, once denied permissions are
+              * allowed to avoid a flood of denied logs.
+              */
+             cache->allowed |= perms;
+         }
+     }
+
+     if (audit)
+     {
+         ereport((!rc && abort) ? ERROR : NOTICE,
+                 (errcode(ERRCODE_SELINUX_AUDIT),
+                  errmsg("SELinux: %s", audit_buffer)));
+     }
+     else if (!rc && abort)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_AUDIT),
+                  errmsg("SELinux: security policy violation")));
+
+     return rc;
+ }
+
+ /*
+  * avc_make_entry
+  *   makes a query to in-kernel SELinux and an avc_datum object to
+  *   cache the result of SELinux's decision for access rights and
+  *   default security context.
+  */
+ #define avc_hash_key(tsid,tclass)    ((tsid) ^ ((tclass) << 2))
+
+ static avc_datum *
+ avc_make_entry(Oid tsid, security_class_t tclass)
+ {
+     security_context_t scontext, tcontext, ncontext;
+     security_class_t e_tclass;
+     MemoryContext oldctx = MemoryContextSwitchTo(AvcMemCtx);
+     struct av_decision avd;
+     avc_datum *cache;
+     uint32 hash_key, index;
+
+     hash_key = avc_hash_key(tsid, tclass);
+     index = hash_key % AVC_HASH_NUM_SLOTS;
+
+     cache = palloc0(sizeof(avc_datum));
+     cache->hash_key = hash_key;
+     cache->tsid = tsid;
+     cache->tclass = tclass;
+
+     scontext = current_avc_page->scontext;
+     tcontext = pgaceLookupSecurityLabel(tsid);
+     if (!tcontext || !pgaceCheckValidSecurityLabel(tcontext))
+         tcontext = pgaceUnlabeledSecurityLabel();
+
+     LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+
+     e_tclass = trans_to_external_tclass(tclass);
+
+     if (security_compute_av_raw(scontext, tcontext, e_tclass, 0, &avd) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not compute av_decision: "
+                         "scontext=%s tcontext=%s tclass=%s",
+                         scontext, tcontext, sepgsql_class_to_string(tclass))));
+
+     cache->allowed = trans_to_internal_perms(e_tclass, avd.allowed, true);
+     cache->decided = trans_to_internal_perms(e_tclass, avd.decided, false);
+     cache->auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false);
+     cache->auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false);
+     cache->hot_cache = true;
+
+     if (security_compute_create_raw(scontext, tcontext, e_tclass, &ncontext) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not compute new context: "
+                         "scontext=%s tcontext=%s tclass=%s",
+                         scontext, tcontext, sepgsql_class_to_string(tclass))));
+     pfree(tcontext);
+
+     LWLockRelease(SepgsqlAvcLock);
+
+     PG_TRY();
+     {
+         cache->ncontext = pstrdup(ncontext);
+     }
+     PG_CATCH();
+     {
+         freecon(ncontext);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+
+     freecon(ncontext);
+
+     sepgsql_avc_reclaim();
+
+     current_avc_page->slot[index]
+         = lcons(cache, current_avc_page->slot[index]);
+
+     avc_datum_count++;
+
+     MemoryContextSwitchTo(oldctx);
+
+     return cache;
+ }
+
+ static avc_datum *avc_lookup(Oid tsid, security_class_t tclass)
+ {
+     avc_datum *cache = NULL;
+     uint32 hash_key, index;
+     ListCell *l;
+
+     /*
+      * check avc invalidation
+      */
+     if (avc_version != selinux_state->version)
+         sepgsql_avc_reset();
+
+     /*
+      * lookup avc entry
+      */
+     hash_key = avc_hash_key(tsid, tclass);
+     index = hash_key % AVC_HASH_NUM_SLOTS;
+
+     foreach (l, current_avc_page->slot[index])
+     {
+         cache = lfirst(l);
+         if (cache->hash_key == hash_key
+             && cache->tclass == tclass
+             && cache->tsid == tsid)
+         {
+             cache->hot_cache = true;
+             return cache;
+         }
+     }
+     return NULL;
+ }
+
+ /*
+  * sepgsqlAvcSwitchClientContext()
+  *   switches current avc_page.
+  *
+  * NOTE: In most cases, SE-PostgreSQL checks whether client is allowed
+  * to do required actions (like SELECT, UPDATE, ...) on the targets.
+  * Both of client and targets have its security context, and all rules
+  * are described as relationship between security context of a client,
+  * a target and kind of actions.
+  * However, the security context of client is unchanged in SE-PostgreSQL
+  * (an exception is invocation of trusted procedure), so we can omit
+  * to compare security context of client with entries of uAVC.
+  * The avc_page is a set of avc_datum sorted out by the security context
+  * of client, so we can lookup correct avc_datum on currently focued
+  * avc_page without comparing the security context of client.
+  * The reason why we don't not use a unique uAVC is the security context
+  * of client does not have its security identifier on pg_security, so
+  * it requires strcmp() for each entries, but it is heavier than integer
+  * comparisons.
+  * Thus we have to switch current avc_page, whenever the security context
+  * of client changes (via trusted procedure). It makes performance well
+  * in most cases.
+  */
+ void sepgsqlAvcSwitchClientContext(security_context_t newcontext)
+ {
+     MemoryContext oldctx;
+     avc_page *avp;
+     int i;
+
+     if (current_avc_page)
+     {
+         avp = current_avc_page;
+         do {
+             if (!strcmp(avp->scontext, newcontext))
+             {
+                 current_avc_page = avp;
+                 return;
+             }
+             avp = avp->next;
+         } while (avp != current_avc_page);
+     }
+
+     /* create a new avc_page */
+     oldctx = MemoryContextSwitchTo(AvcMemCtx);
+     avp = palloc0(sizeof(avc_page));
+     avp->scontext = pstrdup(newcontext);
+     MemoryContextSwitchTo(oldctx);
+
+     for (i=0; i < AVC_HASH_NUM_SLOTS; i++)
+         avp->slot[i] = NIL;
+
+     if (!current_avc_page)
+     {
+         avp->next = avp->prev = avp;
+     }
+     else
+     {
+         avp->next = current_avc_page;
+         avp->prev = current_avc_page->prev;
+         avp->prev->next = avp;
+         avp->next->prev = avp;
+     }
+     current_avc_page = avp;
+ }
+
+ /*
+  * sepgsqlClientHasPermission
+  *   checks client's privileges on given objects via uAVC.
+  *   It raised an error, if required actions are violated.
+  */
+ void
+ sepgsqlClientHasPermission(Oid tsid, security_class_t tclass,
+                            access_vector_t perms,
+                            const char *objname)
+ {
+     avc_datum *cache = avc_lookup(tsid, tclass);
+
+     if (!cache)
+         cache = avc_make_entry(tsid, tclass);
+
+     avc_permission_common(cache, perms, true, NULL, NULL, objname);
+ }
+
+ /*
+  * sepgsqlClientHasPermissionNoAbort
+  *   checks client's privileges on given objects via uAVC.
+  *   It returns false, if required actions are violated.
+  */
+ bool
+ sepgsqlClientHasPermissionNoAbort(Oid tsid, security_class_t tclass,
+                                   access_vector_t perms,
+                                   const char *objname)
+ {
+     avc_datum *cache = avc_lookup(tsid, tclass);
+
+     if (!cache)
+         cache = avc_make_entry(tsid, tclass);
+
+     return avc_permission_common(cache, perms, false, NULL, NULL, objname);
+ }
+
+ /*
+  * sepgsqlClientCreateSid
+  *   returns security identifier of newly created database object.
+  *   Please note that you don't have to invoke this function for
+  *   object classes except for database objects. It have a possibility
+  *   to make an entry on pg_security via pgaceSecurityLabelToSid(),
+  *   but it should be restricted to database object.
+  */
+ Oid
+ sepgsqlClientCreateSid(Oid tsid, security_class_t tclass)
+ {
+     avc_datum *cache = avc_lookup(tsid, tclass);
+
+     if (!cache || cache->nsid == InvalidOid)
+     {
+         if (!cache)
+             cache = avc_make_entry(tsid, tclass);
+         cache->nsid = pgaceSecurityLabelToSid(cache->ncontext);
+     }
+     return cache->nsid;
+ }
+
+ /*
+  * sepgsqlClientCreateContext
+  *   returns security context (string representation) of newly
+  *   created object. It is available for any kind of object
+  *   classes.
+  */
+ security_context_t
+ sepgsqlClientCreateContext(Oid tsid, security_class_t tclass)
+ {
+     avc_datum *cache = avc_lookup(tsid, tclass);
+
+     if (!cache)
+         cache = avc_make_entry(tsid, tclass);
+
+     return pstrdup(cache->ncontext);
+ }
+
+ /*
+  * sepgsql_shmem_init
+  *   attaches shared memory segment.
+  */
+ static void
+ sepgsql_shmem_init(void)
+ {
+     bool found;
+
+     selinux_state = ShmemInitStruct("SELinux policy state",
+                                     sepgsqlShmemSize(), &found);
+     if (!found)
+     {
+         int            enforcing = security_getenforce();
+
+         Assert(enforcing == 0 || enforcing == 1);
+
+         LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+         selinux_state->version = 0;
+         selinux_state->enforcing = enforcing;
+         load_class_av_mapping();
+
+         LWLockRelease(SepgsqlAvcLock);
+     }
+ }
+
+ /*
+  * sepgsqlAvcInit
+  *   initialize local uAVC facility.
+  */
+ void
+ sepgsqlAvcInit(void)
+ {
+     /*
+      * local memory context
+      */
+     AvcMemCtx = AllocSetContextCreate(TopMemoryContext,
+                                       "SE-PostgreSQL userspace avc",
+                                       ALLOCSET_DEFAULT_MINSIZE,
+                                       ALLOCSET_DEFAULT_INITSIZE,
+                                       ALLOCSET_DEFAULT_MAXSIZE);
+     sepgsql_shmem_init();
+
+     /*
+      * reset local avc
+      */
+     sepgsql_avc_reset();
+ }
+
+ /*
+  * sepgsqlComputePermission
+  * sepgsqlComputeCreateContext
+  *
+  * The following two functions make a query to in-kernel SELinux
+  * without userspace caches, due to some reasons.
+  * The uAVC can cover most of cases, but some of corner cases are
+  * not suitable for uAVC structure, so we need uncached interfaces.
+  * For example, uAVC is unavailable when we tries to load a shared
+  * library module, because security context of the library does not
+  * have its security identifier, so we cannot put it on uAVC.
+  */
+ bool
+ sepgsqlComputePermission(const security_context_t scontext,
+                          const security_context_t tcontext,
+                          security_class_t tclass,
+                          access_vector_t perms,
+                          const char *objname)
+ {
+     security_context_t svcon, tvcon;
+     security_class_t e_tclass;
+     struct av_decision avd;
+     avc_datum cache;
+     bool rc;
+
+     svcon = (!security_check_context_raw(scontext)
+              ? scontext : sepgsqlGetUnlabeledContext());
+     tvcon = (!security_check_context_raw(tcontext)
+              ? tcontext : sepgsqlGetUnlabeledContext());
+
+     LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+     e_tclass = trans_to_external_tclass(tclass);
+
+     if (security_compute_av_raw(svcon, tvcon, e_tclass, 0, &avd) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not compute an av_decision"
+                         " scontext=%s tcontext=%s tclass=%s",
+                         svcon, tvcon, security_class_to_string(e_tclass))));
+
+     cache.tclass = tclass;
+     cache.allowed = trans_to_internal_perms(e_tclass, avd.allowed, true);
+     cache.decided = trans_to_internal_perms(e_tclass, avd.decided, false);
+     cache.auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false);
+     cache.auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false);
+     LWLockRelease(SepgsqlAvcLock);
+
+     rc = avc_permission_common(&cache, perms, true, svcon, tvcon, objname);
+
+     if (svcon != scontext)
+         pfree(svcon);
+     if (tvcon != tcontext)
+         pfree(tvcon);
+
+     return rc;
+ }
+
+ security_context_t
+ sepgsqlComputeCreateContext(const security_context_t scontext,
+                             const security_context_t tcontext,
+                             security_class_t tclass)
+ {
+     security_context_t svcon, tvcon, nwcon, copy;
+     security_class_t e_tclass;
+
+     svcon = (!security_check_context_raw(scontext)
+              ? scontext : sepgsqlGetUnlabeledContext());
+     tvcon = (!security_check_context_raw(tcontext)
+              ? tcontext : sepgsqlGetUnlabeledContext());
+
+     LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+     e_tclass = trans_to_external_tclass(tclass);
+
+     if (security_compute_create_raw(svcon, tvcon, e_tclass, &nwcon) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not compute a default context"
+                         " scontext=%s tcontext=%s tclass=%s",
+                         scontext, tcontext, security_class_to_string(e_tclass))));
+
+     LWLockRelease(SepgsqlAvcLock);
+
+     if (svcon != scontext)
+         pfree(svcon);
+     if (tvcon != tcontext)
+         pfree(tvcon);
+
+     PG_TRY();
+     {
+         copy = pstrdup(nwcon);
+     }
+     PG_CATCH();
+     {
+         freecon(nwcon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+
+     freecon(nwcon);
+
+     return copy;
+ }
+
+ /*
+  * SELinux state monitoring process
+  *
+  * This process is forked from postmaster to monitor the state of SELinux.
+  * SELinux can make a notifier message to userspace object manager via
+  * netlink socket. When it receives the message, it updates selinux_state
+  * structure assigned on shared memory region to make any instance reset
+  * its AVC soon.
+  */
+
+ static bool sepgsqlStateMonitorAlive = true;
+
+ static void
+ sepgsqlStateMonitorSIGHUP(SIGNAL_ARGS)
+ {
+     ereport(NOTICE,
+             (errcode(ERRCODE_SELINUX_INFO),
+              errmsg("SELinux: invalidate userspace avc")));
+     selinux_state->version = selinux_state->version + 1;
+ }
+
+ static int
+ sepgsqlStateMonitorMain()
+ {
+     char        buffer[2048];
+     struct sockaddr_nl addr;
+     socklen_t    addrlen;
+     struct nlmsghdr *nlh;
+     int            rc, nl_sockfd;
+
+     /*
+      * map shared memory segment
+      */
+     sepgsql_shmem_init();
+
+     /*
+      * setup the signal handler
+      */
+     pqinitmask();
+     pqsignal(SIGHUP, sepgsqlStateMonitorSIGHUP);
+     pqsignal(SIGINT, SIG_IGN);
+     pqsignal(SIGTERM, exit);
+     pqsignal(SIGQUIT, exit);
+     pqsignal(SIGUSR1, SIG_IGN);
+     pqsignal(SIGUSR2, SIG_IGN);
+     pqsignal(SIGCHLD, SIG_DFL);
+     PG_SETMASK(&UnBlockSig);
+
+     ereport(NOTICE,
+             (errcode(ERRCODE_SELINUX_INFO),
+              errmsg("SELinux: policy state monitor process (pid: %u)",
+                     getpid())));
+     /*
+      * open netlink socket
+      */
+     nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
+     if (nl_sockfd < 0)
+     {
+         ereport(NOTICE,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not open netlink socket")));
+         return 1;
+     }
+     memset(&addr, 0, sizeof(addr));
+     addr.nl_family = AF_NETLINK;
+     addr.nl_groups = SELNL_GRP_AVC;
+     if (bind(nl_sockfd, (struct sockaddr *) &addr, sizeof(addr)))
+     {
+         ereport(NOTICE,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not bind netlink socket")));
+         return 1;
+     }
+
+     /*
+      * waiting loop
+      */
+     while (sepgsqlStateMonitorAlive)
+     {
+         addrlen = sizeof(addr);
+         rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0,
+                       (struct sockaddr *) &addr, &addrlen);
+         if (rc < 0)
+         {
+             if (errno == EINTR)
+                 continue;
+
+             ereport(NOTICE,
+                     (errcode(ERRCODE_SELINUX_ERROR),
+                      errmsg("SELinux: error on netlink recvfrom(): %s",
+                             strerror(errno))));
+             return 1;
+         }
+
+         if (addrlen != sizeof(addr))
+         {
+             ereport(NOTICE,
+                     (errcode(ERRCODE_SELINUX_ERROR),
+                      errmsg("SELinux: netlink address truncated (len=%d)",
+                             addrlen)));
+             return 1;
+         }
+
+         if (addr.nl_pid)
+         {
+             ereport(NOTICE,
+                     (errcode(ERRCODE_SELINUX_ERROR),
+                      errmsg("SELinux: netlink received spoofed packet from: %u",
+                             addr.nl_pid)));
+             continue;
+         }
+
+         if (rc == 0)
+         {
+             ereport(NOTICE,
+                     (errcode(ERRCODE_SELINUX_ERROR),
+                      errmsg("SELinux: netlink received EOF")));
+             return 1;
+         }
+
+         nlh = (struct nlmsghdr *) buffer;
+         if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned int) rc)
+         {
+             ereport(NOTICE,
+                     (errcode(ERRCODE_SELINUX_ERROR),
+                      errmsg("SELinux: netlink incomplete message")));
+             return 1;
+         }
+
+         switch (nlh->nlmsg_type)
+         {
+             case SELNL_MSG_SETENFORCE:
+                 {
+                     struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+
+                     ereport(NOTICE,
+                             (errcode(ERRCODE_SELINUX_INFO),
+                              errmsg("SELinux: setenforce notifier"
+                                     " (enforcing=%d)", msg->val)));
+
+                     LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+                     load_class_av_mapping();
+
+                     /*
+                      * userspace avc invalidation
+                      */
+                     selinux_state->version = selinux_state->version + 1;
+                     selinux_state->enforcing = msg->val ? true : false;
+
+                     LWLockRelease(SepgsqlAvcLock);
+                     break;
+                 }
+             case SELNL_MSG_POLICYLOAD:
+                 {
+                     struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+
+                     ereport(NOTICE,
+                             (errcode(ERRCODE_SELINUX_INFO),
+                              errmsg("policyload notifier (seqno=%d)",
+                                     msg->seqno)));
+
+                     LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+                     load_class_av_mapping();
+                     /*
+                      * userspace avc invalidation
+                      */
+                     selinux_state->version = selinux_state->version + 1;
+
+                     LWLockRelease(SepgsqlAvcLock);
+                     break;
+                 }
+             case NLMSG_ERROR:
+                 {
+                     struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+                     if (err->error == 0)
+                         break;
+
+                     ereport(NOTICE,
+                             (errcode(ERRCODE_SELINUX_ERROR),
+                              errmsg("SELinux: netlink error: %s",
+                                     strerror(-err->error))));
+                     return 1;
+                 }
+             default:
+                 ereport(NOTICE,
+                         (errcode(ERRCODE_SELINUX_ERROR),
+                          errmsg("netlink unknown message type (%d)",
+                                 nlh->nlmsg_type)));
+                 return 1;
+         }
+     }
+     return 0;
+ }
+
+ pid_t
+ sepgsqlStartupWorkerProcess(void)
+ {
+     pid_t        chld;
+
+     chld = fork();
+     if (chld == 0)
+     {
+         ClosePostmasterPorts(false);
+
+         on_exit_reset();
+
+         exit(sepgsqlStateMonitorMain());
+     }
+     else if (chld > 0)
+         return chld;
+
+     return (pid_t) 0;
+ }
diff -Nrpc base/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c
*** base/src/backend/security/sepgsql/core.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/sepgsql/core.c    Sat Jan 24 22:42:56 2009
***************
*** 0 ****
--- 1,623 ----
+ /*
+  * src/backend/security/sepgsql/core.c
+  *     SE-PostgreSQL core facilities
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "catalog/pg_database.h"
+ #include "catalog/pg_security.h"
+ #include "libpq/libpq.h"
+ #include "miscadmin.h"
+ #include "security/pgace.h"
+ #include "utils/builtins.h"
+ #include "utils/syscache.h"
+ #include <selinux/context.h>
+
+ int sepostgresql_mode;
+
+ static security_context_t serverContext = NULL;
+ static security_context_t clientContext = NULL;
+ static security_context_t unlabeledContext = NULL;
+
+ const security_context_t
+ sepgsqlGetServerContext(void)
+ {
+     Assert(serverContext != NULL);
+     return serverContext;
+ }
+
+ const security_context_t
+ sepgsqlGetClientContext(void)
+ {
+     Assert(clientContext != NULL);
+     return clientContext;
+ }
+
+ const security_context_t
+ sepgsqlGetDatabaseContext(void)
+ {
+     security_context_t result;
+
+     if (IsBootstrapProcessingMode())
+     {
+         static security_context_t dbcontext = NULL;
+
+         if (!dbcontext)
+         {
+             if (security_compute_create_raw(sepgsqlGetClientContext(),
+                                             sepgsqlGetClientContext(),
+                                             SECCLASS_DB_DATABASE,
+                                             &dbcontext) < 0)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SELINUX_ERROR),
+                          errmsg("SELinux: could not get database context")));
+         }
+         result = pstrdup(dbcontext);
+     }
+     else
+     {
+         HeapTuple tuple;
+
+         tuple = SearchSysCache(DATABASEOID,
+                                ObjectIdGetDatum(MyDatabaseId),
+                                0, 0, 0);
+         if (!HeapTupleIsValid(tuple))
+             elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId);
+
+         result = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple));
+         if (!result || !pgaceCheckValidSecurityLabel(result))
+             result = pgaceUnlabeledSecurityLabel();
+
+         ReleaseSysCache(tuple);
+     }
+
+     return result;
+ }
+
+ Oid
+ sepgsqlGetDatabaseSecurityId(void)
+ {
+     Oid sid;
+
+     if (IsBootstrapProcessingMode())
+     {
+         security_context_t dcontext
+             = sepgsqlGetDatabaseContext();
+
+         sid = pgaceSecurityLabelToSid(dcontext);
+     }
+     else
+     {
+         HeapTuple tuple;
+
+         tuple = SearchSysCache(DATABASEOID,
+                                ObjectIdGetDatum(MyDatabaseId),
+                                0, 0, 0);
+         if (!HeapTupleIsValid(tuple))
+             elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId);
+
+         sid = HeapTupleGetSecLabel(tuple);
+
+         ReleaseSysCache(tuple);
+     }
+
+     return sid;
+ }
+
+ const security_context_t
+ sepgsqlGetUnlabeledContext(void)
+ {
+     if (unlabeledContext)
+         return unlabeledContext;
+
+     if (security_get_initial_context_raw("unlabeled", &unlabeledContext) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not get unlabeled context")));
+
+     return unlabeledContext;
+ }
+
+ const security_context_t
+ sepgsqlSwitchClientContext(security_context_t new_context)
+ {
+     security_context_t original_context = clientContext;
+
+     clientContext = new_context;
+
+     sepgsqlAvcSwitchClientContext(new_context);
+
+     return original_context;
+ }
+
+ static void
+ initContexts(void)
+ {
+     /*
+      * server context
+      */
+     if (getcon_raw(&serverContext) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not get server process context")));
+
+     /*
+      * client context
+      */
+     if (!MyProcPort)
+     {
+         /*
+          * When the proces is not invoked as a backend of clietnt,
+          * it works as a server process and as a client process
+          * in same time.
+          */
+         clientContext = serverContext;
+     }
+     else
+     {
+         if (getpeercon_raw(MyProcPort->sock, &clientContext) < 0)
+         {
+             /*
+              * fallbacked security context
+              *
+              * When getpeercon() API does not obtain the context of
+              * peer process, SEPGSQL_FALLBACK_CONTEXT environment
+              * variable is used as an alternative security context
+              * of the peer.
+              *
+              * getpeercon() needs the following condition to fail:
+              * - Connection come from remote host,
+              * - and, there is no labeled ipsec configuration between
+              *   localhost and remote host.
+              * - and, there is no static fallbacked context configuration
+              *   for the remote host.
+              */
+             char       *fallback = getenv("SEPGSQL_FALLBACK_CONTEXT");
+
+             if (!fallback)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SELINUX_ERROR),
+                          errmsg
+                          ("SELinux: could not get client process context")));
+
+             if (security_check_context(fallback) < 0
+                 || selinux_trans_to_raw_context(fallback, &clientContext) < 0)
+                 ereport(ERROR,
+                         (errcode(ERRCODE_SELINUX_ERROR),
+                          errmsg("SELinux: %s is not a valid context",
+                                 fallback)));
+         }
+     }
+ }
+
+ /*
+  * sepgsqlInitialize
+  *
+  * It initializes SE-PostgreSQL itself including assignment of shared
+  * memory segment, reset of AVC, obtaining the client/server security
+  * context and checks whether the client can access the required database,
+  * or not.
+  */
+ void
+ sepgsqlInitialize(bool bootstrap)
+ {
+     char       *dbname;
+
+     initContexts();
+
+     sepgsqlAvcInit();
+
+     /*
+      * check db_database:{ access }
+      */
+     if (IsBootstrapProcessingMode())
+         dbname = "template1";
+     else
+     {
+         Form_pg_database dbForm;
+         HeapTuple    tuple;
+
+         tuple = SearchSysCache(DATABASEOID,
+                                ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+         if (!HeapTupleIsValid(tuple))
+             elog(ERROR, "SELinux: cache lookup failed for database %u",
+                  MyDatabaseId);
+         dbForm = (Form_pg_database) GETSTRUCT(tuple);
+
+         dbname = pstrdup(NameStr(dbForm->datname));
+
+         ReleaseSysCache(tuple);
+     }
+
+     sepgsqlComputePermission(sepgsqlGetClientContext(),
+                              sepgsqlGetDatabaseContext(),
+                              SECCLASS_DB_DATABASE,
+                              DB_DATABASE__ACCESS,
+                              dbname);
+ }
+
+ /*
+  * sepgsqlIsEnabled
+  *
+  * This function returns the state of SE-PostgreSQL when PGACE hooks
+  * are invoked, to prevent to call sepgsqlXXXX() functions when
+  * SE-PostgreSQL is disabled.
+  *
+  * We can config the state of SE-PostgreSQL in $PGDATA/postgresql.conf.
+  * The GUC option "sepostgresql" can have the following four parameter.
+  *
+  * - default    : It always follows the in-kernel SELinux state. When it
+  *                works in Enforcing mode, SE-PostgreSQL also works in
+  *                Enforcing mode. Changes of in-kernel state are delivered
+  *                to userspace SE-PostgreSQL soon, and SELinux state
+  *                monitoring process updates it rapidly.
+  * - enforcing  : It always works in Enforcing mode. In-kernel SELinux
+  *                has to be enabled.
+  * - permissive : It always works in Permissive mode. In-kernel SELinux
+  *                has to be enabled.
+  * - disabled   : It disables SE-PostgreSQL feature. It works as if
+  *                original PostgreSQL
+  */
+ bool
+ sepgsqlIsEnabled(void)
+ {
+     static int    enabled = -1;
+
+     if (enabled < 0)
+     {
+         if (sepostgresql_mode == SEPGSQL_MODE_DISABLED)
+             enabled = 0;
+         else
+         {
+             enabled = is_selinux_enabled();
+             if (enabled == 0    /* in-kernel SELinux is disabled */
+                 && sepostgresql_mode != SEPGSQL_MODE_DEFAULT)
+             {
+                 ereport(FATAL,
+                         (errcode(ERRCODE_SELINUX_ERROR),
+                          errmsg("SELinux: disabled in kernel, but sepostgresql = %s",
+                                 SEPGSQL_MODE_ENFORCING ? "enforcing" : "permissive")));
+             }
+         }
+     }
+
+     return enabled > 0 ? true : false;
+ }
+
+ /*
+  * sepgsql_getcon(void)
+  *
+  * It returns security context of client
+  */
+ Datum
+ sepgsql_getcon(PG_FUNCTION_ARGS)
+ {
+     security_context_t context;
+     Datum        labelTxt;
+
+     if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled())
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: disabled now")));
+
+     if (selinux_raw_to_trans_context(clientContext, &context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not translate mls label")));
+     PG_TRY();
+     {
+         labelTxt = CStringGetTextDatum(context);
+     }
+     PG_CATCH();
+     {
+         freecon(context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(context);
+
+     PG_RETURN_DATUM(labelTxt);
+ }
+
+ /*
+  * sepgsql_getcon(void)
+  *
+  * It returns security context of server process
+  */
+ Datum
+ sepgsql_getservcon(PG_FUNCTION_ARGS)
+ {
+     security_context_t context;
+     Datum        labelTxt;
+
+     if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled())
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: disabled now")));
+
+     if (selinux_raw_to_trans_context(serverContext, &context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not translate mls label")));
+     PG_TRY();
+     {
+         labelTxt = CStringGetTextDatum(context);
+     }
+     PG_CATCH();
+     {
+         freecon(context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(context);
+
+     PG_RETURN_DATUM(labelTxt);
+ }
+
+ static void
+ parse_to_context(security_context_t context,
+                  char **user, char **role, char **type, char **range)
+ {
+     security_context_t raw_context;
+
+     if (pgace_feature != PGACE_FEATURE_SELINUX || !sepgsqlIsEnabled())
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: disabled now")));
+
+     if (selinux_trans_to_raw_context(context, &raw_context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not translate mls label")));
+     PG_TRY();
+     {
+         char       *tmp;
+
+         tmp = pstrdup(strtok(raw_context, ":"));
+         if (user)
+             *user = tmp;
+         tmp = pstrdup(strtok(NULL, ":"));
+         if (role)
+             *role = tmp;
+         tmp = pstrdup(strtok(NULL, ":"));
+         if (type)
+             *type = tmp;
+         if (is_selinux_mls_enabled())
+         {
+             tmp = pstrdup(strtok(NULL, "\0"));
+             if (range)
+                 *range = tmp;
+         }
+         else if (range)
+             *range = NULL;
+     }
+     PG_CATCH();
+     {
+         freecon(raw_context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(raw_context);
+ }
+
+ /*
+  * text sepgsql_get_user(text)
+  *
+  * It picks up the USER field of given security context.
+  */
+ Datum
+ sepgsql_get_user(PG_FUNCTION_ARGS)
+ {
+     char       *user;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      &user, NULL, NULL, NULL);
+     PG_RETURN_TEXT_P(CStringGetTextDatum(user));
+ }
+
+ /*
+  * text sepgsql_set_user(text, text)
+  *
+  * It replaces the USER field of given security context by the second argument.
+  */
+ Datum
+ sepgsql_set_user(PG_FUNCTION_ARGS)
+ {
+     char       *user, *role, *type, *range;
+     char        buffer[1024];
+     security_context_t newcon;
+     Datum        result;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      &user, &role, &type, &range);
+     if (range)
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+                  TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type, range);
+     else
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+                  TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type);
+     if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not set a new user")));
+     PG_TRY();
+     {
+         result = CStringGetTextDatum(newcon);
+     }
+     PG_CATCH();
+     {
+         freecon(newcon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(newcon);
+
+     PG_RETURN_DATUM(result);
+ }
+
+ /*
+  * text sepgsql_get_role(text)
+  *
+  * It picks up the ROLE field of given security context.
+  */
+ Datum
+ sepgsql_get_role(PG_FUNCTION_ARGS)
+ {
+     char       *role;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      NULL, &role, NULL, NULL);
+     PG_RETURN_TEXT_P(CStringGetTextDatum(role));
+ }
+
+ /*
+  * text sepgsql_set_user(text, text)
+  *
+  * It replaces the ROLE field of given security context by the second argument.
+  */
+ Datum
+ sepgsql_set_role(PG_FUNCTION_ARGS)
+ {
+     char       *user, *role, *type, *range;
+     char        buffer[1024];
+     security_context_t newcon;
+     Datum        result;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      &user, &role, &type, &range);
+     if (range)
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+                  user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type, range);
+     else
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+                  user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type);
+     if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not set a new role")));
+     PG_TRY();
+     {
+         result = CStringGetTextDatum(newcon);
+     }
+     PG_CATCH();
+     {
+         freecon(newcon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(newcon);
+
+     PG_RETURN_DATUM(result);
+ }
+
+ /*
+  * text sepgsql_get_type(text)
+  *
+  * It picks up the TYPE field of given security context.
+  */
+ Datum
+ sepgsql_get_type(PG_FUNCTION_ARGS)
+ {
+     char       *type;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      NULL, NULL, &type, NULL);
+     PG_RETURN_TEXT_P(CStringGetTextDatum(type));
+ }
+
+ /*
+  * text sepgsql_set_user(text, text)
+  *
+  * It replaces the TYPE field of given security context by the second argument.
+  */
+ Datum
+ sepgsql_set_type(PG_FUNCTION_ARGS)
+ {
+     char       *user, *role, *type, *range;
+     char        buffer[1024];
+     security_context_t newcon;
+     Datum        result;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      &user, &role, &type, &range);
+     if (range)
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+                  user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)), range);
+     else
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+                  user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)));
+     if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not set a new type")));
+     PG_TRY();
+     {
+         result = CStringGetTextDatum(newcon);
+     }
+     PG_CATCH();
+     {
+         freecon(newcon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(newcon);
+
+     PG_RETURN_DATUM(result);
+ }
+
+ /*
+  * text sepgsql_get_range(text)
+  *
+  * It picks up the RANGE field of given security context.
+  */
+ Datum
+ sepgsql_get_range(PG_FUNCTION_ARGS)
+ {
+     char       *range;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      NULL, NULL, NULL, &range);
+     PG_RETURN_TEXT_P(CStringGetTextDatum(range));
+ }
+
+ /*
+  * text sepgsql_set_user(text, text)
+  *
+  * It replaces the RANGE field of given security context by the second argument.
+  */
+ Datum
+ sepgsql_set_range(PG_FUNCTION_ARGS)
+ {
+     char       *user, *role, *type, *range;
+     char        buffer[1024];
+     security_context_t newcon;
+     Datum        result;
+
+     parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+                      &user, &role, &type, &range);
+     if (range)
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+                  user, role, type, TextDatumGetCString(PG_GETARG_TEXT_P(1)));
+     else
+         snprintf(buffer, sizeof(buffer), "%s:%s:%s", user, role, type);
+     if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not set a new range")));
+     PG_TRY();
+     {
+         result = CStringGetTextDatum(newcon);
+     }
+     PG_CATCH();
+     {
+         freecon(newcon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(newcon);
+
+     PG_RETURN_DATUM(result);
+ }
diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c
*** base/src/backend/security/sepgsql/hooks.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/sepgsql/hooks.c    Thu Jan 22 10:39:45 2009
***************
*** 0 ****
--- 1,1019 ----
+ /*
+  * src/backend/security/sepgsql/hooks.c
+  *      Implementations of PGACE framework in SE-PostgreSQL
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "access/genam.h"
+ #include "access/skey.h"
+ #include "access/sysattr.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_aggregate.h"
+ #include "catalog/pg_database.h"
+ #include "catalog/pg_largeobject.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_security.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "security/pgace.h"
+ #include "storage/bufmgr.h"
+ #include "utils/fmgroids.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+
+ /*******************************************************************************
+  * Extended SQL statement hooks
+  *******************************************************************************/
+ bool
+ sepgsqlIsGramSecurityItem(DefElem *defel)
+ {
+     Assert(IsA(defel, DefElem));
+
+     if (defel->defname &&
+         strcmp(defel->defname, SecurityLabelAttributeName) == 0)
+         return true;
+
+     return false;
+ }
+
+ static void
+ putExplicitContext(HeapTuple tuple, DefElem *defel)
+ {
+     if (defel)
+     {
+         Oid sid  = pgaceSecurityLabelToSid(strVal(defel->arg));
+
+         HeapTupleSetSecLabel(tuple, sid);
+     }
+ }
+
+ void
+ sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ void
+ sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+ {
+     putExplicitContext(tuple, defel);
+ }
+
+ /*******************************************************************************
+  * DATABASE object related hooks
+  *******************************************************************************/
+
+ void
+ sepgsqlGetDatabaseParam(const char *name)
+ {
+     HeapTuple    tuple;
+     const char *audit_name;
+
+     tuple = SearchSysCache(DATABASEOID,
+                            ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for database %u",
+              MyDatabaseId);
+
+     audit_name = sepgsqlTupleName(DatabaseRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_DATABASE,
+                                DB_DATABASE__GET_PARAM,
+                                audit_name);
+     ReleaseSysCache(tuple);
+ }
+
+ void
+ sepgsqlSetDatabaseParam(const char *name, char *argstring)
+ {
+     HeapTuple    tuple;
+     const char *audit_name;
+
+     tuple = SearchSysCache(DATABASEOID,
+                            ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for database %u",
+              MyDatabaseId);
+
+     audit_name = sepgsqlTupleName(DatabaseRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_DATABASE,
+                                DB_DATABASE__SET_PARAM,
+                                audit_name);
+     ReleaseSysCache(tuple);
+ }
+
+ /*******************************************************************************
+  * RELATION(Table)/ATTRIBTUE(column) object related hooks
+  *******************************************************************************/
+ void
+ sepgsqlLockTable(Oid relid)
+ {
+     HeapTuple    tuple;
+
+     tuple = SearchSysCache(RELOID,
+                            ObjectIdGetDatum(relid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for relation %u", relid);
+
+     if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
+     {
+         const char *audit_name
+             = sepgsqlTupleName(RelationRelationId, tuple);
+         sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                    SECCLASS_DB_TABLE,
+                                    DB_TABLE__LOCK,
+                                    audit_name);
+     }
+     ReleaseSysCache(tuple);
+ }
+
+ void
+ sepgsqlExecTruncate(List *trunc_rels)
+ {
+     ListCell *l;
+
+     foreach (l, trunc_rels)
+     {
+         const char *audit_name;
+         HeapTuple tuple;
+         HeapScanDesc scan;
+         Relation rel = (Relation) lfirst(l);
+
+         if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+             continue;
+
+         /*
+          * check db_table:{delete}
+          */
+         tuple = SearchSysCache(RELOID,
+                                ObjectIdGetDatum(RelationGetRelid(rel)),
+                                0, 0, 0);
+         if (!HeapTupleIsValid(tuple))
+             elog(ERROR, "SELinux: cache lookup failed for relation %u",
+                  RelationGetRelid(rel));
+
+         audit_name = sepgsqlTupleName(RelationRelationId, tuple);
+         sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                    SECCLASS_DB_TABLE,
+                                    DB_TABLE__DELETE,
+                                    audit_name);
+         ReleaseSysCache(tuple);
+
+         /*
+          * check db_tuple:{delete}
+          */
+         scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+         {
+             sepgsqlCheckTuplePerms(rel, tuple, NULL,
+                                    SEPGSQL_PERMS_DELETE, true);
+         }
+         heap_endscan(scan);
+     }
+ }
+
+ /*******************************************************************************
+  * PROCEDURE related hooks
+  *******************************************************************************/
+
+ typedef struct
+ {
+     PGFunction            fn_addr;
+     security_context_t    fn_con;
+ } sepgsql_fn_info;
+
+ static Datum
+ invokeTrustedProcedure(PG_FUNCTION_ARGS)
+ {
+     sepgsql_fn_info *sefinfo = fcinfo->flinfo->fn_pgaceItem;
+     security_context_t orig_context;
+     Datum        retval;
+
+     /*
+      * set new domain
+      */
+     orig_context = sepgsqlSwitchClientContext(sefinfo->fn_con);
+
+     PG_TRY();
+     {
+         retval = sefinfo->fn_addr(fcinfo);
+     }
+     PG_CATCH();
+     {
+         sepgsqlSwitchClientContext(orig_context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     sepgsqlSwitchClientContext(orig_context);
+
+     return retval;
+ }
+
+ void
+ sepgsqlCallFunction(FmgrInfo *finfo)
+ {
+     MemoryContext        oldctx;
+     HeapTuple            tuple;
+     security_context_t    newcon;
+     access_vector_t        perms = DB_PROCEDURE__EXECUTE;
+     const char           *audit_name;
+
+     if (IsBootstrapProcessingMode())
+         return;        /* under initialization of pg_proc */
+
+     tuple = SearchSysCache(PROCOID,
+                            ObjectIdGetDatum(finfo->fn_oid),
+                            0, 0, 0);
+     Assert(HeapTupleIsValid(tuple));
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for procedure %u", finfo->fn_oid);
+
+     oldctx = MemoryContextSwitchTo(finfo->fn_mcxt);
+     /*
+      * check trusted procedure
+      */
+     newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(tuple),
+                                         SECCLASS_PROCESS);
+     if (strcmp(newcon, sepgsqlGetClientContext()) != 0)
+     {
+         sepgsql_fn_info *sefinfo
+             = palloc0(sizeof(sepgsql_fn_info));
+
+         sefinfo->fn_addr = finfo->fn_addr;
+         sefinfo->fn_con = newcon;
+         finfo->fn_addr = invokeTrustedProcedure;
+         finfo->fn_pgaceItem = sefinfo;
+
+         perms |= DB_PROCEDURE__ENTRYPOINT;
+     }
+     else
+         pfree(newcon);
+
+     MemoryContextSwitchTo(oldctx);
+
+     audit_name = sepgsqlTupleName(ProcedureRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_PROCEDURE,
+                                perms,
+                                audit_name);
+     ReleaseSysCache(tuple);
+ }
+
+ void
+ sepgsqlCallAggFunction(HeapTuple aggTuple)
+ {
+     Form_pg_aggregate aggForm
+         = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+     HeapTuple tuple;
+     const char *audit_name;
+
+     /* check pg_proc.oid = pg_aggregate.aggfnoid */
+     tuple = SearchSysCache(PROCOID,
+                            ObjectIdGetDatum(aggForm->aggfnoid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for procedure %u",
+              aggForm->aggfnoid);
+
+     audit_name = sepgsqlTupleName(ProcedureRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_PROCEDURE,
+                                DB_PROCEDURE__EXECUTE,
+                                audit_name);
+     ReleaseSysCache(tuple);
+ }
+
+ bool
+ sepgsqlCallTriggerFunction(TriggerData *tgdata)
+ {
+     Relation    rel = tgdata->tg_relation;
+     HeapTuple    newtup = NULL;
+     HeapTuple    oldtup = NULL;
+
+     /*
+      * We don't need to check tuple permissions for
+      * statement triggers
+      */
+     if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event))
+         return true;
+
+     if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event))
+     {
+         if (TRIGGER_FIRED_AFTER(tgdata->tg_event))
+             newtup = tgdata->tg_trigtuple;
+     }
+     else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event))
+     {
+         oldtup = tgdata->tg_trigtuple;
+         if (TRIGGER_FIRED_AFTER(tgdata->tg_event))
+         {
+             Oid securityId = HeapTupleGetSecLabel(tgdata->tg_newtuple);
+
+             if (HeapTupleGetSecLabel(oldtup) != securityId)
+                 newtup = tgdata->tg_newtuple;
+         }
+     }
+     else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event))
+     {
+         if (TRIGGER_FIRED_AFTER(tgdata->tg_event))
+             oldtup = tgdata->tg_trigtuple;
+     }
+     else
+     {
+         elog(ERROR, "SELinux: unexpected trigger event type (%u)",
+              tgdata->tg_event);
+     }
+     if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL,
+                                           SEPGSQL_PERMS_SELECT, false))
+         return false;
+     if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL,
+                                           SEPGSQL_PERMS_SELECT, false))
+         return false;
+
+     return true;
+ }
+
+ bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple)
+ {
+     security_context_t    newcon;
+     const char *audit_name;
+
+     /*
+      * If function is defined as trusted procedure, we always should
+      * not allow it to be inlined, and actual permission checks are
+      * done later phase.
+      */
+     newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(func_tuple),
+                                         SECCLASS_PROCESS);
+     if (strcmp(newcon, sepgsqlGetClientContext()) != 0)
+         return false;
+
+     audit_name = sepgsqlTupleName(ProcedureRelationId, func_tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(func_tuple),
+                                SECCLASS_DB_PROCEDURE,
+                                DB_PROCEDURE__EXECUTE,
+                                audit_name);
+     return true;
+ }
+
+ /*******************************************************************************
+  * LOAD shared library module hook
+  *******************************************************************************/
+ void
+ sepgsqlLoadSharedModule(const char *filename)
+ {
+     security_context_t filecon;
+
+     if (getfilecon_raw(filename, &filecon) < 0)
+         ereport(ERROR,
+                 (errcode_for_file_access(),
+                  errmsg("could not access file \"%s\": %m", filename)));
+     PG_TRY();
+     {
+         sepgsqlComputePermission(sepgsqlGetDatabaseContext(),
+                                  filecon,
+                                  SECCLASS_DB_DATABASE,
+                                  DB_DATABASE__LOAD_MODULE,
+                                  filename);
+     }
+     PG_CATCH();
+     {
+         freecon(filecon);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(filecon);
+ }
+
+ /*******************************************************************************
+  * Binary Large Object hooks
+  *******************************************************************************/
+
+ void
+ sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple)
+ {
+     const char *audit_name;
+
+     sepgsqlSetDefaultContext(rel, tuple);
+
+     audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_BLOB,
+                                DB_BLOB__CREATE,
+                                audit_name);
+ }
+
+ void
+ sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem)
+ {
+     Oid            security_id = HeapTupleGetSecLabel(tuple);
+     List       *okList = (List *) (*pgaceItem);
+     ListCell   *l;
+     const char *audit_name;
+
+     foreach (l, okList)
+     {
+         if (security_id == lfirst_oid(l))
+             return;        /* already allowed */
+     }
+
+     audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple);
+     sepgsqlClientHasPermission(security_id,
+                                SECCLASS_DB_BLOB,
+                                DB_BLOB__DROP,
+                                audit_name);
+
+     *pgaceItem = lappend_oid(okList, security_id);
+ }
+
+ static void
+ checkLargeObjectPages(Oid loid, Snapshot snapshot,
+                       int32 start_pageno, int32 end_pageno,
+                       access_vector_t perms)
+ {
+     Relation        rel;
+     HeapTuple        tuple;
+     SysScanDesc        sd;
+     ScanKeyData        skey[2];
+     List           *okList = NIL;
+
+     rel = heap_open(LargeObjectRelationId, AccessShareLock);
+
+     ScanKeyInit(&skey[0],
+                 Anum_pg_largeobject_loid,
+                 BTEqualStrategyNumber, F_OIDEQ,
+                 ObjectIdGetDatum(loid));
+
+     if (start_pageno <= 0)
+         sd = systable_beginscan(rel, LargeObjectLOidPNIndexId,
+                                 true, snapshot, 1, skey);
+     else
+     {
+         ScanKeyInit(&skey[1],
+                     Anum_pg_largeobject_pageno,
+                     BTGreaterEqualStrategyNumber, F_INT4GE,
+                     Int32GetDatum(start_pageno));
+
+         sd = systable_beginscan(rel, LargeObjectLOidPNIndexId,
+                                 true, snapshot, 2, skey);
+     }
+
+     while ((tuple = systable_getnext(sd)) != NULL)
+     {
+         Form_pg_largeobject loForm
+             = (Form_pg_largeobject) GETSTRUCT(tuple);
+         Oid            security_id;
+         ListCell    *l;
+         const char  *audit_name;
+
+         if (end_pageno >= 0 && loForm->pageno > end_pageno)
+             break;
+
+         security_id = HeapTupleGetSecLabel(tuple);
+
+         foreach (l, okList)
+         {
+             if (security_id == lfirst_oid(l))
+                 goto skip;
+         }
+         okList = lappend_oid(okList, security_id);
+
+         audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple);
+         sepgsqlClientHasPermission(security_id,
+                                    SECCLASS_DB_BLOB,
+                                    perms,
+                                    audit_name);
+     skip:
+         ;
+     }
+     systable_endscan(sd);
+
+     list_free(okList);
+
+     heap_close(rel, NoLock);
+ }
+
+ void
+ sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int32 length)
+ {
+     int32 start_pageno    = lodesc->offset / LOBLKSIZE;
+     int32 end_pageno    = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE;
+
+     checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+                           start_pageno, end_pageno, DB_BLOB__READ);
+ }
+
+ void
+ sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int32 length)
+ {
+     int32 start_pageno    = lodesc->offset / LOBLKSIZE;
+     int32 end_pageno    = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE;
+
+     checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+                           start_pageno, end_pageno, DB_BLOB__WRITE);
+ }
+
+ void
+ sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int32 offset)
+ {
+     int32 start_pageno    = lodesc->offset / LOBLKSIZE;
+
+     checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+                           start_pageno, -1, DB_BLOB__WRITE);
+ }
+
+ void
+ sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename)
+ {
+     security_context_t    tcontext;
+     security_class_t tclass
+         = sepgsqlFileObjectClass(fdesc, filename);
+
+     if (fgetfilecon_raw(fdesc, &tcontext) < 0)
+         ereport(ERROR,
+                 (errcode_for_file_access(),
+                  errmsg("could not get security context \"%s\": %m", filename)));
+     PG_TRY();
+     {
+         sepgsqlComputePermission(sepgsqlGetClientContext(),
+                                  tcontext,
+                                  tclass,
+                                  FILE__READ,
+                                  filename);
+     }
+     PG_CATCH();
+     {
+         freecon(tcontext);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(tcontext);
+
+     checkLargeObjectPages(loid, SnapshotNow, -1, -1,
+                           DB_BLOB__WRITE | DB_BLOB__IMPORT);
+ }
+
+ void
+ sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename)
+ {
+     security_context_t    tcontext;
+     security_class_t tclass
+         = sepgsqlFileObjectClass(fdesc, filename);
+
+     if (fgetfilecon_raw(fdesc, &tcontext) < 0)
+         ereport(ERROR,
+                 (errcode_for_file_access(),
+                  errmsg("could not security context \"%s\": %m", filename)));
+     PG_TRY();
+     {
+         sepgsqlComputePermission(sepgsqlGetClientContext(),
+                                  tcontext,
+                                  tclass,
+                                  FILE__WRITE,
+                                  filename);
+     }
+     PG_CATCH();
+     {
+         freecon(tcontext);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(tcontext);
+
+     checkLargeObjectPages(loid, SnapshotNow, -1, -1,
+                           DB_BLOB__READ | DB_BLOB__EXPORT);
+ }
+
+ void
+ sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple)
+ {
+     const char *audit_name
+         = sepgsqlTupleName(LargeObjectRelationId, tuple);
+
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_BLOB,
+                                DB_BLOB__GETATTR,
+                                audit_name);
+ }
+
+ void
+ sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup)
+ {
+     const char *audit_name;
+
+     if (HeapTupleGetSecLabel(newtup) == HeapTupleGetSecLabel(oldtup))
+         return;
+
+     audit_name = sepgsqlTupleName(LargeObjectRelationId, oldtup);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(oldtup),
+                                SECCLASS_DB_BLOB,
+                                DB_BLOB__SETATTR | DB_BLOB__RELABELFROM,
+                                audit_name);
+     /*
+      * check db_blob:{setattr relabelto}
+      */
+     audit_name = sepgsqlTupleName(LargeObjectRelationId, newtup);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(newtup),
+                                SECCLASS_DB_BLOB,
+                                DB_BLOB__RELABELTO,
+                                audit_name);
+ }
+
+ /*******************************************************************************
+  * ExecScan hooks
+  *******************************************************************************/
+ static bool abort_on_violated_tuple = false;
+
+ bool
+ sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot)
+ {
+     HeapTuple    tuple;
+     uint32        perms = (scan->pgaceTuplePerms & SEPGSQL_PERMS_MASK);
+
+     if (perms == 0)
+         return true;
+
+     tuple = ExecMaterializeSlot(slot);
+
+     return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms,
+                                   abort_on_violated_tuple);
+ }
+
+ /* ----------------------------------------------------------
+  * special cases for Foreign Key constraint
+  * ---------------------------------------------------------- */
+ Datum
+ sepgsqlBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid)
+ {
+     Datum save_pgace = BoolGetDatum(abort_on_violated_tuple);
+
+     /*
+      * NOTE: when a tuple is inserted/updated on FK relation, all we should
+      * do is simply filtering violated tuples on PK relation, as normal
+      * row-level access controls doing.
+      * At the result, INSERT/UPDATE with invisible tuple will be failed.
+      */
+     if (is_primary)
+         abort_on_violated_tuple = true;
+
+     return save_pgace;
+ }
+
+ void
+ sepgsqlEndPerformCheckFK(Relation rel, Datum save_pgace)
+ {
+     abort_on_violated_tuple = DatumGetBool(save_pgace);
+ }
+
+ /*******************************************************************************
+  * security_label hooks
+  *******************************************************************************/
+ bool
+ sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts)
+ {
+     /*
+      * Newly created table via SELECT INTO/CREATE TABLE AS
+      */
+     if (rel == NULL)
+         return sepostgresql_row_level;
+
+     if (RelationGetForm(rel)->relkind != RELKIND_RELATION &&
+         RelationGetForm(rel)->relkind != RELKIND_SEQUENCE)
+         return false;
+
+     if (RelationGetRelid(rel) == DatabaseRelationId ||
+         RelationGetRelid(rel) == RelationRelationId ||
+         RelationGetRelid(rel) == AttributeRelationId ||
+         RelationGetRelid(rel) == ProcedureRelationId ||
+         RelationGetRelid(rel) == LargeObjectRelationId)
+         return true;
+
+     return sepostgresql_row_level;
+ }
+
+ char *
+ sepgsqlTranslateSecurityLabelIn(const char *context)
+ {
+     security_context_t i_context;
+     char       *result;
+
+     if (selinux_trans_to_raw_context((security_context_t) context, &i_context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not translate mls label")));
+     PG_TRY();
+     {
+         result = pstrdup(i_context);
+     }
+     PG_CATCH();
+     {
+         freecon(i_context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(i_context);
+
+     return result;
+ }
+
+ char *
+ sepgsqlTranslateSecurityLabelOut(const char *context)
+ {
+     security_context_t o_context;
+     char       *result;
+
+     if (selinux_raw_to_trans_context((security_context_t) context, &o_context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not translate mls label")));
+     PG_TRY();
+     {
+         result = pstrdup(o_context);
+     }
+     PG_CATCH();
+     {
+         freecon(o_context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(o_context);
+
+     return result;
+ }
+
+ /*
+  * sepgsqlCheckValidSecurityLabel() checks whether the given
+  * security context is valid on the current working security
+  * policy, or not.
+  * If it's invalid, sepgsqlUnlabeledSecurityLabel() is invoked
+  * at the next to get an alternative security label.
+  */
+ bool
+ sepgsqlCheckValidSecurityLabel(char *context)
+ {
+     if (security_check_context_raw((security_context_t) context) < 0)
+         return false;
+
+     return true;
+ }
+
+ char *
+ sepgsqlUnlabeledSecurityLabel(void)
+ {
+     security_context_t unlabeled;
+     char *result;
+
+     if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not get unlabeled initial context")));
+     PG_TRY();
+     {
+         result = pstrdup(unlabeled);
+     }
+     PG_CATCH();
+     {
+         freecon(unlabeled);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(unlabeled);
+
+     return result;
+ }
+
+ char *
+ sepgsqlSecurityLabelOfLabel(void)
+ {
+     security_context_t table_context, tuple_context;
+     HeapTuple    tuple;
+
+     /*
+      * obtain security context of pg_security
+      */
+     tuple = SearchSysCache(RELOID,
+                            ObjectIdGetDatum(SecurityRelationId), 0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for relation %u",
+                     SecurityRelationId);
+
+     table_context = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple));
+     if (!table_context || !pgaceCheckValidSecurityLabel(table_context))
+         table_context = pgaceUnlabeledSecurityLabel();
+
+     tuple_context = sepgsqlComputeCreateContext(sepgsqlGetServerContext(),
+                                                 table_context, SECCLASS_DB_TUPLE);
+     pfree(table_context);
+
+     ReleaseSysCache(tuple);
+
+     return tuple_context;
+ }
+
+ /******************************************************************
+  * HeapTuple modification hooks
+  ******************************************************************/
+ static HeapTuple
+ getHeapTupleFromItemPointer(Relation rel, ItemPointer tid)
+ {
+     /*
+      * obtain an old tuple
+      */
+     Buffer        buffer;
+     PageHeader    dp;
+     ItemId        lp;
+     HeapTupleData tuple;
+     HeapTuple    oldtup;
+
+     buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid));
+     LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+     dp = (PageHeader) BufferGetPage(buffer);
+     lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
+
+     Assert(ItemIdIsNormal(lp));
+
+     tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
+     tuple.t_len = ItemIdGetLength(lp);
+     tuple.t_self = *tid;
+     tuple.t_tableOid = RelationGetRelid(rel);
+     oldtup = heap_copytuple(&tuple);
+
+     LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+     ReleaseBuffer(buffer);
+
+     return oldtup;
+ }
+
+ static bool
+ isTrustedRelation(Relation rel, bool is_internal)
+ {
+     if (!is_internal)
+         return false;
+
+     if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+         return true;
+
+     switch (RelationGetRelid(rel))
+     {
+         case LargeObjectRelationId:
+         case SecurityRelationId:
+             return true;
+     }
+     return false;
+ }
+
+ bool
+ sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple,
+                        bool is_internal, bool with_returning)
+ {
+     uint32        perms;
+
+     /*
+      * default context for no explicit labeled tuple
+      */
+     if (!OidIsValid(HeapTupleGetSecLabel(tuple)))
+     {
+         /*
+          * If user gives no valid security context,
+          * it assigns a default one on the new tuple.
+          */
+         if (HeapTupleHasSecLabel(tuple))
+             sepgsqlSetDefaultContext(rel, tuple);
+     }
+     else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: unable to insert "
+                         "pg_largeobject.security_label")));
+
+     if (isTrustedRelation(rel, is_internal))
+         return true;
+
+     perms = SEPGSQL_PERMS_INSERT;
+     if (with_returning)
+         perms |= SEPGSQL_PERMS_SELECT;
+
+     return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal);
+ }
+
+ bool
+ sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+                        bool is_internal, bool with_returning)
+ {
+     Oid            relid = RelationGetRelid(rel);
+     HeapTuple    oldtup;
+     uint32        perms;
+     bool        rc = true;
+     bool        relabel = false;
+
+     oldtup = getHeapTupleFromItemPointer(rel, otid);
+
+     if (!OidIsValid(HeapTupleGetSecLabel(newtup)))
+     {
+         /*
+          * If user does not specify new security context
+          * explicitly, it preserves a security context of
+          * older tuple.
+          */
+         Oid sid = HeapTupleGetSecLabel(oldtup);
+
+         if (HeapTupleHasSecLabel(newtup))
+             HeapTupleSetSecLabel(newtup, sid);
+     }
+     else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: unable to update "
+                         "pg_largeobject.security_label")));
+
+     if (isTrustedRelation(rel, is_internal))
+         return true;
+
+     if (HeapTupleGetSecLabel(newtup) != HeapTupleGetSecLabel(oldtup) ||
+         sepgsqlTupleObjectClass(relid, newtup) != sepgsqlTupleObjectClass(relid, oldtup))
+         relabel = true;
+
+     perms = SEPGSQL_PERMS_UPDATE;
+     if (relabel)
+         perms |= SEPGSQL_PERMS_RELABELFROM;
+     rc = sepgsqlCheckTuplePerms(rel, oldtup, newtup, perms, is_internal);
+     if (!rc)
+         goto out;
+
+     if (relabel)
+     {
+         perms = SEPGSQL_PERMS_RELABELTO;
+         if (with_returning)
+             perms |= SEPGSQL_PERMS_SELECT;
+         rc = sepgsqlCheckTuplePerms(rel, newtup, NULL, perms, is_internal);
+     }
+   out:
+     heap_freetuple(oldtup);
+     return rc;
+ }
+
+ bool
+ sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid,
+                        bool is_internal, bool with_returning)
+ {
+     HeapTuple    oldtup;
+     uint32        perms = SEPGSQL_PERMS_DELETE;
+     bool        rc;
+
+     if (isTrustedRelation(rel, is_internal))
+         return true;
+
+     oldtup = getHeapTupleFromItemPointer(rel, otid);
+     rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal);
+     heap_freetuple(oldtup);
+
+     return rc;
+ }
diff -Nrpc base/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c
*** base/src/backend/security/sepgsql/permissions.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/sepgsql/permissions.c    Thu Jan 22 14:26:56 2009
***************
*** 0 ****
--- 1,785 ----
+ /*
+  * src/backend/security/sepgsql/permissions.c
+  *     applies SE-PostgreSQL permission checks
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "access/genam.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_aggregate.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_attribute.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_cast.h"
+ #include "catalog/pg_class.h"
+ #include "catalog/pg_conversion.h"
+ #include "catalog/pg_database.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_largeobject.h"
+ #include "catalog/pg_operator.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_security.h"
+ #include "catalog/pg_trigger.h"
+ #include "catalog/pg_ts_parser.h"
+ #include "catalog/pg_ts_template.h"
+ #include "catalog/pg_type.h"
+ #include "miscadmin.h"
+ #include "security/pgace.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+ /*
+  * It can be configured via a GUC variable to toggle
+  * row-level access controls.
+  */
+ bool sepostgresql_row_level;
+
+ /*
+  * sepgsqlTupleName
+  *   returns an identifier string to generate audit record for
+  *   the given tuple. Please note that its results can indicate
+  *   an address within the given tuple, so we should not refer
+  *   the returned pointer after HeapTuple is released.
+  */
+ const char *
+ sepgsqlTupleName(Oid relid, HeapTuple tuple)
+ {
+     static char buffer[NAMEDATALEN * 2 + 10];
+
+     switch (relid)
+     {
+     case DatabaseRelationId:
+         return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname);
+
+     case RelationRelationId:
+         return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname);
+
+     case AttributeRelationId:
+         if (!IsBootstrapProcessingMode())
+         {
+             Form_pg_attribute attForm
+                 = (Form_pg_attribute) GETSTRUCT(tuple);
+             char *relname
+                 = get_rel_name(attForm->attrelid);
+
+             if (relname)
+             {
+                 snprintf(buffer, sizeof(buffer), "%s.%s",
+                          relname, NameStr(attForm->attname));
+                 pfree(relname);
+                 return buffer;
+             }
+         }
+         return NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname);
+
+     case ProcedureRelationId:
+         return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname);
+
+     case LargeObjectRelationId:
+         snprintf(buffer, sizeof(buffer), "loid:%u",
+                  ((Form_pg_largeobject) GETSTRUCT(tuple))->loid);
+         return buffer;
+     }
+     return NULL;    /* No tuple name for audit record */
+ }
+
+ /*
+  * sepgsqlFileObjectClass
+  *
+  * It returns proper object class of filesystem object already opened.
+  * It is necessary to check privileges voluntarily.
+  */
+ security_class_t
+ sepgsqlFileObjectClass(int fdesc, const char *filename)
+ {
+     struct stat stbuf;
+
+     if (fstat(fdesc, &stbuf) != 0)
+         ereport(ERROR,
+                 (errcode_for_file_access(),
+                  errmsg("could not stat file \"%s\": %m", filename)));
+
+     if (S_ISDIR(stbuf.st_mode))
+         return SECCLASS_DIR;
+     else if (S_ISCHR(stbuf.st_mode))
+         return SECCLASS_CHR_FILE;
+     else if (S_ISBLK(stbuf.st_mode))
+         return SECCLASS_BLK_FILE;
+     else if (S_ISFIFO(stbuf.st_mode))
+         return SECCLASS_FIFO_FILE;
+     else if (S_ISLNK(stbuf.st_mode))
+         return SECCLASS_LNK_FILE;
+     else if (S_ISSOCK(stbuf.st_mode))
+         return SECCLASS_SOCK_FILE;
+
+     return SECCLASS_FILE;
+ }
+
+ /*
+  * sepgsqlTupleObjectClass
+  *
+  * It returns proper object class of given tuple
+  */
+ security_class_t
+ sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple)
+ {
+     Form_pg_class clsForm;
+     Form_pg_attribute attForm;
+
+     switch (relid)
+     {
+         case DatabaseRelationId:
+             return SECCLASS_DB_DATABASE;
+
+         case RelationRelationId:
+             clsForm = (Form_pg_class) GETSTRUCT(tuple);
+             if (clsForm->relkind == RELKIND_RELATION)
+                 return SECCLASS_DB_TABLE;
+             break;
+
+         case AttributeRelationId:
+             attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+             if (attForm->attkind == RELKIND_RELATION)
+                 return SECCLASS_DB_COLUMN;
+             break;
+
+         case ProcedureRelationId:
+             return SECCLASS_DB_PROCEDURE;
+
+         case LargeObjectRelationId:
+             return SECCLASS_DB_BLOB;
+     }
+
+     return SECCLASS_DB_TUPLE;
+ }
+
+ /*
+  * sepgsqlCheckTuplePerms
+  *
+  * This function evaluates given permission set (SEPGSQL_PERMS_*) onto the
+  * given tuple, with translating them into proper SELinux permission.
+  *
+  * Accesses to some of system catalog has special meanings. DELETE a tuple
+  * within pg_class also means DROP TABLE for instance. In this case,
+  * SE-PostgreSQL translate given SEPGSQL_PERMS_DELETE into DB_TABLE__DROP
+  * to keep consistency of user operation. To delete a tuple within pg_class
+  * always means dropping a table independent from what SQL statement is
+  * used.
+  *
+  * Thus, checks for some of system catalog need to modify given permission
+  * set at checkTuplePermsXXXX() functions.
+  */
+ static access_vector_t
+ sepgsqlPermsToCommonAv(uint32 perms)
+ {
+     access_vector_t result = 0;
+
+     result |= (perms & SEPGSQL_PERMS_USE         ? COMMON_DATABASE__GETATTR : 0);
+     result |= (perms & SEPGSQL_PERMS_SELECT      ? COMMON_DATABASE__GETATTR : 0);
+     result |= (perms & SEPGSQL_PERMS_UPDATE      ? COMMON_DATABASE__SETATTR : 0);
+     result |= (perms & SEPGSQL_PERMS_INSERT      ? COMMON_DATABASE__CREATE : 0);
+     result |= (perms & SEPGSQL_PERMS_DELETE      ? COMMON_DATABASE__DROP : 0);
+     result |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0);
+     result |= (perms & SEPGSQL_PERMS_RELABELTO   ? COMMON_DATABASE__RELABELTO : 0);
+
+     return result;
+ }
+
+ static access_vector_t
+ sepgsqlPermsToDatabaseAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     return sepgsqlPermsToCommonAv(perms);
+ }
+
+ static access_vector_t
+ sepgsqlPermsToTableAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     return sepgsqlPermsToCommonAv(perms);
+ }
+
+ static access_vector_t
+ sepgsqlPermsToProcedureAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     access_vector_t result = sepgsqlPermsToCommonAv(perms);
+     Form_pg_proc proForm;
+     HeapTuple protup;
+     Datum probin;
+     bool isnull;
+
+     /*
+      * Check permission for loadable module installation
+      */
+     protup = HeapTupleIsValid(newtup) ? newtup : tuple;
+     proForm = (Form_pg_proc) GETSTRUCT(protup);
+
+     if (proForm->prolang == ClanguageId)
+     {
+         bool need_check = false;
+
+         probin = SysCacheGetAttr(PROCOID, protup,
+                                  Anum_pg_proc_probin,
+                                  &isnull);
+         if (!isnull)
+         {
+             if (result & DB_PROCEDURE__CREATE)
+                 need_check = true;
+             else if (HeapTupleIsValid(newtup))
+             {
+                 Form_pg_proc oldForm = (Form_pg_proc) GETSTRUCT(tuple);
+
+                 if (oldForm->prolang != proForm->prolang)
+                     need_check = true;
+                 else
+                 {
+                     Datum oldbin = SysCacheGetAttr(PROCOID, tuple,
+                                                    Anum_pg_proc_probin,
+                                                    &isnull);
+                     if (isnull)
+                         need_check = true;
+                     else
+                     {
+                         Datum comp = DirectFunctionCall2(byteane, oldbin, probin);
+                         need_check = DatumGetBool(comp);
+                     }
+                 }
+             }
+
+             if (need_check)
+             {
+                 char *filename = TextDatumGetCString(probin);
+
+                 sepgsqlCheckModuleInstallPerms(filename);
+             }
+         }
+     }
+
+     return result;
+ }
+
+ static access_vector_t
+ sepgsqlPermsToColumnAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     access_vector_t result = sepgsqlPermsToCommonAv(perms);
+
+     if (HeapTupleIsValid(newtup))
+     {
+         Form_pg_attribute oldatt = (Form_pg_attribute) GETSTRUCT(tuple);
+         Form_pg_attribute newatt = (Form_pg_attribute) GETSTRUCT(newtup);
+
+         if (!oldatt->attisdropped && newatt->attisdropped)
+             result |= DB_COLUMN__DROP;
+         if (oldatt->attisdropped && !newatt->attisdropped)
+             result |= DB_COLUMN__CREATE;
+     }
+     return result;
+ }
+
+ static access_vector_t
+ sepgsqlPermsToTupleAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     access_vector_t result = 0;
+
+     result |= (perms & SEPGSQL_PERMS_USE         ? DB_TUPLE__USE : 0);
+     result |= (perms & SEPGSQL_PERMS_SELECT      ? DB_TUPLE__SELECT : 0);
+     result |= (perms & SEPGSQL_PERMS_UPDATE      ? DB_TUPLE__UPDATE : 0);
+     result |= (perms & SEPGSQL_PERMS_INSERT      ? DB_TUPLE__INSERT : 0);
+     result |= (perms & SEPGSQL_PERMS_DELETE      ? DB_TUPLE__DELETE : 0);
+     result |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0);
+     result |= (perms & SEPGSQL_PERMS_RELABELTO   ? DB_TUPLE__RELABELTO : 0);
+
+     return result;
+ }
+
+ static access_vector_t
+ sepgsqlPermsToBlobAv(uint32 perms, HeapTuple tuple, HeapTuple newtup)
+ {
+     access_vector_t result = sepgsqlPermsToCommonAv(perms);
+
+     /*
+      * NOTE: INSERT tuples into pg_largeobject has a possibility to create
+      * a new largeobject, if the given loid is not exist on the current
+      * pg_largeobject. Ditto for DELETE statement, it also has a possibility
+      * to drop a largeobject, if it removes all tuples within a large object.
+      *
+      * UPDATE pg_largeobject.loid has a possibility to create and drop
+      * a largeobject in same time, so we need to check it when loid is
+      * changed.
+      *
+      * db_blob:{create} and db_blob:{drop} should be evaluated for
+      * creation/deletion of largeobject, but we have to check pg_largeobject
+      * with SnapshotSelf whether there is one or more tuple having same loid,
+      * or not, on each tuple insertion or deletion.
+      *
+      * So, we assume any INSERT means db_blob:{create}, any DELETE means
+      * db_blob:{drop}.
+      */
+     result |= (perms & SEPGSQL_PERMS_INSERT    ? DB_BLOB__WRITE : 0);
+     if (perms & SEPGSQL_PERMS_UPDATE)
+     {
+         result |= DB_BLOB__WRITE;
+
+         if (((Form_pg_largeobject) GETSTRUCT(tuple))->loid !=
+             ((Form_pg_largeobject) GETSTRUCT(newtup))->loid)
+             result |= (DB_BLOB__CREATE | DB_BLOB__DROP);
+     }
+     result |= (perms & SEPGSQL_PERMS_DELETE    ? DB_BLOB__WRITE : 0);
+     result |= (perms & SEPGSQL_PERMS_READ    ? DB_BLOB__READ  : 0);
+
+     return result;
+ }
+
+ /*
+  * sepgsqlCheckProcedureInstall
+  *   checks permission: db_procedure:{install}, when client tries to modify
+  *   a system catalog which contains procedure id to invoke it later.
+  *   Because these functions are invoked internally, to search a table with
+  *   a special index algorithm for example, the security policy has to prevent
+  *   malicious user-defined functions to be installed.
+  */
+ static void
+ checkProcedureInstall(Oid proc_oid)
+ {
+     if (!OidIsValid(proc_oid))
+         return;
+
+     if (IsBootstrapProcessingMode())
+     {
+         /*
+          * We assume all procedures have same security context
+          * in bootstrap processing mode, because no one can
+          * relabel it.
+          */
+         Oid proc_sid
+             = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                      SECCLASS_DB_PROCEDURE);
+         sepgsqlClientHasPermission(proc_sid,
+                                    SECCLASS_DB_PROCEDURE,
+                                    DB_PROCEDURE__INSTALL,
+                                    NULL);
+     }
+     else
+     {
+         HeapTuple protup;
+         const char *audit_name;
+
+         protup = SearchSysCache(PROCOID,
+                                 ObjectIdGetDatum(proc_oid),
+                                 0, 0, 0);
+         if (!HeapTupleIsValid(protup))
+             return;
+
+         audit_name = sepgsqlTupleName(ProcedureRelationId, protup);
+         sepgsqlClientHasPermission(HeapTupleGetSecLabel(protup),
+                                    SECCLASS_DB_PROCEDURE,
+                                    DB_PROCEDURE__INSTALL,
+                                    audit_name);
+         ReleaseSysCache(protup);
+     }
+ }
+
+ #define CHECK_PROC_INSTALL_HANDLER(catalog,member,tuple,newtup)            \
+     do {                                                                \
+         if (!HeapTupleIsValid(newtup))                                    \
+             checkProcedureInstall(((CppConcat(Form_,catalog)) GETSTRUCT(tuple))->member); \
+         else if (((CppConcat(Form_,catalog)) GETSTRUCT(tuple))->member    \
+                  != ((CppConcat(Form_,catalog)) GETSTRUCT(newtup))->member) \
+             checkProcedureInstall(((CppConcat(Form_,catalog)) GETSTRUCT(newtup))->member); \
+     } while(0)
+
+ static void
+ sepgsqlCheckProcedureInstall(Relation rel, HeapTuple tuple, HeapTuple newtup)
+ {
+     /*
+      * Some of system catalog can be configured to invoke functions
+      * implicitly. It checks permission to prevent implicit invocation
+      * of malicious functions.
+      */
+     switch (RelationGetRelid(rel))
+     {
+     case AggregateRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfnoid, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggtransfn, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfinalfn, tuple, newtup);
+         break;
+
+     case AccessMethodRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_am, aminsert, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, ambeginscan, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amgettuple, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amgetbitmap, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amrescan, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amendscan, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, ammarkpos, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amrestrpos, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, ambuild, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, ambulkdelete, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amvacuumcleanup, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amcostestimate, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_am, amoptions, tuple, newtup);
+         break;
+
+     case AccessMethodProcedureRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_amproc, amproc, tuple, newtup);
+         break;
+
+     case CastRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_cast, castfunc, tuple, newtup);
+         break;
+
+     case ConversionRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_conversion, conproc, tuple, newtup);
+         break;
+
+     case LanguageRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_language, lanplcallfoid, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_language, lanvalidator, tuple, newtup);
+         break;
+
+     case OperatorRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_operator, oprcode, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_operator, oprrest, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_operator, oprjoin, tuple, newtup);
+         break;
+
+     case TriggerRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_trigger, tgfoid, tuple, newtup);
+         break;
+
+     case TSParserRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsstart, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prstoken, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsend, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsheadline, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prslextype, tuple, newtup);
+         break;
+
+     case TSTemplateRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmplinit, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmpllexize, tuple, newtup);
+         break;
+
+     case TypeRelationId:
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typinput, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typoutput, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typreceive, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typsend, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typmodin, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typmodout, tuple, newtup);
+         CHECK_PROC_INSTALL_HANDLER(pg_type, typanalyze, tuple, newtup);
+         break;
+     }
+ }
+
+ bool
+ sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup,
+                        uint32 perms, bool abort)
+ {
+     security_class_t tclass;
+     access_vector_t av = 0;
+     bool rc = true;
+
+     Assert(HeapTupleIsValid(tuple));
+
+     if ((perms & (SEPGSQL_PERMS_INSERT | SEPGSQL_PERMS_UPDATE)) != 0)
+         sepgsqlCheckProcedureInstall(rel, tuple, newtup);
+
+     tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple);
+
+     switch (tclass)
+     {
+         case SECCLASS_DB_DATABASE:
+             av = sepgsqlPermsToDatabaseAv(perms, tuple, newtup);
+             break;
+
+         case SECCLASS_DB_TABLE:
+             av = sepgsqlPermsToTableAv(perms, tuple, newtup);
+             break;
+
+         case SECCLASS_DB_PROCEDURE:
+             av = sepgsqlPermsToProcedureAv(perms, tuple, newtup);
+             break;
+
+         case SECCLASS_DB_COLUMN:
+             av = sepgsqlPermsToColumnAv(perms, tuple, newtup);
+             break;
+
+         case SECCLASS_DB_BLOB:
+             av = sepgsqlPermsToBlobAv(perms, tuple, newtup);
+             break;
+
+         default: /* SECCLASS_DB_TUPLE */
+             if (sepostgresql_row_level)
+                 av = sepgsqlPermsToTupleAv(perms, tuple, newtup);
+             break;
+     }
+
+     if (av)
+     {
+         const char *audit_name
+             = sepgsqlTupleName(RelationGetRelid(rel), tuple);
+
+         if (abort)
+         {
+             sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                        tclass, av, audit_name);
+         }
+         else
+         {
+             rc = sepgsqlClientHasPermissionNoAbort(HeapTupleGetSecLabel(tuple),
+                                                    tclass, av, audit_name);
+         }
+     }
+
+     return rc;
+ }
+
+ /*
+  * sepgsqlCheckModuleInstallPerms
+  *
+  * It checks client's privilege to install a new shared loadable file.
+  */
+ void
+ sepgsqlCheckModuleInstallPerms(const char *filename)
+ {
+     security_context_t file_context;
+     Form_pg_database dbform;
+     HeapTuple dbtup;
+     char *fullpath;
+
+     /* (client) <-- db_database:module_install --> (database) */
+     dbtup = SearchSysCache(DATABASEOID,
+                            ObjectIdGetDatum(MyDatabaseId),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(dbtup))
+         elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId);
+
+     dbform = (Form_pg_database) GETSTRUCT(dbtup);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(dbtup),
+                                SECCLASS_DB_DATABASE,
+                                DB_DATABASE__INSTALL_MODULE,
+                                NameStr(dbform->datname));
+     ReleaseSysCache(dbtup);
+
+     /* (client) <-- db_databse:module_install --> (*.so file) */
+     fullpath = expand_dynamic_library_name(filename);
+     if (getfilecon_raw(fullpath, &file_context) < 0)
+         ereport(ERROR,
+                 (errcode_for_file_access(),
+                  errmsg("could not access file \"%s\": %m", fullpath)));
+     PG_TRY();
+     {
+         sepgsqlComputePermission(sepgsqlGetClientContext(),
+                                  file_context,
+                                  SECCLASS_DB_DATABASE,
+                                  DB_DATABASE__INSTALL_MODULE,
+                                  fullpath);
+     }
+     PG_CATCH();
+     {
+         freecon(file_context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(file_context);
+ }
+
+ /*
+  * sepgsqlSetDefaultContext
+  *
+  * This function attach a proper security context for a newly inserted tuple,
+  * refering the security policy.
+  * In the default, any tuple inherits the security context of its table.
+  * However, we have several exception for some of system catalog. It come from
+  * TYPE_TRANSITION rules in the security policy.
+  */
+ static Oid
+ sepgsqlDefaultDatabaseContext(Relation rel, HeapTuple tuple)
+ {
+     security_context_t newcon;
+
+     newcon = sepgsqlComputeCreateContext(sepgsqlGetClientContext(),
+                                          sepgsqlGetClientContext(),
+                                          SECCLASS_DB_DATABASE);
+     return pgaceSecurityLabelToSid(newcon);
+ }
+
+ static Oid
+ sepgsqlDefaultTableContext(Relation rel, HeapTuple tuple)
+ {
+     return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                   SECCLASS_DB_TABLE);
+ }
+
+ static Oid
+ sepgsqlDefaultProcedureContext(Relation rel, HeapTuple tuple)
+ {
+     return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                   SECCLASS_DB_PROCEDURE);
+ }
+
+ static Oid
+ sepgsqlDefaultColumnContext(Relation rel, HeapTuple tuple)
+ {
+     Form_pg_attribute attForm;
+     Oid tblsid;
+
+     attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+     if (IsBootstrapProcessingMode() &&
+         (attForm->attrelid == TypeRelationId ||
+          attForm->attrelid == ProcedureRelationId ||
+          attForm->attrelid == AttributeRelationId ||
+          attForm->attrelid == RelationRelationId))
+     {
+         /*
+          * We cannot access relation caches on very early phase
+          * in bootstrap, so it assumes tables has default security
+          * context and unlabeled by initdb.
+          */
+         tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                         SECCLASS_DB_TABLE);
+     }
+     else
+     {
+         HeapTuple reltup
+             = SearchSysCache(RELOID,
+                              ObjectIdGetDatum(attForm->attrelid),
+                              0, 0, 0);
+         if (!HeapTupleIsValid(reltup))
+             elog(ERROR, "SELinux: cache lookup failed for relation: %u",
+                  attForm->attrelid);
+
+         tblsid = HeapTupleGetSecLabel(reltup);
+
+         ReleaseSysCache(reltup);
+     }
+
+     return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_COLUMN);
+ }
+
+ static Oid
+ sepgsqlDefaultTupleContext(Relation rel, HeapTuple tuple)
+ {
+     Oid tblsid;
+
+     if (IsBootstrapProcessingMode() &&
+         (RelationGetRelid(rel) == TypeRelationId ||
+          RelationGetRelid(rel) == ProcedureRelationId ||
+          RelationGetRelid(rel) == AttributeRelationId ||
+          RelationGetRelid(rel) == RelationRelationId))
+     {
+         /*
+          * We cannot access relation caches on very early phase
+          * in bootstrap, so it assumes tables has default security
+          * context and unlabeled by initdb.
+          */
+         tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                         SECCLASS_DB_TABLE);
+     }
+     else
+     {
+         HeapTuple reltup
+             = SearchSysCache(RELOID,
+                              ObjectIdGetDatum(RelationGetRelid(rel)),
+                              0, 0, 0);
+         if (!HeapTupleIsValid(reltup))
+             elog(ERROR, "SELinux: cache lookup failed for relation: %u",
+                  RelationGetRelid(rel));
+
+         tblsid = HeapTupleGetSecLabel(reltup);
+
+         ReleaseSysCache(reltup);
+     }
+
+     return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_TUPLE);
+ }
+
+ static Oid
+ sepgsqlDefaultBlobContext(Relation rel, HeapTuple tuple)
+ {
+     /*
+      * NOTE:
+      * A new tuple to be inserted into pg_largeobject inherits
+      * a security context of prior tuples of same large object.
+      * The "SnapshotNow" is available for this purpose because
+      * lo_create() invokes CommandCounterIncrement() just after
+      * creation of a new large object.
+      *
+      * If we can find no prior tuples, it means this action to
+      * insert the first page, or client invokes INSERT INTO ...
+      * with multiple tuples with same loid. However, these
+      * tuples are labeled by same TYPE_TRANSITION rules in both
+      * cases. So, there are no differences.
+      */
+     Form_pg_largeobject loForm
+         = (Form_pg_largeobject) GETSTRUCT(tuple);
+     ScanKeyData        skey;
+     SysScanDesc        scan;
+     HeapTuple        lotup;
+     Oid                newsid = InvalidOid;
+
+     ScanKeyInit(&skey,
+                 Anum_pg_largeobject_loid,
+                 BTEqualStrategyNumber, F_OIDEQ,
+                 ObjectIdGetDatum(loForm->loid));
+     scan = systable_beginscan(rel,
+                               LargeObjectLOidPNIndexId, true,
+                               SnapshotNow, 1, &skey);
+     while ((lotup = systable_getnext(scan)) != NULL)
+     {
+         newsid = HeapTupleGetSecLabel(lotup);
+         if (OidIsValid(newsid))
+             break;
+     }
+     systable_endscan(scan);
+
+     if (!OidIsValid(newsid))
+     {
+         newsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(),
+                                         SECCLASS_DB_BLOB);
+     }
+
+     return newsid;
+ }
+
+ void
+ sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple)
+ {
+     security_class_t tclass;
+     Oid newsid;
+
+     Assert(HeapTupleHasSecLabel(tuple));
+     tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple);
+
+     switch (tclass)
+     {
+         case SECCLASS_DB_DATABASE:
+             newsid = sepgsqlDefaultDatabaseContext(rel, tuple);
+             break;
+         case SECCLASS_DB_TABLE:
+             newsid = sepgsqlDefaultTableContext(rel, tuple);
+             break;
+         case SECCLASS_DB_PROCEDURE:
+             newsid = sepgsqlDefaultProcedureContext(rel, tuple);
+             break;
+         case SECCLASS_DB_COLUMN:
+             newsid = sepgsqlDefaultColumnContext(rel, tuple);
+             break;
+         case SECCLASS_DB_BLOB:
+             newsid = sepgsqlDefaultBlobContext(rel, tuple);
+             break;
+         default: /* SECCLASS_DB_TUPLE */
+             newsid = sepgsqlDefaultTupleContext(rel, tuple);
+             break;
+     }
+
+     HeapTupleSetSecLabel(tuple, newsid);
+ }
diff -Nrpc base/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c
*** base/src/backend/security/sepgsql/proxy.c    Thu Jan  1 09:00:00 1970
--- sepgsql/src/backend/security/sepgsql/proxy.c    Fri Jan 23 13:05:55 2009
***************
*** 0 ****
--- 1,1097 ----
+ /*
+  * src/backend/security/sepgsql/proxy.c
+  *    Proxying the given Query trees via SE-PostgreSQL
+  *
+  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #include "postgres.h"
+
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/sysattr.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_attribute.h"
+ #include "catalog/pg_class.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_database.h"
+ #include "catalog/pg_largeobject.h"
+ #include "catalog/pg_operator.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_security.h"
+ #include "catalog/pg_trigger.h"
+ #include "catalog/pg_type.h"
+ #include "executor/executor.h"
+ #include "nodes/nodeFuncs.h"
+ #include "nodes/security.h"
+ #include "optimizer/clauses.h"
+ #include "optimizer/plancat.h"
+ #include "optimizer/prep.h"
+ #include "optimizer/tlist.h"
+ #include "parser/parsetree.h"
+ #include "security/pgace.h"
+ #include "storage/lock.h"
+ #include "utils/array.h"
+ #include "utils/fmgroids.h"
+ #include "utils/fmgrtab.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+ /*
+  * sepgsqlWalkerContext
+  *
+  * This structure holds a context during analyzing a given query.
+  * selist is a list of SEvalItemXXX objects to enumerate appared
+  * tables and columns. These are evaluated later, just before
+  * executing query.
+  * is_internal_use shows the current state whether the current
+  * Node is chained with target list, or conditional clause.
+  */
+ typedef struct sepgsqlWalkerContext
+ {
+     struct sepgsqlWalkerContext *parent;
+     Query  *query;    /* Query structure of current layer */
+     List   *selist;    /* list of SEvalItemXXX */
+     bool    is_internal_use;
+ } sepgsqlWalkerContext;
+
+ #define seitem_index_to_attno(index)            \
+     ((index) + FirstLowInvalidHeapAttributeNumber + 1)
+ #define seitem_attno_to_index(attno)            \
+     ((attno) - FirstLowInvalidHeapAttributeNumber - 1)
+
+
+ /*
+  * addEvalRelation
+  * addEvalRelationRTE
+  *
+  * These functions add a given relation into selist, if it is not
+  * contained yet. In addition, addEvalRelationRTE also marks required
+  * permissions on rte->pgaceTuplePerms. It is delivered to Scan object
+  * and we can use it on ExecScan hook to apply tuple-level access
+  * controls.
+  */
+ static List *
+ addEvalRelation(List *selist, Oid relid, bool inh, uint32 perms)
+ {
+     SelinuxEvalItem *seitem;
+     Form_pg_class relForm;
+     HeapTuple tuple;
+     ListCell *l;
+
+     foreach (l, selist)
+     {
+         seitem = (SelinuxEvalItem *) lfirst(l);
+         Assert(IsA(seitem, SelinuxEvalItem));
+
+         if (seitem->relid == relid && seitem->inh == inh)
+         {
+             seitem->relperms |= perms;
+             return selist;
+         }
+     }
+
+     /* not found, so create a new one */
+     tuple = SearchSysCache(RELOID,
+                            ObjectIdGetDatum(relid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "cache lookup failed for relation %u", relid);
+     relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+     seitem = makeNode(SelinuxEvalItem);
+     seitem->relid = relid;
+     seitem->inh = inh;
+     seitem->relperms = perms;
+     seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1;
+     seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32));
+
+     ReleaseSysCache(tuple);
+
+     return lappend(selist, seitem);
+ }
+
+ static List *
+ addEvalRelationRTE(List *selist, RangeTblEntry *rte, uint32 perms)
+ {
+     rte->pgaceTuplePerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0);
+     rte->pgaceTuplePerms |=    (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0);
+
+     return addEvalRelation(selist, rte->relid, rte->inh, perms);
+ }
+
+ /*
+  * addEvalAttribute
+  * addEvalAttributeRTE
+  *
+  * These functions add a given attribute into selist, if it is not
+  * contained yet. In addition, addEvalAttributeRTE also marks required
+  * permissions on rte->pgaceTuplePerms. It is delivered to Scan object
+  * and we can use it on ExecScan hook to apply tuple-level access
+  * controls.
+  */
+ static List *
+ addEvalAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms)
+ {
+     SelinuxEvalItem *seitem;
+     Form_pg_class relForm;
+     HeapTuple tuple;
+     ListCell *l;
+     int index = seitem_attno_to_index(attno);
+
+     foreach (l, selist)
+     {
+         seitem = (SelinuxEvalItem *) lfirst(l);
+         Assert(IsA(seitem, SelinuxEvalItem));
+
+         if (seitem->relid == relid && seitem->inh == inh)
+         {
+             if (index >= seitem->nattrs)
+             {
+                 uint32 *attperms, nattrs;
+
+                 /*
+                  * NOTE: the following step has a possibility that
+                  * index number overs seitem->nattrs
+                  *
+                  * 1. PREPARE p AS SELECT t FROM t;
+                  * 2. ALTER TABLE t ADD COLUMN x int;
+                  * 3. EXECUTE p;
+                  *
+                  * Because whole-row-reference is extracted to
+                  * references to all the user columns, so table
+                  * may have different number of columns between
+                  * state.1 and state.3.
+                  * In this case, we need to rebuild seitem->attperms
+                  */
+
+                 tuple = SearchSysCache(RELOID,
+                                        ObjectIdGetDatum(relid),
+                                        0, 0, 0);
+                 if (!HeapTupleIsValid(tuple))
+                     elog(ERROR, "cache lookup failed for relation %u", relid);
+                 relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+                 nattrs = seitem_attno_to_index(relForm->relnatts) + 1;
+                 attperms = palloc0(nattrs * sizeof(uint32));
+                 memcpy(attperms, seitem->attperms,
+                        seitem->nattrs * sizeof(uint32));
+                 seitem->nattrs = nattrs;
+                 seitem->attperms = attperms;
+
+                 ReleaseSysCache(tuple);
+             }
+
+             if (index < 0 || index >= seitem->nattrs)
+                 elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u",
+                      attno, relid);
+
+             seitem->attperms[index] |= perms;
+
+             return selist;
+         }
+     }
+
+     /* not found, so create a new one */
+     tuple = SearchSysCache(RELOID,
+                            ObjectIdGetDatum(relid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "cache lookup failed for relation %u", relid);
+     relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+     seitem = makeNode(SelinuxEvalItem);
+     seitem->relid = relid;
+     seitem->inh = inh;
+     seitem->relperms = 0;
+     seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1;
+     seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32));
+     if (index < 0 || index >= seitem->nattrs)
+         elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u",
+              attno, relid);
+     seitem->attperms[index] |= perms;
+
+     ReleaseSysCache(tuple);
+
+     return lappend(selist, seitem);
+ }
+
+ static List *
+ addEvalAttributeRTE(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms)
+ {
+     uint32        tbl_perms = 0;
+
+     tbl_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 0);
+     tbl_perms |= (perms & DB_COLUMN__SELECT ? DB_TABLE__SELECT : 0);
+     tbl_perms |= (perms & DB_COLUMN__INSERT ? DB_TABLE__INSERT : 0);
+     tbl_perms |= (perms & DB_COLUMN__UPDATE ? DB_TABLE__UPDATE : 0);
+     selist = addEvalRelationRTE(selist, rte, tbl_perms);
+
+     /*
+      * Special care for pg_largeobject.data
+      */
+     if ((perms & DB_COLUMN__SELECT) != 0 &&
+         rte->relid == LargeObjectRelationId &&
+         attno == Anum_pg_largeobject_data)
+         rte->pgaceTuplePerms |= SEPGSQL_PERMS_READ;
+
+     return addEvalAttribute(selist, rte->relid, rte->inh, attno, perms);
+ }
+
+ /*
+  * addEvalForeignKeyConstraint
+  *
+  * This function add special case handling for PK/FK constraints.
+  * invoke trigger function requires to access rights for all attribute
+  *
+  */
+ static List *
+ addEvalForeignKeyConstraint(List *selist, Form_pg_trigger trigger)
+ {
+     HeapTuple contup;
+     Datum attdat;
+     ArrayType *attrs;
+     int index;
+     int16 *attnum;
+     bool isnull;
+
+     contup = SearchSysCache(CONSTROID,
+                             ObjectIdGetDatum(trigger->tgconstraint),
+                             0, 0, 0);
+     if (!HeapTupleIsValid(contup))
+         elog(ERROR, "SELinux: cache lookup failed for constraint %u",
+              trigger->tgconstrrelid);
+
+     if (trigger->tgfoid == F_RI_FKEY_CHECK_INS ||
+         trigger->tgfoid == F_RI_FKEY_CHECK_UPD)
+         attdat = SysCacheGetAttr(CONSTROID, contup,
+                                  Anum_pg_constraint_conkey, &isnull);
+     else
+         attdat = SysCacheGetAttr(CONSTROID, contup,
+                                  Anum_pg_constraint_confkey, &isnull);
+     if (isnull)
+         elog(ERROR, "null PK/FK for constraint %u",
+              trigger->tgconstrrelid);
+     attrs = DatumGetArrayTypeP(attdat);
+
+     if (ARR_NDIM(attrs) != 1 ||
+         ARR_HASNULL(attrs) ||
+         ARR_ELEMTYPE(attrs) != INT2OID)
+         elog(ERROR, "SELinux: unexpected constraint %u", trigger->tgconstrrelid);
+
+     attnum = (int16 *) ARR_DATA_PTR(attrs);
+     for (index = 0; index < ARR_DIMS(attrs)[0]; index++)
+         selist = addEvalAttribute(selist, trigger->tgrelid, false,
+                                   attnum[index], DB_COLUMN__SELECT);
+
+     ReleaseSysCache(contup);
+
+     return selist;
+ }
+
+ /*
+  * addEvalTriggerFunction
+  *
+  * This function adds needed items into selist, to execute a trigger
+  * function. At least, it requires permission set to execute a function
+  * configured as a trigger, to select a table and whole of columns
+  * because whole of a tuple is delivered to trigger functions.
+  */
+ static List *
+ addEvalTriggerFunction(List *selist, Oid relid, int cmdType)
+ {
+     Relation    rel;
+     SysScanDesc scan;
+     ScanKeyData skey;
+     HeapTuple    tuple;
+
+     rel = heap_open(TriggerRelationId, AccessShareLock);
+     ScanKeyInit(&skey,
+                 Anum_pg_trigger_tgrelid,
+                 BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid));
+     scan = systable_beginscan(rel, TriggerRelidNameIndexId,
+                               true, SnapshotNow, 1, &skey);
+     while (HeapTupleIsValid((tuple = systable_getnext(scan))))
+     {
+         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
+         Form_pg_class relForm;
+         HeapTuple reltup;
+
+         /*
+          * Skip not-invoked triggers
+          */
+         if (!trigForm->tgenabled)
+             continue;
+         if (cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype))
+             continue;
+         if (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype))
+             continue;
+         if (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype))
+             continue;
+
+         /*
+          * per STATEMENT trigger cannot refer whole of a tuple
+          */
+         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
+             continue;
+
+         /*
+          * BEFORE-ROW-INSERT trigger cannot refer whole of a tuple
+          */
+         if (TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
+             TRIGGER_FOR_INSERT(trigForm->tgtype))
+             continue;
+
+         reltup = SearchSysCache(RELOID,
+                                 ObjectIdGetDatum(relid),
+                                 0, 0, 0);
+         relForm = (Form_pg_class) GETSTRUCT(reltup);
+
+         selist = addEvalRelation(selist, relid, false, DB_TABLE__SELECT);
+
+         if (RI_FKey_trigger_type(trigForm->tgfoid) != RI_TRIGGER_NONE)
+             selist = addEvalForeignKeyConstraint(selist, trigForm);
+         else
+             selist = addEvalAttribute(selist, relid, false,
+                                       0, DB_COLUMN__SELECT);
+         ReleaseSysCache(reltup);
+     }
+     systable_endscan(scan);
+     heap_close(rel, AccessShareLock);
+
+     return selist;
+ }
+
+ /*
+  * sepgsqlExprWalker
+  *
+  * This function walks on the given expression tree to pick up
+  * all the appeared tables and columns. Their identifiers are
+  * chains on swc->selist to evaluate permissions on them later.
+  *
+  * walkVarHelper picks up an accessed column and its contained
+  * table, and chains them on swc->selist.
+  * When swc->is_internal_use is true, it means this reference
+  * is checked as "use" permission because its contents are
+  * consumed internally, and not to be returned to client directly.
+  * Otherwise, "select" permission is applied.
+  *
+  * walkQueryHelper walks on Query structure.
+  * The reason why we don't use query_tree_walker() is that
+  * SE-PostgreSQL need to apply different permission between
+  * targetList and havingQual, for example.
+  */
+
+ static bool
+ sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc);
+
+ static void
+ sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc,
+                        bool is_internal_use);
+
+ /*
+  * wholeRefJoinWalker
+  *
+  * A corner case need to invoke this walker function.
+  * When we use whole-row-reference on RTE_JOIN relation,
+  * it should be extracted to whole-row-references on
+  * sources relations.
+  *
+  * EXAMPLE:
+  *   SELECT t4 FROM (t1 JOIN (t2 JOIN t3 USING (a)) USING (b)) AS t4;
+  *
+  * Because RangeTblEntry with RTE_JOIN does not have any identifiers
+  * of its source relations, we have to scan Query->jointree again to
+  * look up sources again. :(
+  */
+ typedef struct
+ {
+     Query *query;
+     int rtindex;
+     /*
+      * rtindex == 0 means we are now walking on the required JoinExpr
+      * or its leafs, so we need to pick up all the appeared relations
+      * under the JoinExpr in this case.
+      */
+     List *selist;
+     uint32 perms;
+ } wholeRefJoinWalkerContext;
+
+ static bool
+ wholeRefJoinWalker(Node *node, wholeRefJoinWalkerContext *jwc)
+ {
+     if (!node)
+         return false;
+
+     if (IsA(node, JoinExpr))
+     {
+         JoinExpr *j = (JoinExpr *) node;
+
+         if (j->rtindex == jwc->rtindex)
+         {
+             int rtindex_backup = jwc->rtindex;
+             bool rc;
+
+             jwc->rtindex = 0;
+             rc = expression_tree_walker(node, wholeRefJoinWalker, jwc);
+             jwc->rtindex = rtindex_backup;
+
+             return rc;
+         }
+     }
+     else if (IsA(node, RangeTblRef) && jwc->rtindex == 0)
+     {
+         RangeTblRef *rtr = (RangeTblRef *) node;
+         RangeTblEntry *rte = rt_fetch(rtr->rtindex,
+                                       jwc->query->rtable);
+         if (rte->rtekind == RTE_RELATION)
+         {
+             jwc->selist = addEvalAttributeRTE(jwc->selist, rte, 0, jwc->perms);
+         }
+     }
+     return expression_tree_walker(node, wholeRefJoinWalker, jwc);
+ }
+
+ static void
+ walkVarHelper(Var *var, sepgsqlWalkerContext *swc)
+ {
+     sepgsqlWalkerContext *cur = swc;
+     Query           *query;
+     RangeTblEntry  *rte;
+     int                lv;
+
+     Assert(IsA(var, Var));
+
+     for (lv = var->varlevelsup; lv > 0; lv--)
+     {
+         Assert(cur->parent != NULL);
+         cur = cur->parent;
+     }
+     query = cur->query;
+
+     rte = rt_fetch(var->varno, query->rtable);
+     Assert(IsA(rte, RangeTblEntry));
+
+     if (rte->rtekind == RTE_RELATION)
+     {
+         uint32 perms = swc->is_internal_use
+             ? DB_COLUMN__USE : DB_COLUMN__SELECT;
+
+         swc->selist = addEvalAttributeRTE(swc->selist, rte,
+                                           var->varattno, perms);
+     }
+     else if (rte->rtekind == RTE_JOIN)
+     {
+         if (var->varattno == 0)
+         {
+             wholeRefJoinWalkerContext jwcData;
+
+             jwcData.query = query;
+             jwcData.rtindex = var->varno;
+             jwcData.selist = swc->selist;
+             jwcData.perms = swc->is_internal_use
+                 ? DB_COLUMN__USE : DB_COLUMN__SELECT;
+
+             wholeRefJoinWalker((Node *)query->jointree, &jwcData);
+             swc->selist = jwcData.selist;
+         }
+         else
+         {
+             Node *node = list_nth(rte->joinaliasvars,
+                                   var->varattno - 1);
+             sepgsqlExprWalker(node, swc);
+         }
+     }
+ }
+
+ static List *
+ walkQueryHelper(Query *query, sepgsqlWalkerContext *swc)
+ {
+     sepgsqlWalkerContext swcData;
+     RangeTblEntry *rte;
+
+     memset(&swcData, 0, sizeof(swcData));
+     swcData.parent = swc;
+     swcData.selist = (!swc ? NIL : swc->selist);
+     swcData.query = query;
+
+     if (query->commandType != CMD_DELETE)
+     {
+         ListCell *l;
+
+         foreach (l, query->targetList)
+         {
+             TargetEntry *tle = lfirst(l);
+             bool is_security = false;
+
+             Assert(IsA(tle, TargetEntry));
+
+             if (tle->resjunk &&
+                 tle->resname &&
+                 strcmp(tle->resname, SecurityLabelAttributeName) == 0)
+                 is_security = true;
+
+             if (tle->resjunk && !is_security)
+             {
+                 sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, true);
+                 continue;
+             }
+
+             sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, false);
+
+             if (query->commandType != CMD_SELECT)
+             {
+                 AttrNumber attno = tle->resno;
+                 uint32 perms;
+
+                 if (is_security)
+                     attno = SecurityLabelAttributeNumber;
+
+                 if (query->commandType == CMD_UPDATE)
+                     perms = DB_COLUMN__UPDATE;
+                 else
+                     perms = DB_COLUMN__INSERT;
+
+                 rte = rt_fetch(query->resultRelation, query->rtable);
+                 Assert(IsA(rte, RangeTblEntry));
+
+                 swcData.selist
+                     = addEvalAttributeRTE(swcData.selist, rte, attno, perms);
+             }
+         }
+     }
+     else
+     {
+         /* no need to check column-level permission for DELETE */
+         rte = rt_fetch(query->resultRelation, query->rtable);
+         Assert(IsA(rte, RangeTblEntry));
+
+         swcData.selist
+             = addEvalRelationRTE(swcData.selist, rte, DB_TABLE__DELETE);
+     }
+
+     sepgsqlExprWalkerFlags((Node *) query->returningList, &swcData, false);
+     sepgsqlExprWalkerFlags((Node *) query->jointree, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->setOperations, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->havingQual, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->sortClause, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->groupClause, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->limitOffset, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->limitCount, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->cteList, &swcData, true);
+     sepgsqlExprWalkerFlags((Node *) query->windowClause, &swcData, true);
+
+     return swcData.selist;
+ }
+
+ static void
+ walkRangeTblRefHelper(RangeTblRef *rtr, sepgsqlWalkerContext *swc)
+ {
+     Query *query = swc->query;
+     RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+     Assert(IsA(rte, RangeTblEntry));
+
+     switch (rte->rtekind)
+     {
+     case RTE_RELATION:
+         if (rtr->rtindex != query->resultRelation)
+             swc->selist = addEvalRelationRTE(swc->selist, rte,
+                                              DB_TABLE__SELECT);
+         break;
+
+     case RTE_SUBQUERY:
+         swc->selist = walkQueryHelper(rte->subquery, swc);
+         break;
+
+     case RTE_FUNCTION:
+         sepgsqlExprWalker(rte->funcexpr, swc);
+         break;
+
+     case RTE_VALUES:
+         sepgsqlExprWalker((Node *) rte->values_lists, swc);
+         break;
+
+     default:
+         /* do nothing */
+         break;
+     }
+ }
+
+ static void
+ walkSortGroupClauseHelper(SortGroupClause *sgc, sepgsqlWalkerContext *swc)
+ {
+     Query *query = swc->query;
+     TargetEntry *tle
+         = get_sortgroupref_tle(sgc->tleSortGroupRef,
+                                query->targetList);
+
+     Assert(IsA(tle, TargetEntry));
+
+     sepgsqlExprWalker((Node *) tle->expr, swc);
+ }
+
+ static bool
+ sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc)
+ {
+     if (node == NULL)
+         return false;
+     else if (IsA(node, Var))
+         walkVarHelper((Var *) node, swc);
+     else if (IsA(node, RangeTblRef))
+         walkRangeTblRefHelper((RangeTblRef *) node, swc);
+     else if (IsA(node, Query))
+     {
+         swc->selist
+             = walkQueryHelper((Query *) node, swc);
+     }
+     else if (IsA(node, SortGroupClause))
+     {
+         walkSortGroupClauseHelper((SortGroupClause *) node, swc);
+
+         return false;
+     }
+     return expression_tree_walker(node, sepgsqlExprWalker, (void *) swc);
+ }
+
+ static void
+ sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc,
+                        bool is_internal_use)
+ {
+     bool        saved_is_internal_use = swc->is_internal_use;
+
+     swc->is_internal_use = is_internal_use;
+     sepgsqlExprWalker(node, swc);
+     swc->is_internal_use = saved_is_internal_use;
+ }
+
+ /*
+  * sepgsqlPostQueryRewrite
+  *
+  * This function is invoked just after given queries are rewritten
+  * via query-rewritter phase. It walks on given query trees to
+  * picks up all appeared tables and columns, and to chains the list
+  * of them on query->pgaceItem.
+  * This list is used to evaluate permissions later, just before
+  * the query execution.
+  *
+  * It do nothing for DDL queries, because these are processed in
+  * sepgsqlProcessUtility() hook.
+  */
+ List *
+ sepgsqlPostQueryRewrite(List *queryList)
+ {
+     ListCell   *l;
+
+     foreach (l, queryList)
+     {
+         Query  *query = (Query *) lfirst(l);
+
+         Assert(IsA(query, Query));
+
+         if (query->commandType == CMD_SELECT ||
+             query->commandType == CMD_UPDATE ||
+             query->commandType == CMD_INSERT ||
+             query->commandType == CMD_DELETE)
+         {
+             query->pgaceItem
+                 = (Node *) walkQueryHelper(query, NULL);
+         }
+     }
+
+     return queryList;
+ }
+
+ /*
+  * checkSelinuxEvalItem
+  *   checks give SelinuxEvalItem object based on the security
+  *   policy of SELinux.
+  */
+ static void
+ checkSelinuxEvalItem(SelinuxEvalItem *seitem)
+ {
+     Form_pg_class relForm;
+     Form_pg_attribute attForm;
+     HeapTuple tuple;
+     AttrNumber attno;
+     const char *audit_name;
+     int index;
+
+     Assert(IsA(seitem, SelinuxEvalItem));
+
+     /*
+      * Prevent to write pg_security by hand
+      */
+     if (seitem->relid == SecurityRelationId &&
+         (seitem->relperms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)))
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not modify pg_security by hand")));
+
+     /*
+      * Permission checks on table
+      */
+     tuple = SearchSysCache(RELOID,
+                            ObjectIdGetDatum(seitem->relid),
+                            0, 0, 0);
+     if (!HeapTupleIsValid(tuple))
+         elog(ERROR, "SELinux: cache lookup failed for relation: %u",
+              seitem->relid);
+     relForm = (Form_pg_class) GETSTRUCT(tuple);
+     if (relForm->relkind != RELKIND_RELATION)
+     {
+         ReleaseSysCache(tuple);
+         return;
+     }
+
+     audit_name = sepgsqlTupleName(RelationRelationId, tuple);
+     sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                SECCLASS_DB_TABLE,
+                                seitem->relperms,
+                                audit_name);
+     ReleaseSysCache(tuple);
+
+     /*
+      * Expand whole-row-reference
+      */
+     index = seitem_attno_to_index(InvalidAttrNumber);
+     if (seitem->attperms[index] != 0)
+     {
+         uint32 perms = seitem->attperms[index];
+
+         seitem->attperms[index] = 0;
+         for (index++; index < seitem->nattrs; index++)
+             seitem->attperms[index] |= perms;
+     }
+
+     /*
+      * Permission checks on columns
+      */
+     for (index = 0; index < seitem->nattrs; index++)
+     {
+         if (seitem->attperms[index] == 0)
+             continue;
+
+         attno = seitem_index_to_attno(index);
+         tuple = SearchSysCache(ATTNUM,
+                                ObjectIdGetDatum(seitem->relid),
+                                Int16GetDatum(attno),
+                                0, 0);
+         if (!HeapTupleIsValid(tuple))
+             elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u",
+                  attno, seitem->relid);
+         attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+         /*
+          * NOTE: When user uses whole-row-reference on a table
+          * which has already dropped column, the column can have
+          * non-zero required permissions, but being ignorable.
+          */
+         if (attForm->attisdropped)
+         {
+             ReleaseSysCache(tuple);
+             continue;
+         }
+
+         audit_name = sepgsqlTupleName(AttributeRelationId, tuple);
+         sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple),
+                                    SECCLASS_DB_COLUMN,
+                                    seitem->attperms[index],
+                                    audit_name);
+         ReleaseSysCache(tuple);
+     }
+ }
+
+ static List *
+ expandEvalItemInheritance(List *selist)
+ {
+     List       *result = NIL;
+     List       *inherits;
+     ListCell   *l, *i;
+     int index;
+
+     foreach (l, selist)
+     {
+         SelinuxEvalItem *seitem = lfirst(l);
+
+         Assert(IsA(seitem, SelinuxEvalItem));
+
+         if (!seitem->inh)
+         {
+             result = lappend(result, seitem);
+             continue;
+         }
+
+         inherits = find_all_inheritors(seitem->relid);
+         foreach (i, inherits)
+         {
+             result = addEvalRelation(result, lfirst_oid(i), false,
+                                      seitem->relperms);
+             for (index = 0; index < seitem->nattrs; index++)
+             {
+                 Oid relid_inh = lfirst_oid(i);
+                 AttrNumber attno;
+
+                 if (seitem->attperms[index] == 0)
+                     continue;
+
+                 attno = seitem_index_to_attno(index);
+                 if (attno < 1 || seitem->relid == relid_inh)
+                 {
+                     /*
+                      * If attribute is system-column or whole-row-reference,
+                      * or inherit relation is itself, we don't need to fix up
+                      * attribute number.
+                      */
+                     result = addEvalAttribute(result, relid_inh, false,
+                                               attno, seitem->attperms[index]);
+                     continue;
+                 }
+                 else
+                 {
+                     char *attname = get_attname(seitem->relid, attno);
+
+                     if (!attname)
+                         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+                              attno, seitem->relid);
+
+                     attno = get_attnum(relid_inh, attname);
+                     if (attno == InvalidAttrNumber)
+                         elog(ERROR, "cache lookup failed for attribute %s of relation %u",
+                              attname, relid_inh);
+
+                     result = addEvalAttribute(result, relid_inh, false,
+                                               attno, seitem->attperms[index]);
+                     pfree(attname);
+                 }
+             }
+         }
+     }
+     return result;
+ }
+
+ /*
+  * sepgsqlExecutorStart
+  *
+  * This function is invoked at the head of ExecutorStart, to evaluate
+  * permissions to access appeared object within the given query.
+  * Query->pgaceItem is a list of SelinuxEvalItem objects generated in
+  * previous phase, and it is copied to PlannedStmt->pgaceItem in the
+  * optimizer.
+  * This functions expand given selist based on table inheritance,
+  * adds additional permissions related to trigger functions, and
+  * expands whole-row-references. Then, these items are evaluated
+  * based on the security policy of SELinux.
+  */
+ void
+ sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags)
+ {
+     PlannedStmt *pstmt = queryDesc->plannedstmt;
+     RangeTblEntry *rte;
+     List       *selist;
+     ListCell   *l;
+
+     /*
+      * EXPLAIN statement does not access any object.
+      */
+     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
+         return;
+
+     if (!pstmt->pgaceItem)
+         return;
+
+     Assert(IsA(pstmt->pgaceItem, List));
+     selist = copyObject(pstmt->pgaceItem);
+
+     /*
+      * expand table inheritances
+      */
+     selist = expandEvalItemInheritance(selist);
+
+     /*
+      * add checks for access via trigger function
+      */
+     foreach(l, pstmt->resultRelations)
+     {
+         Index        rindex = lfirst_int(l);
+
+         rte = rt_fetch(rindex, pstmt->rtable);
+         Assert(IsA(rte, RangeTblEntry));
+
+         selist = addEvalTriggerFunction(selist, rte->relid,
+                                         pstmt->commandType);
+     }
+
+     /*
+      * Check SelinuxEvalItem
+      */
+     foreach (l, selist)
+         checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l));
+ }
+
+ /*
+  * --------------------------------------------------------------
+  * Process Utility hooks
+  * --------------------------------------------------------------
+  */
+
+ /*
+  * sepgsqlProcessUtility
+  *
+  * This function is invoked from the head of ProcessUtility(), and
+  * checks given DDL queries.
+  * SE-PostgreSQL catch most of DDL actions on HeapTuple hooks, but
+  * an exception is TRUNCATE statement.
+  */
+ void
+ sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel)
+ {
+     switch (nodeTag(parsetree))
+     {
+     case T_LoadStmt:
+         {
+             LoadStmt *load = (LoadStmt *)parsetree;
+             sepgsqlCheckModuleInstallPerms(load->filename);
+         }
+         break;
+
+     case T_CreateFdwStmt:
+         {
+             CreateFdwStmt *createFdw = (CreateFdwStmt *)parsetree;
+             sepgsqlCheckModuleInstallPerms(createFdw->library);
+         }
+         break;
+
+     case T_AlterFdwStmt:
+         {
+             AlterFdwStmt *alterFdw = (AlterFdwStmt *)parsetree;
+             if (alterFdw->library)
+                 sepgsqlCheckModuleInstallPerms(alterFdw->library);
+         }
+         break;
+
+     default:
+         /*
+          * do nothing here
+          */
+         break;
+     }
+ }
+
+ /* ----------------------------------------------------------
+  * COPY TO/COPY FROM statement hooks
+  * ---------------------------------------------------------- */
+
+ /*
+  * sepgsqlCopyTable
+  *
+  * This function checks permission on the target table and columns
+  * of COPY statement. We don't place it at sepgsql/hooks.c because
+  * it internally uses addEvalXXXX() interface statically declared.
+  */
+ void
+ sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom)
+ {
+     List       *selist = NIL;
+     ListCell   *l;
+
+     /*
+      * on 'COPY FROM SELECT ...' cases, any checkings are done in select.c
+      */
+     if (rel == NULL)
+         return;
+
+     /*
+      * no need to check non-table relation
+      */
+     if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+         return;
+
+     selist = addEvalRelation(selist, RelationGetRelid(rel), false,
+                              isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT);
+     foreach(l, attNumList)
+     {
+         AttrNumber    attnum = lfirst_int(l);
+
+         selist = addEvalAttribute(selist, RelationGetRelid(rel), false, attnum,
+                                   isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT);
+     }
+
+     /*
+      * check call trigger function
+      */
+     if (isFrom)
+         selist = addEvalTriggerFunction(selist, RelationGetRelid(rel), CMD_INSERT);
+
+     foreach (l, selist)
+         checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l));
+ }
+
+ /*
+  * sepgsqlCopyFile
+  *
+  * This function check permission whether the client can
+  * read from/write to the given file.
+  */
+ void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom)
+ {
+     security_context_t context;
+     security_class_t tclass
+         = sepgsqlFileObjectClass(fdesc, filename);
+
+     if (fgetfilecon_raw(fdesc, &context) < 0)
+         ereport(ERROR,
+                 (errcode(ERRCODE_SELINUX_ERROR),
+                  errmsg("SELinux: could not get context of %s", filename)));
+     PG_TRY();
+     {
+         sepgsqlComputePermission(sepgsqlGetClientContext(),
+                                  context,
+                                  tclass,
+                                  isFrom ? FILE__READ : FILE__WRITE,
+                                  filename);
+     }
+     PG_CATCH();
+     {
+         freecon(context);
+         PG_RE_THROW();
+     }
+     PG_END_TRY();
+     freecon(context);
+ }
+
+ /*
+  * sepgsqlCopyToTuple
+  *
+  * This function check permission to read the given tuple.
+  * If not allowed to read, it returns false to skip COPY TO
+  * this tuple. In the result, any violated tuples are filtered
+  * from the result of COPY TO, as if these are not exist.
+  */
+ bool
+ sepgsqlCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple)
+ {
+     uint32        perms = SEPGSQL_PERMS_SELECT;
+
+     /*
+      * for 'pg_largeobject'
+      */
+     if (RelationGetRelid(rel) == LargeObjectRelationId)
+     {
+         ListCell   *l;
+
+         foreach(l, attNumList)
+         {
+             AttrNumber    attnum = lfirst_int(l);
+
+             if (attnum == Anum_pg_largeobject_data)
+             {
+                 perms |= SEPGSQL_PERMS_READ;
+                 break;
+             }
+         }
+     }
+     return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false);
+ }
diff -Nrpc base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c
*** base/src/backend/storage/file/fd.c    Tue Jan 13 09:22:28 2009
--- sepgsql/src/backend/storage/file/fd.c    Tue Jan 13 09:39:35 2009
*************** FileTruncate(File file, off_t offset)
*** 1291,1296 ****
--- 1291,1303 ----
      return returnCode;
  }

+ int
+ FileRawDescriptor(File file)
+ {
+     Assert(FileIsValid(file));
+
+     return VfdCache[file].fd;
+ }

  /*
   * Routines that want to use stdio (ie, FILE*) should use AllocateFile
diff -Nrpc base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c
*** base/src/backend/storage/ipc/ipci.c    Mon Jan  5 17:36:07 2009
--- sepgsql/src/backend/storage/ipc/ipci.c    Mon Jan  5 17:41:04 2009
***************
*** 25,30 ****
--- 25,31 ----
  #include "postmaster/autovacuum.h"
  #include "postmaster/bgwriter.h"
  #include "postmaster/postmaster.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/ipc.h"
  #include "storage/pg_shmem.h"
*************** CreateSharedMemoryAndSemaphores(bool mak
*** 118,123 ****
--- 119,125 ----
  #ifdef EXEC_BACKEND
          size = add_size(size, ShmemBackendArraySize());
  #endif
+         size = add_size(size, pgaceShmemSize());

          /* freeze the addin request size and include it */
          addin_request_allowed = false;
diff -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c
*** base/src/backend/tcop/fastpath.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/tcop/fastpath.c    Fri Jan 16 17:05:27 2009
***************
*** 28,33 ****
--- 28,34 ----
  #include "miscadmin.h"
  #include "tcop/fastpath.h"
  #include "tcop/tcopprot.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/lsyscache.h"
  #include "utils/snapmgr.h"
*************** HandleFunctionRequest(StringInfo msgBuf)
*** 348,353 ****
--- 349,355 ----
      if (aclresult != ACLCHECK_OK)
          aclcheck_error(aclresult, ACL_KIND_PROC,
                         get_func_name(fid));
+     pgaceCallFunction(&fip->flinfo);

      /*
       * Prepare function call info block and insert arguments.
diff -Nrpc base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c
*** base/src/backend/tcop/pquery.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/tcop/pquery.c    Sat Jan  3 15:58:18 2009
*************** PortalStart(Portal portal, ParamListInfo
*** 573,579 ****
                      Assert(pstmt->returningLists);
                      portal->tupDesc =
                          ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
!                                             false);
                  }

                  /*
--- 573,579 ----
                      Assert(pstmt->returningLists);
                      portal->tupDesc =
                          ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
!                                             false, false, false);
                  }

                  /*
diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c
*** base/src/backend/tcop/utility.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/tcop/utility.c    Fri Jan 23 10:55:35 2009
***************
*** 49,54 ****
--- 49,55 ----
  #include "postmaster/bgwriter.h"
  #include "rewrite/rewriteDefine.h"
  #include "rewrite/rewriteRemove.h"
+ #include "security/pgace.h"
  #include "storage/fd.h"
  #include "tcop/pquery.h"
  #include "tcop/utility.h"
*************** ProcessUtility(Node *parsetree,
*** 258,263 ****
--- 259,266 ----
      if (completionTag)
          completionTag[0] = '\0';

+     pgaceProcessUtility(parsetree, params, isTopLevel);
+
      switch (nodeTag(parsetree))
      {
              /*
diff -Nrpc base/src/backend/utils/adt/acl.c sepgsql/src/backend/utils/adt/acl.c
*** base/src/backend/utils/adt/acl.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/utils/adt/acl.c    Fri Jan 23 10:55:35 2009
*************** static List *cached_membership_roles = N
*** 66,73 ****

  static const char *getid(const char *s, char *n);
  static void putid(char *p, const char *s);
- static Acl *allocacl(int n);
- static void check_acl(const Acl *acl);
  static const char *aclparse(const char *s, AclItem *aip);
  static bool aclitem_match(const AclItem *a1, const AclItem *a2);
  static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
--- 66,71 ----
*************** aclparse(const char *s, AclItem *aip)
*** 347,353 ****
   * RETURNS:
   *        the new Acl
   */
! static Acl *
  allocacl(int n)
  {
      Acl           *new_acl;
--- 345,351 ----
   * RETURNS:
   *        the new Acl
   */
! Acl *
  allocacl(int n)
  {
      Acl           *new_acl;
*************** aclconcat(const Acl *left_acl, const Acl
*** 410,416 ****
  /*
   * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
   */
! static void
  check_acl(const Acl *acl)
  {
      if (ARR_ELEMTYPE(acl) != ACLITEMOID)
--- 408,414 ----
  /*
   * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
   */
! void
  check_acl(const Acl *acl)
  {
      if (ARR_ELEMTYPE(acl) != ACLITEMOID)
diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c
*** base/src/backend/utils/adt/ri_triggers.c    Fri Jan  9 10:09:59 2009
--- sepgsql/src/backend/utils/adt/ri_triggers.c    Fri Jan  9 10:15:15 2009
***************
*** 39,44 ****
--- 39,45 ----
  #include "parser/parse_coerce.h"
  #include "parser/parse_relation.h"
  #include "miscadmin.h"
+ #include "security/pgace.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
*************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl
*** 3264,3269 ****
--- 3265,3272 ----
      int            spi_result;
      Oid            save_userid;
      bool        save_secdefcxt;
+     Datum        rowacl_private = 0;
+     Datum        pgace_private = 0;
      Datum        vals[RI_MAX_NUMKEYS * 2];
      char        nulls[RI_MAX_NUMKEYS * 2];

*************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl
*** 3346,3356 ****
      GetUserIdAndContext(&save_userid, &save_secdefcxt);
      SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);

!     /* Finally we can run the query. */
!     spi_result = SPI_execute_snapshot(qplan,
!                                       vals, nulls,
!                                       test_snapshot, crosscheck_snapshot,
!                                       false, false, limit);

      /* Restore UID */
      SetUserIdAndContext(save_userid, save_secdefcxt);
--- 3349,3371 ----
      GetUserIdAndContext(&save_userid, &save_secdefcxt);
      SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);

!     pgaceBeginPerformCheckFK(query_rel, detectNewRows, save_userid,
!                              &rowacl_private, &pgace_private);
!     PG_TRY();
!     {
!         /* Finally we can run the query. */
!         spi_result = SPI_execute_snapshot(qplan,
!                                           vals, nulls,
!                                           test_snapshot, crosscheck_snapshot,
!                                           false, false, limit);
!     }
!     PG_CATCH();
!     {
!         pgaceEndPerformCheckFK(query_rel, rowacl_private, pgace_private);
!         PG_RE_THROW();
!     }
!     PG_END_TRY();
!     pgaceEndPerformCheckFK(query_rel, rowacl_private, pgace_private);

      /* Restore UID */
      SetUserIdAndContext(save_userid, save_secdefcxt);
diff -Nrpc base/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c
*** base/src/backend/utils/adt/trigfuncs.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/adt/trigfuncs.c    Sat Jan  3 15:58:18 2009
*************** suppress_redundant_updates_trigger(PG_FU
*** 62,78 ****
      newheader = newtuple->t_data;
      oldheader = oldtuple->t_data;

-     /*
-      * We are called before the OID, if any, has been transcribed from the
-      * old tuple to the new (in heap_update).  To avoid a bogus compare
-      * failure, copy the OID now.  But check that someone didn't already put
-      * another OID value into newtuple.  (That's not actually possible at
-      * present, but maybe someday.)
-      */
-      if (trigdata->tg_relation->rd_rel->relhasoids &&
-         !OidIsValid(HeapTupleHeaderGetOid(newheader)))
-         HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader));
-
      /* if the tuple payload is the same ... */
      if (newtuple->t_len == oldtuple->t_len &&
          newheader->t_hoff == oldheader->t_hoff &&
--- 62,67 ----
diff -Nrpc base/src/backend/utils/cache/catcache.c sepgsql/src/backend/utils/cache/catcache.c
*** base/src/backend/utils/cache/catcache.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/cache/catcache.c    Fri Jan 16 17:05:27 2009
*************** ReleaseCatCache(HeapTuple tuple)
*** 1316,1321 ****
--- 1316,1353 ----
          CatCacheRemoveCTup(ct->my_cache, ct);
  }

+ /*
+  * InsertCatCache
+  *
+  * This function enables to refer a tuple recently inserted, using catcache
+  * until next CommandCounterIncrement.
+  */
+ void InsertCatCache(CatCache *cache, HeapTuple tuple)
+ {
+     ScanKeyData skey[4];
+     uint32 hashValue;
+     Index hashIndex;
+     bool isnull;
+     int i;
+
+     /* initialize the search key information */
+     memcpy(skey, cache->cc_skey, sizeof(skey));
+     for (i=0; i < cache->cc_nkeys; i++)
+     {
+         skey[i].sk_argument = heap_getattr(tuple, cache->cc_key[i],
+                                            cache->cc_tupdesc, &isnull);
+         Assert(!isnull);
+     }
+
+     /* find the hash bucket in which to look for the tuple */
+     if (cache->cc_tupdesc == NULL)
+         CatalogCacheInitializeCache(cache);
+     hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, skey);
+     hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+
+     /* Insert a new tuple */
+     CatalogCacheCreateEntry(cache, tuple, hashValue, hashIndex, false);
+ }

  /*
   *    SearchCatCacheList
diff -Nrpc base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c
*** base/src/backend/utils/cache/plancache.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/cache/plancache.c    Sat Jan  3 15:58:18 2009
*************** PlanCacheComputeResultDesc(List *stmt_li
*** 847,858 ****
              if (IsA(node, Query))
              {
                  query = (Query *) node;
!                 return ExecCleanTypeFromTL(query->targetList, false);
              }
              if (IsA(node, PlannedStmt))
              {
                  pstmt = (PlannedStmt *) node;
!                 return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
              }
              /* other cases shouldn't happen, but return NULL */
              break;
--- 847,860 ----
              if (IsA(node, Query))
              {
                  query = (Query *) node;
!                 return ExecCleanTypeFromTL(query->targetList,
!                                            false, false, false);
              }
              if (IsA(node, PlannedStmt))
              {
                  pstmt = (PlannedStmt *) node;
!                 return ExecCleanTypeFromTL(pstmt->planTree->targetlist,
!                                            false, false, false);
              }
              /* other cases shouldn't happen, but return NULL */
              break;
*************** PlanCacheComputeResultDesc(List *stmt_li
*** 863,875 ****
              {
                  query = (Query *) node;
                  Assert(query->returningList);
!                 return ExecCleanTypeFromTL(query->returningList, false);
              }
              if (IsA(node, PlannedStmt))
              {
                  pstmt = (PlannedStmt *) node;
                  Assert(pstmt->returningLists);
!                 return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
              }
              /* other cases shouldn't happen, but return NULL */
              break;
--- 865,879 ----
              {
                  query = (Query *) node;
                  Assert(query->returningList);
!                 return ExecCleanTypeFromTL(query->returningList,
!                                            false, false, false);
              }
              if (IsA(node, PlannedStmt))
              {
                  pstmt = (PlannedStmt *) node;
                  Assert(pstmt->returningLists);
!                 return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
!                                            false, false, false);
              }
              /* other cases shouldn't happen, but return NULL */
              break;
diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c
*** base/src/backend/utils/cache/relcache.c    Fri Jan 23 10:23:37 2009
--- sepgsql/src/backend/utils/cache/relcache.c    Fri Jan 23 10:55:35 2009
***************
*** 55,60 ****
--- 55,61 ----
  #include "optimizer/prep.h"
  #include "optimizer/var.h"
  #include "rewrite/rewriteDefine.h"
+ #include "security/pgace.h"
  #include "storage/fd.h"
  #include "storage/lmgr.h"
  #include "storage/smgr.h"
*************** AllocateRelationDesc(Relation relation,
*** 329,335 ****
      /* initialize relation tuple form */
      relation->rd_rel = relationForm;

!     /* and allocate attribute tuple form storage */
      relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
                                                 relationForm->relhasoids);
      /* which we mark as a reference-counted tupdesc */
--- 330,342 ----
      /* initialize relation tuple form */
      relation->rd_rel = relationForm;

!     /*
!      * and allocate attribute tuple form storage
!      *
!      * Please note that relation->rd_att->tdhasrowacl and tdhasseclabel
!      * have to be fixed up correctly at RelationBuildTupleDesc(), because
!      * security module may need reloptions info to make its decision.
!      */
      relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
                                                 relationForm->relhasoids);
      /* which we mark as a reference-counted tupdesc */
*************** RelationBuildDesc(Oid targetRelId, Relat
*** 887,892 ****
--- 894,905 ----
      /* extract reloptions if any */
      RelationParseRelOptions(relation, pg_class_tuple);

+     /* fixup relation->rd_att->tdhassecacl and tdhasseclabel */
+     relation->rd_att->tdhasrowacl
+         = pgaceTupleDescHasRowAcl(relation, NIL);
+     relation->rd_att->tdhasseclabel
+         = pgaceTupleDescHasSecLabel(relation, NIL);
+
      /*
       * initialize the relation lock manager information
       */
*************** formrdesc(const char *relationName, Oid
*** 1474,1479 ****
--- 1487,1500 ----
      relation->rd_rel->relfilenode = RelationGetRelid(relation);

      /*
+      * Fixup relation->rd_att->tdhasrowacl and tdhasseclabel
+      */
+     RelationGetDescr(relation)->tdhasrowacl
+         = pgaceTupleDescHasRowAcl(relation, NIL);
+     RelationGetDescr(relation)->tdhasseclabel
+         = pgaceTupleDescHasSecLabel(relation, NIL);
+
+     /*
       * initialize the relation lock manager information
       */
      RelationInitLockInfo(relation);        /* see lmgr.c */
*************** BuildHardcodedDescriptor(int natts, Form
*** 2708,2713 ****
--- 2729,2741 ----

      oldcxt = MemoryContextSwitchTo(CacheMemoryContext);

+     /*
+      * NOTE: we assume the returned TupleDesc is only used for
+      * references to toast'ed data, and it is not delivered to
+      * heap_form_tuple(), so TupleDesc->tdhasrowacl and tdhasseclabel
+      * don't give us any effect.
+      * We omit to invoke pgaceTupleDescHasSecurity() here.
+      */
      result = CreateTemplateTupleDesc(natts, hasoids);
      result->tdtypeid = RECORDOID;        /* not right, but we don't care */
      result->tdtypmod = -1;
*************** load_relcache_init_file(void)
*** 3465,3470 ****
--- 3493,3506 ----
              rel->rd_options = NULL;
          }

+         /*
+          * fixup rel->rd_att->tdhassecurity
+          */
+         rel->rd_att->tdhasrowacl
+             = pgaceTupleDescHasRowAcl(rel, NIL);
+         rel->rd_att->tdhasseclabel
+             = pgaceTupleDescHasSecLabel(rel, NIL);
+
          /* mark not-null status */
          if (has_not_null)
          {
diff -Nrpc base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c
*** base/src/backend/utils/cache/syscache.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/cache/syscache.c    Sat Jan  3 15:58:18 2009
***************
*** 41,46 ****
--- 41,47 ----
  #include "catalog/pg_opfamily.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_rewrite.h"
+ #include "catalog/pg_security.h"
  #include "catalog/pg_statistic.h"
  #include "catalog/pg_ts_config.h"
  #include "catalog/pg_ts_config_map.h"
*************** static const struct cachedesc cacheinfo[
*** 584,589 ****
--- 585,614 ----
          },
          1024
      },
+     {SecurityRelationId,        /*SECURITYOID */
+         SecurityOidIndexId,
+         0,
+         1,
+         {
+             ObjectIdAttributeNumber,
+             0,
+             0,
+             0
+         },
+         128
+     },
+     {SecurityRelationId,        /* SECURITYLABEL */
+         SecuritySeclabelIndexId,
+         0,
+         1,
+         {
+             Anum_pg_security_seclabel,
+             0,
+             0,
+             0
+         },
+         128
+     },
      {StatisticRelationId,        /* STATRELATT */
          StatisticRelidAttnumIndexId,
          Anum_pg_statistic_starelid,
*************** ReleaseSysCache(HeapTuple tuple)
*** 859,864 ****
--- 884,904 ----
  }

  /*
+  * InsertSysCache
+  *      interts a tuple temporary until next CommandCounterIncrement
+  */
+ void InsertSysCache(Oid relid, HeapTuple tuple)
+ {
+     int cacheId;
+
+     for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
+     {
+         if (SysCache[cacheId]->cc_reloid == relid)
+             InsertCatCache(SysCache[cacheId], tuple);
+     }
+ }
+
+ /*
   * SearchSysCacheCopy
   *
   * A convenience routine that does SearchSysCache and (if successful)
diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c
*** base/src/backend/utils/fmgr/dfmgr.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/fmgr/dfmgr.c    Sat Jan  3 15:58:18 2009
***************
*** 23,28 ****
--- 23,29 ----
  #endif
  #include "lib/stringinfo.h"
  #include "miscadmin.h"
+ #include "security/pgace.h"
  #include "utils/dynamic_loader.h"
  #include "utils/hsearch.h"

*************** static void incompatible_module_error(co
*** 76,82 ****
                                      const Pg_magic_struct *module_magic_data);
  static void internal_unload_library(const char *libname);
  static bool file_exists(const char *name);
- static char *expand_dynamic_library_name(const char *name);
  static void check_restricted_library_name(const char *name);
  static char *substitute_libpath_macro(const char *name);
  static char *find_in_dynamic_libpath(const char *basename);
--- 77,82 ----
*************** load_external_function(char *filename, c
*** 109,114 ****
--- 109,117 ----
      /* Expand the possibly-abbreviated filename to an exact path name */
      fullname = expand_dynamic_library_name(filename);

+     /* Check whether the shared library should be loaded, or not */
+     pgaceLoadSharedModule(fullname);
+
      /* Load the shared library, unless we already did */
      lib_handle = internal_load_library(fullname);

*************** load_file(const char *filename, bool res
*** 149,154 ****
--- 152,160 ----
      /* Expand the possibly-abbreviated filename to an exact path name */
      fullname = expand_dynamic_library_name(filename);

+     /* Check whether the library should be loaded, or not */
+     pgaceLoadSharedModule(fullname);
+
      /* Unload the library if currently loaded */
      internal_unload_library(fullname);

*************** file_exists(const char *name)
*** 470,476 ****
   *
   * The result will always be freshly palloc'd.
   */
! static char *
  expand_dynamic_library_name(const char *name)
  {
      bool        have_slash;
--- 476,482 ----
   *
   * The result will always be freshly palloc'd.
   */
! char *
  expand_dynamic_library_name(const char *name)
  {
      bool        have_slash;
diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c
*** base/src/backend/utils/init/postinit.c    Sat Jan  3 13:01:35 2009
--- sepgsql/src/backend/utils/init/postinit.c    Sat Jan  3 15:58:18 2009
***************
*** 32,37 ****
--- 32,38 ----
  #include "pgstat.h"
  #include "postmaster/autovacuum.h"
  #include "postmaster/postmaster.h"
+ #include "security/pgace.h"
  #include "storage/backendid.h"
  #include "storage/bufmgr.h"
  #include "storage/fd.h"
*************** InitPostgres(const char *in_dbname, Oid
*** 645,650 ****
--- 646,654 ----
      if (!bootstrap)
          pgstat_bestart();

+     /* initialize mandatory access control facilities */
+     pgaceInitialize(bootstrap);
+
      /* close the transaction we started above */
      if (!bootstrap)
          CommitTransactionCommand();
diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c
*** base/src/backend/utils/misc/guc.c    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/utils/misc/guc.c    Thu Jan 22 14:41:23 2009
***************
*** 56,61 ****
--- 56,62 ----
  #include "postmaster/syslogger.h"
  #include "postmaster/walwriter.h"
  #include "regex/regex.h"
+ #include "security/pgace.h"
  #include "storage/bufmgr.h"
  #include "storage/fd.h"
  #include "tcop/tcopprot.h"
*************** static const struct config_enum_entry xm
*** 296,301 ****
--- 297,320 ----
      {NULL, 0, false}
  };

+ static const struct config_enum_entry pgace_feature_options[] = {
+     {"none", PGACE_FEATURE_NONE, false},
+ #ifdef HAVE_SELINUX
+     {"selinux", PGACE_FEATURE_SELINUX, false},
+ #endif
+     {NULL, 0, false}
+ };
+
+ #ifdef HAVE_SELINUX
+ static const struct config_enum_entry sepgsqloption_options[] = {
+     {"default", SEPGSQL_MODE_DEFAULT, false},
+     {"enforcing", SEPGSQL_MODE_ENFORCING, false},
+     {"permissive", SEPGSQL_MODE_PERMISSIVE, false},
+     {"disabled", SEPGSQL_MODE_DISABLED, false},
+     {NULL, 0, false}
+ };
+ #endif
+
  /*
   * Although only "on", "off", and "safe_encoding" are documented, we
   * accept all the likely variants of "on" and "off".
*************** static struct config_bool ConfigureNames
*** 1220,1225 ****
--- 1239,1255 ----
          &IgnoreSystemIndexes,
          false, NULL, NULL
      },
+ #ifdef HAVE_SELINUX
+     {
+         {"sepostgresql_row_level", PGC_POSTMASTER, UNGROUPED,
+          gettext_noop("Availability of row-level security in SE-PostgreSQL."),
+          NULL,
+          GUC_NOT_IN_SAMPLE,
+         },
+         &sepostgresql_row_level,
+         true, NULL, NULL
+     },
+ #endif

      /* End-of-list marker */
      {
*************** static struct config_enum ConfigureNames
*** 2671,2678 ****
          &xmloption,
          XMLOPTION_CONTENT, xmloption_options, NULL, NULL
      },
!
!
      /* End-of-list marker */
      {
          {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL
--- 2701,2725 ----
          &xmloption,
          XMLOPTION_CONTENT, xmloption_options, NULL, NULL
      },
!     {
!         {"pgace_feature", PGC_POSTMASTER, UNGROUPED,
!          gettext_noop("A option to choose an enhanced security feature which is "
!                       "a guest of PGACE security framework"),
!          NULL
!         },
!         &pgace_feature,
!         PGACE_FEATURE_NONE, pgace_feature_options, NULL, NULL
!     },
! #ifdef HAVE_SELINUX
!     {
!         {"sepostgresql", PGC_POSTMASTER, UNGROUPED,
!          gettext_noop("SE-PostgreSQL mode (default|permissive|enforcing|disabled)"),
!          NULL
!         },
!         &sepostgresql_mode,
!         SEPGSQL_MODE_DEFAULT, sepgsqloption_options, NULL, NULL
!     },
! #endif
      /* End-of-list marker */
      {
          {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL
*************** ResetAllOptions(void)
*** 3551,3556 ****
--- 3598,3605 ----
  {
      int            i;

+     pgaceSetDatabaseParam("all", NULL);
+
      for (i = 0; i < num_guc_variables; i++)
      {
          struct config_generic *gconf = guc_variables[i];
*************** ExecSetVariableStmt(VariableSetStmt *stm
*** 5476,5481 ****
--- 5525,5531 ----
      {
          case VAR_SET_VALUE:
          case VAR_SET_CURRENT:
+             pgaceSetDatabaseParam(stmt->name, ExtractSetVariableArgs(stmt));
              set_config_option(stmt->name,
                                ExtractSetVariableArgs(stmt),
                                (superuser() ? PGC_SUSET : PGC_USERSET),
*************** ExecSetVariableStmt(VariableSetStmt *stm
*** 5533,5538 ****
--- 5583,5589 ----
              break;
          case VAR_SET_DEFAULT:
          case VAR_RESET:
+             pgaceSetDatabaseParam(stmt->name, NULL);
              set_config_option(stmt->name,
                                NULL,
                                (superuser() ? PGC_SUSET : PGC_USERSET),
*************** EmitWarningsOnPlaceholders(const char *c
*** 5956,5961 ****
--- 6007,6015 ----
  void
  GetPGVariable(const char *name, DestReceiver *dest)
  {
+     /* Check get param permissions */
+     pgaceGetDatabaseParam(name);
+
      if (guc_name_compare(name, "all") == 0)
          ShowAllGUCConfig(dest);
      else
diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample
*** base/src/backend/utils/misc/postgresql.conf.sample    Thu Jan 22 14:34:54 2009
--- sepgsql/src/backend/utils/misc/postgresql.conf.sample    Thu Jan 22 14:41:23 2009
***************
*** 485,490 ****
--- 485,496 ----


  #------------------------------------------------------------------------------
+ # ENHANCED SECURITY OPTIONS
+ #------------------------------------------------------------------------------
+
+ #pgace_feature = 'none'
+
+ #------------------------------------------------------------------------------
  # CUSTOMIZED OPTIONS
  #------------------------------------------------------------------------------

diff -Nrpc base/src/include/access/htup.h sepgsql/src/include/access/htup.h
*** base/src/include/access/htup.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/access/htup.h    Sat Jan  3 15:58:18 2009
*************** typedef HeapTupleHeaderData *HeapTupleHe
*** 187,193 ****
   * information stored in t_infomask2:
   */
  #define HEAP_NATTS_MASK            0x07FF    /* 11 bits for number of attributes */
! /* bits 0x3800 are available */
  #define HEAP_HOT_UPDATED        0x4000    /* tuple was HOT-updated */
  #define HEAP_ONLY_TUPLE            0x8000    /* this is heap-only tuple */

--- 187,195 ----
   * information stored in t_infomask2:
   */
  #define HEAP_NATTS_MASK            0x07FF    /* 11 bits for number of attributes */
! /* bits 0x0800 are available */
! #define HEAP_HAS_ROWACL            0x1000    /* tuple has Row-level ACLs */
! #define HEAP_HAS_SECLABEL        0x2000    /* tuple has Security Label */
  #define HEAP_HOT_UPDATED        0x4000    /* tuple was HOT-updated */
  #define HEAP_ONLY_TUPLE            0x8000    /* this is heap-only tuple */

*************** do { \
*** 290,295 ****
--- 292,300 ----
      (tup)->t_choice.t_datum.datum_typmod = (typmod) \
  )

+ #define HeapTupleHeaderHasOid(tup) \
+     ((tup)->t_infomask & HEAP_HASOID)
+
  #define HeapTupleHeaderGetOid(tup) \
  ( \
      ((tup)->t_infomask & HEAP_HASOID) ? \
*************** do { \
*** 349,354 ****
--- 354,400 ----
      (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
  )

+ #define HeapTupleHeaderHasRowAcl(tup)            \
+     ((tup)->t_infomask2 & HEAP_HAS_ROWACL)
+
+ #define HeapTupleHeaderHasSecLabel(tup)    \
+     ((tup)->t_infomask2 & HEAP_HAS_SECLABEL)
+
+ #define HeapTupleHeaderGetRowAcl(tup)                                    \
+     (                                                                    \
+         HeapTupleHeaderHasRowAcl(tup)                                    \
+         ? (*((Oid *)((char *)(tup) + (tup)->t_hoff                        \
+                      - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0)    \
+                      - (HeapTupleHeaderHasSecLabel(tup) ? sizeof(Oid) : 0) \
+                      - sizeof(Oid))))                                    \
+         : InvalidOid                                                    \
+     )
+
+ #define HeapTupleHeaderGetSecLabel(tup)                                    \
+     (                                                                    \
+         HeapTupleHeaderHasSecLabel(tup)                                    \
+         ? (*(Oid *)((char *)(tup) + (tup)->t_hoff                        \
+                     - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0)    \
+                     - sizeof(Oid)))                                        \
+         : InvalidOid                                                    \
+     )
+
+ #define HeapTupleHeaderSetRowAcl(tup, rowacl)                            \
+     do {                                                                \
+         Assert(HeapTupleHeaderHasRowAcl(tup));                            \
+         *((Oid *)((char *)(tup) + (tup)->t_hoff                            \
+                   - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0)        \
+                   - (HeapTupleHeaderHasSecLabel(tup) ? sizeof(Oid) : 0) \
+                   - sizeof(Oid))) = (rowacl);                            \
+     } while (0)
+
+ #define HeapTupleHeaderSetSecLabel(tup, seclabel)                        \
+     do {                                                                \
+         Assert(HeapTupleHeaderHasSecLabel(tup));                        \
+         *((Oid *)((char *)(tup) + (tup)->t_hoff                            \
+                   - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0)        \
+                   - sizeof(Oid))) = (seclabel);                            \
+     } while(0)

  /*
   * BITMAPLEN(NATTS) -
*************** typedef HeapTupleData *HeapTuple;
*** 543,554 ****
--- 589,620 ----
  #define HeapTupleClearHeapOnly(tuple) \
          HeapTupleHeaderClearHeapOnly((tuple)->t_data)

+ #define HeapTupleHasOid(tuple) \
+         HeapTupleHeaderHasOid((tuple)->t_data)
+
  #define HeapTupleGetOid(tuple) \
          HeapTupleHeaderGetOid((tuple)->t_data)

  #define HeapTupleSetOid(tuple, oid) \
          HeapTupleHeaderSetOid((tuple)->t_data, (oid))

+ #define HeapTupleHasRowAcl(tuple) \
+         HeapTupleHeaderHasRowAcl((tuple)->t_data)
+
+ #define HeapTupleHasSecLabel(tuple) \
+         HeapTupleHeaderHasSecLabel((tuple)->t_data)
+
+ #define HeapTupleGetRowAcl(tuple) \
+         HeapTupleHeaderGetRowAcl((tuple)->t_data)
+
+ #define HeapTupleGetSecLabel(tuple) \
+         HeapTupleHeaderGetSecLabel((tuple)->t_data)
+
+ #define HeapTupleSetRowAcl(tuple, rowacl) \
+         HeapTupleHeaderSetRowAcl((tuple)->t_data, (rowacl))
+
+ #define HeapTupleSetSecLabel(tuple, seclabel) \
+         HeapTupleHeaderSetSecLabel((tuple)->t_data, (seclabel))

  /*
   * WAL record definitions for heapam.c's WAL operations
diff -Nrpc base/src/include/access/sysattr.h sepgsql/src/include/access/sysattr.h
*** base/src/include/access/sysattr.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/access/sysattr.h    Sat Jan  3 15:58:18 2009
***************
*** 25,31 ****
  #define MaxTransactionIdAttributeNumber            (-5)
  #define MaxCommandIdAttributeNumber                (-6)
  #define TableOidAttributeNumber                    (-7)
! #define FirstLowInvalidHeapAttributeNumber        (-8)


  #endif   /* SYSATTR_H */
--- 25,38 ----
  #define MaxTransactionIdAttributeNumber            (-5)
  #define MaxCommandIdAttributeNumber                (-6)
  #define TableOidAttributeNumber                    (-7)
! #define SecurityAclAttributeNumber                (-8)
! #define SecurityLabelAttributeNumber            (-9)
! #define FirstLowInvalidHeapAttributeNumber        (-10)

+ /*
+  * Attribute names for the system-defined attributes
+  */
+ #define SecurityAclAttributeName        "security_acl"
+ #define SecurityLabelAttributeName        "security_label"

  #endif   /* SYSATTR_H */
diff -Nrpc base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h
*** base/src/include/access/tupdesc.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/access/tupdesc.h    Sat Jan  3 15:58:18 2009
*************** typedef struct tupleDesc
*** 75,80 ****
--- 75,82 ----
      Oid            tdtypeid;        /* composite type ID for tuple type */
      int32        tdtypmod;        /* typmod for tuple type */
      bool        tdhasoid;        /* tuple has oid attribute in its header */
+     bool        tdhasrowacl;    /* tuple has Row-level ACLs in its header */
+     bool        tdhasseclabel;    /* tuple has security label in its header */
      int            tdrefcount;        /* reference count, or -1 if not counting */
  }    *TupleDesc;

diff -Nrpc base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h
*** base/src/include/catalog/heap.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/catalog/heap.h    Sat Jan  3 15:58:18 2009
*************** extern Oid heap_create_with_catalog(cons
*** 56,62 ****
                           int oidinhcount,
                           OnCommitAction oncommit,
                           Datum reloptions,
!                          bool allow_system_table_mods);

  extern void heap_drop_with_catalog(Oid relid);

--- 56,63 ----
                           int oidinhcount,
                           OnCommitAction oncommit,
                           Datum reloptions,
!                          bool allow_system_table_mods,
!                          List *pgaceAttrList);

  extern void heap_drop_with_catalog(Oid relid);

*************** extern List *heap_truncate_find_FKs(List
*** 68,79 ****

  extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
                          Form_pg_attribute new_attribute,
!                         CatalogIndexState indstate);

  extern void InsertPgClassTuple(Relation pg_class_desc,
                     Relation new_rel_desc,
                     Oid new_rel_oid,
!                    Datum reloptions);

  extern List *AddRelationNewConstraints(Relation rel,
                            List *newColDefaults,
--- 69,82 ----

  extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
                          Form_pg_attribute new_attribute,
!                         CatalogIndexState indstate,
!                         List *pgaceAttrList);

  extern void InsertPgClassTuple(Relation pg_class_desc,
                     Relation new_rel_desc,
                     Oid new_rel_oid,
!                    Datum reloptions,
!                    List *pgaceAttrList);

  extern List *AddRelationNewConstraints(Relation rel,
                            List *newColDefaults,
*************** extern Form_pg_attribute SystemAttribute
*** 103,108 ****
--- 106,113 ----
  extern Form_pg_attribute SystemAttributeByName(const char *attname,
                        bool relhasoids);

+ extern bool SystemAttributeIsWritable(AttrNumber attnum);
+
  extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind);

  extern void CheckAttributeType(const char *attname, Oid atttypid);
diff -Nrpc base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h
*** base/src/include/catalog/indexing.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/catalog/indexing.h    Fri Jan 23 10:55:35 2009
*************** DECLARE_UNIQUE_INDEX(pg_type_oid_index,
*** 252,257 ****
--- 252,262 ----
  DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace
oid_ops));
  #define TypeNameNspIndexId    2704

+ DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops));
+ #define SecurityOidIndexId    3401
+ DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops));
+ #define SecuritySeclabelIndexId    3402
+
  DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops));
  #define ForeignDataWrapperOidIndexId    112

diff -Nrpc base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h
*** base/src/include/catalog/pg_attribute.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/catalog/pg_attribute.h    Fri Jan 23 11:58:46 2009
*************** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
*** 130,135 ****
--- 130,141 ----
       */
      char        attalign;

+     /*
+      * attkind is a copy of the relkind field from pg_class for this
+      * attribute. See pg_class.h for more details.
+      */
+     char        attkind;
+
      /* This flag represents the "NOT NULL" constraint */
      bool        attnotnull;

*************** typedef FormData_pg_attribute *Form_pg_a
*** 176,182 ****
   * ----------------
   */

! #define Natts_pg_attribute                18
  #define Anum_pg_attribute_attrelid        1
  #define Anum_pg_attribute_attname        2
  #define Anum_pg_attribute_atttypid        3
--- 182,188 ----
   * ----------------
   */

! #define Natts_pg_attribute                19
  #define Anum_pg_attribute_attrelid        1
  #define Anum_pg_attribute_attname        2
  #define Anum_pg_attribute_atttypid        3
*************** typedef FormData_pg_attribute *Form_pg_a
*** 189,200 ****
  #define Anum_pg_attribute_attbyval        10
  #define Anum_pg_attribute_attstorage    11
  #define Anum_pg_attribute_attalign        12
! #define Anum_pg_attribute_attnotnull    13
! #define Anum_pg_attribute_atthasdef        14
! #define Anum_pg_attribute_attisdropped    15
! #define Anum_pg_attribute_attislocal    16
! #define Anum_pg_attribute_attinhcount    17
! #define Anum_pg_attribute_attacl        18


  /* ----------------
--- 195,207 ----
  #define Anum_pg_attribute_attbyval        10
  #define Anum_pg_attribute_attstorage    11
  #define Anum_pg_attribute_attalign        12
! #define Anum_pg_attribute_attkind        13
! #define Anum_pg_attribute_attnotnull    14
! #define Anum_pg_attribute_atthasdef        15
! #define Anum_pg_attribute_attisdropped    16
! #define Anum_pg_attribute_attislocal    17
! #define Anum_pg_attribute_attinhcount    18
! #define Anum_pg_attribute_attacl        19


  /* ----------------
*************** typedef FormData_pg_attribute *Form_pg_a
*** 212,457 ****
   * ----------------
   */
  #define Schema_pg_type \
! { 1247, {"typname"},       19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typnamespace"},  26, -1,    4,    2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typowner"},       26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typlen"},           21, -1,    2,    4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typbyval"},       16, -1,    1,    5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typtype"},       18, -1,    1,    6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typcategory"},   18, -1,    1,    7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typispreferred"},16, -1,    1,    8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typisdefined"},  16, -1,    1,    9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typdelim"},       18, -1,    1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typrelid"},       26, -1,    4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typelem"},       26, -1,    4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typarray"},       26, -1,    4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typinput"},       24, -1,    4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typoutput"},       24, -1,    4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typreceive"},    24, -1,    4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typsend"},       24, -1,    4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typmodin"},       24, -1,    4, 18, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typmodout"},       24, -1,    4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typanalyze"},    24, -1,    4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typalign"},       18, -1,    1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typstorage"},    18, -1,    1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typnotnull"},    16, -1,    1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typbasetype"},   26, -1,    4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typtypmod"},       23, -1,    4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typndims"},       23, -1,    4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 1247, {"typdefault"},    25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1247 typname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c t f f t 0 _null_));
! DATA(insert ( 1247 typnamespace        26 -1 4   2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typowner            26 -1 4   3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typlen            21 -1 2   4 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1247 typbyval            16 -1 1   5 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typtype            18 -1 1   6 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typcategory        18 -1 1   7 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typispreferred    16 -1 1   8 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typisdefined        16 -1 1   9 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typdelim            18 -1 1  10 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typrelid            26 -1 4  11 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typelem            26 -1 4  12 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typarray            26 -1 4  13 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typinput            24 -1 4  14 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typoutput        24 -1 4  15 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typreceive        24 -1 4  16 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typsend            24 -1 4  17 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typmodin            24 -1 4  18 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typmodout        24 -1 4  19 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typanalyze        24 -1 4  20 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typalign            18 -1 1  21 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typstorage        18 -1 1  22 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typnotnull        16 -1 1  23 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1247 typbasetype        26 -1 4  24 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typtypmod        23 -1 4  25 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typndims            23 -1 4  26 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 typdefaultbin    25 -1 -1 27 0 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1247 typdefault        25 -1 -1 28 0 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1247 ctid                27 0  6  -1 0 -1 -1 f p s t f f t 0 _null_));
! DATA(insert ( 1247 oid                26 0  4  -2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 xmin                28 0  4  -3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 cmin                29 0  4  -4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 xmax                28 0  4  -5 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 cmax                29 0  4  -6 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1247 tableoid            26 0  4  -7 0 -1 -1 t p i t f f t 0 _null_));

  /* ----------------
   *        pg_proc
   * ----------------
   */
  #define Schema_pg_proc \
! { 1255, {"proname"},            19, -1, NAMEDATALEN,  1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0
}}, \ 
! { 1255, {"pronamespace"},        26, -1, 4,    2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proowner"},            26, -1, 4,    3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"prolang"},            26, -1, 4,    4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"procost"},           700, -1, 4,    5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, {
0} }, \ 
! { 1255, {"prorows"},           700, -1, 4,    6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, {
0} }, \ 
! { 1255, {"provariadic"},        26, -1, 4,    7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proisagg"},            16, -1, 1,    8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proiswindow"},        16, -1, 1,    9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"prosecdef"},            16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proisstrict"},        16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proretset"},            16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"provolatile"},        18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1255, {"pronargs"},            21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1255, {"pronargdefaults"},    21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1255, {"prorettype"},            26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proargtypes"},        30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1255, {"proallargtypes"},   1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 1255, {"proargmodes"},      1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 1255, {"proargnames"},      1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 1255, {"proargdefaults"},        25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } },
\
! { 1255, {"prosrc"},                25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } },
\
! { 1255, {"probin"},                17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } },
\
! { 1255, {"proconfig"},          1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } },
\
! { 1255, {"proacl"},              1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1255 proname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c t f f t 0 _null_));
! DATA(insert ( 1255 pronamespace        26 -1 4   2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 proowner            26 -1 4   3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 prolang            26 -1 4   4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 procost           700 -1 4   5 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
! DATA(insert ( 1255 prorows           700 -1 4   6 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
! DATA(insert ( 1255 provariadic        26 -1 4   7 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 proisagg            16 -1 1   8 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 proiswindow        16 -1 1   9 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 prosecdef        16 -1 1  10 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 proisstrict        16 -1 1  11 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 proretset        16 -1 1  12 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 provolatile        18 -1 1  13 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1255 pronargs            21 -1 2  14 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1255 pronargdefaults    21 -1 2  15 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1255 prorettype        26 -1 4  16 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 proargtypes        30 -1 -1 17 1 -1 -1 f p i t f f t 0 _null_));
! DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 proargmodes      1002 -1 -1 19 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 proargnames      1009 -1 -1 20 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 proargdefaults    25 -1 -1 21 0 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 prosrc            25 -1 -1 22 0 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 probin            17 -1 -1 23 0 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 proconfig      1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 proacl          1034 -1 -1 25 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1255 ctid                27 0  6  -1 0 -1 -1 f p s t f f t 0 _null_));
! DATA(insert ( 1255 oid                26 0  4  -2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 xmin                28 0  4  -3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 cmin                29 0  4  -4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 xmax                28 0  4  -5 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 cmax                29 0  4  -6 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1255 tableoid            26 0  4  -7 0 -1 -1 t p i t f f t 0 _null_));

  /* ----------------
   *        pg_attribute
   * ----------------
   */
  #define Schema_pg_attribute \
! { 1249, {"attrelid"},      26, -1,    4,    1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attname"},      19, -1, NAMEDATALEN,    2, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 }
},\ 
! { 1249, {"atttypid"},      26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attstattarget"}, 23, -1,    4,    4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attlen"},          21, -1,    2,    5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attnum"},          21, -1,    2,    6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attndims"},      23, -1,    4,    7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attcacheoff"},  23, -1,    4,    8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"atttypmod"},      23, -1,    4,    9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attbyval"},      16, -1,    1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attstorage"},   18, -1,    1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attalign"},      18, -1,    1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attnotnull"},   16, -1,    1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"atthasdef"},      16, -1,    1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attisdropped"}, 16, -1,    1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attislocal"},   16, -1,    1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attinhcount"},  23, -1,    4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attacl"},     1034, -1,  -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1249 attrelid            26 -1  4   1 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attname            19 -1 NAMEDATALEN  2 0 -1 -1 f p c t f f t 0 _null_));
! DATA(insert ( 1249 atttypid            26 -1  4   3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attstattarget    23 -1  4   4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attlen            21 -1  2   5 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1249 attnum            21 -1  2   6 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1249 attndims            23 -1  4   7 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attcacheoff        23 -1  4   8 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 atttypmod        23 -1  4   9 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attbyval            16 -1  1  10 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attstorage        18 -1  1  11 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attalign            18 -1  1  12 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attnotnull        16 -1  1  13 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 atthasdef        16 -1  1  14 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attisdropped        16 -1  1  15 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attislocal        16 -1  1  16 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1249 attinhcount        23 -1  4  17 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 attacl          1034 -1 -1  18 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1249 ctid                27 0  6  -1 0 -1 -1 f p s t f f t 0 _null_));
  /* no OIDs in pg_attribute */
! DATA(insert ( 1249 xmin                28 0  4  -3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 cmin                29 0  4  -4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 xmax                28 0  4  -5 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 cmax                29 0  4  -6 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1249 tableoid            26 0  4  -7 0 -1 -1 t p i t f f t 0 _null_));

  /* ----------------
   *        pg_class
   * ----------------
   */
  #define Schema_pg_class \
! { 1259, {"relname"},       19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } },
\
! { 1259, {"relnamespace"},  26, -1,    4,    2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"reltype"},       26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relowner"},       26, -1,    4,    4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relam"},           26, -1,    4,    5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relfilenode"},   26, -1,    4,    6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"reltablespace"}, 26, -1,    4,    7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relpages"},       23, -1,    4,    8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"reltuples"},       700, -1, 4,    9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0
}}, \ 
! { 1259, {"reltoastrelid"}, 26, -1,    4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"reltoastidxid"}, 26, -1,    4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhasindex"},   16, -1,    1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relisshared"},   16, -1,    1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relkind"},       18, -1,    1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relnatts"},       21, -1,    2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relchecks"},       21, -1,    2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhasoids"},    16, -1,    1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhaspkey"},    16, -1,    1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhasrules"},   16, -1,    1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhastriggers"},16, -1,    1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhassubclass"},16, -1,    1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relfrozenxid"},  28, -1,    4, 22, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relacl"},         1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 1259, {"reloptions"},  1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1259 relname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c t f f t 0 _null_));
! DATA(insert ( 1259 relnamespace        26 -1 4   2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 reltype            26 -1 4   3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relowner            26 -1 4   4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relam            26 -1 4   5 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relfilenode        26 -1 4   6 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 reltablespace    26 -1 4   7 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relpages            23 -1 4   8 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 reltuples       700 -1 4   9 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_));
! DATA(insert ( 1259 reltoastrelid    26 -1 4  10 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 reltoastidxid    26 -1 4  11 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relhasindex        16 -1 1  12 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relisshared        16 -1 1  13 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relkind            18 -1 1  14 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relnatts            21 -1 2  15 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1259 relchecks        21 -1 2  16 0 -1 -1 t p s t f f t 0 _null_));
! DATA(insert ( 1259 relhasoids        16 -1 1  17 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relhaspkey        16 -1 1  18 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relhasrules        16 -1 1  19 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relhastriggers    16 -1 1  20 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relhassubclass    16 -1 1  21 0 -1 -1 t p c t f f t 0 _null_));
! DATA(insert ( 1259 relfrozenxid        28 -1 4  22 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 relacl          1034 -1 -1 23 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1259 reloptions      1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_));
! DATA(insert ( 1259 ctid                27 0  6  -1 0 -1 -1 f p s t f f t 0 _null_));
! DATA(insert ( 1259 oid                26 0  4  -2 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 xmin                28 0  4  -3 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 cmin                29 0  4  -4 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 xmax                28 0  4  -5 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 cmax                29 0  4  -6 0 -1 -1 t p i t f f t 0 _null_));
! DATA(insert ( 1259 tableoid            26 0  4  -7 0 -1 -1 t p i t f f t 0 _null_));

  /* ----------------
   *        pg_index
--- 219,477 ----
   * ----------------
   */
  #define Schema_pg_type \
! { 1247, {"typname"},       19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0
}}, \ 
! { 1247, {"typnamespace"},  26, -1,    4,    2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typowner"},       26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typlen"},           21, -1,    2,    4, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1247, {"typbyval"},       16, -1,    1,    5, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typtype"},       18, -1,    1,    6, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typcategory"},   18, -1,    1,    7, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typispreferred"},16, -1,    1,    8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typisdefined"},  16, -1,    1,    9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typdelim"},       18, -1,    1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typrelid"},       26, -1,    4, 11, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typelem"},       26, -1,    4, 12, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typarray"},       26, -1,    4, 13, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typinput"},       24, -1,    4, 14, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typoutput"},       24, -1,    4, 15, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typreceive"},    24, -1,    4, 16, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typsend"},       24, -1,    4, 17, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typmodin"},       24, -1,    4, 18, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typmodout"},       24, -1,    4, 19, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typanalyze"},    24, -1,    4, 20, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typalign"},       18, -1,    1, 21, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typstorage"},    18, -1,    1, 22, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typnotnull"},    16, -1,    1, 23, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typbasetype"},   26, -1,    4, 24, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typtypmod"},       23, -1,    4, 25, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1247, {"typndims"},       23, -1,    4, 26, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \
! { 1247, {"typdefault"},    25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1247 typname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c r t f f t 0 _null_));
! DATA(insert ( 1247 typnamespace        26 -1 4   2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typowner            26 -1 4   3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typlen            21 -1 2   4 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1247 typbyval            16 -1 1   5 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typtype            18 -1 1   6 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typcategory        18 -1 1   7 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typispreferred    16 -1 1   8 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typisdefined        16 -1 1   9 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typdelim            18 -1 1  10 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typrelid            26 -1 4  11 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typelem            26 -1 4  12 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typarray            26 -1 4  13 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typinput            24 -1 4  14 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typoutput        24 -1 4  15 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typreceive        24 -1 4  16 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typsend            24 -1 4  17 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typmodin            24 -1 4  18 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typmodout        24 -1 4  19 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typanalyze        24 -1 4  20 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typalign            18 -1 1  21 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typstorage        18 -1 1  22 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typnotnull        16 -1 1  23 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1247 typbasetype        26 -1 4  24 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typtypmod        23 -1 4  25 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typndims            23 -1 4  26 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 typdefaultbin    25 -1 -1 27 0 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1247 typdefault        25 -1 -1 28 0 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1247 ctid                27 0  6  -1 0 -1 -1 f p s r t f f t 0 _null_));
! DATA(insert ( 1247 oid                26 0  4  -2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 xmin                28 0  4  -3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 cmin                29 0  4  -4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 xmax                28 0  4  -5 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 cmax                29 0  4  -6 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 tableoid            26 0  4  -7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1247 security_acl      1034 0 -1  -8 1 -1 -1 f x i r t f f t 0 _null_));
! DATA(insert ( 1247 security_label    25 0 -1  -9 0 -1 -1 f x i r t f f t 0 _null_));

  /* ----------------
   *        pg_proc
   * ----------------
   */
  #define Schema_pg_proc \
! { 1255, {"proname"},            19, -1, NAMEDATALEN,  1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true,
0,{ 0 } }, \ 
! { 1255, {"pronamespace"},        26, -1, 4,    2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proowner"},            26, -1, 4,    3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"prolang"},            26, -1, 4,    4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"procost"},           700, -1, 4,    5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true,
0,{ 0 } }, \ 
! { 1255, {"prorows"},           700, -1, 4,    6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true,
0,{ 0 } }, \ 
! { 1255, {"provariadic"},        26, -1, 4,    7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proisagg"},            16, -1, 1,    8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proiswindow"},        16, -1, 1,    9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"prosecdef"},            16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proisstrict"},        16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1255, {"proretset"},            16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"provolatile"},        18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } },
\
! { 1255, {"pronargs"},            21, -1, 2, 14, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } },
\
! { 1255, {"pronargdefaults"},    21, -1, 2, 15, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } },
\
! { 1255, {"prorettype"},            26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proargtypes"},        30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proallargtypes"},   1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proargmodes"},      1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proargnames"},      1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 }
},\ 
! { 1255, {"proargdefaults"},        25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0
}}, \ 
! { 1255, {"prosrc"},                25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0
}}, \ 
! { 1255, {"probin"},                17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0
}}, \ 
! { 1255, {"proconfig"},          1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0
}}, \ 
! { 1255, {"proacl"},              1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0
}} 
!
! DATA(insert ( 1255 proname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c r t f f t 0 _null_));
! DATA(insert ( 1255 pronamespace        26 -1 4   2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 proowner            26 -1 4   3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 prolang            26 -1 4   4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 procost           700 -1 4   5 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_));
! DATA(insert ( 1255 prorows           700 -1 4   6 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_));
! DATA(insert ( 1255 provariadic        26 -1 4   7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 proisagg            16 -1 1   8 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 proiswindow        16 -1 1   9 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 prosecdef        16 -1 1  10 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 proisstrict        16 -1 1  11 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 proretset        16 -1 1  12 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 provolatile        18 -1 1  13 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1255 pronargs            21 -1 2  14 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1255 pronargdefaults    21 -1 2  15 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1255 prorettype        26 -1 4  16 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 proargtypes        30 -1 -1 17 1 -1 -1 f p i r t f f t 0 _null_));
! DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 proargmodes      1002 -1 -1 19 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 proargnames      1009 -1 -1 20 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 proargdefaults    25 -1 -1 21 0 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 prosrc            25 -1 -1 22 0 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 probin            17 -1 -1 23 0 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 proconfig      1009 -1 -1 24 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 proacl          1034 -1 -1 25 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1255 ctid                27 0  6  -1 0 -1 -1 f p s r t f f t 0 _null_));
! DATA(insert ( 1255 oid                26 0  4  -2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 xmin                28 0  4  -3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 cmin                29 0  4  -4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 xmax                28 0  4  -5 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 cmax                29 0  4  -6 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 tableoid            26 0  4  -7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1255 security_acl      1034 0 -1  -8 1 -1 -1 f x i r t f f t 0 _null_));
! DATA(insert ( 1255 security_label    25 0 -1  -9 0 -1 -1 f x i r t f f t 0 _null_));
!

  /* ----------------
   *        pg_attribute
   * ----------------
   */
  #define Schema_pg_attribute \
! { 1249, {"attrelid"},      26, -1,    4,    1, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1249, {"attname"},      19, -1, NAMEDATALEN,    2, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, {
0} }, \ 
! { 1249, {"atttypid"},      26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1249, {"attstattarget"}, 23, -1,    4,    4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1249, {"attlen"},          21, -1,    2,    5, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1249, {"attnum"},          21, -1,    2,    6, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1249, {"attndims"},      23, -1,    4,    7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1249, {"attcacheoff"},  23, -1,    4,    8, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"atttypmod"},      23, -1,    4,    9, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1249, {"attbyval"},      16, -1,    1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attstorage"},   18, -1,    1, 11, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attalign"},      18, -1,    1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attkind"},      18, -1,    1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attnotnull"},   16, -1,    1, 14, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"atthasdef"},      16, -1,    1, 15, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attisdropped"}, 16, -1,    1, 16, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attislocal"},   16, -1,    1, 17, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attinhcount"},  23, -1,    4, 18, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1249, {"attacl"},     1034, -1,  -1, 19, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1249 attrelid            26 -1  4   1 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attname            19 -1 NAMEDATALEN  2 0 -1 -1 f p c r t f f t 0 _null_));
! DATA(insert ( 1249 atttypid            26 -1  4   3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attstattarget    23 -1  4   4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attlen            21 -1  2   5 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1249 attnum            21 -1  2   6 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1249 attndims            23 -1  4   7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attcacheoff        23 -1  4   8 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 atttypmod        23 -1  4   9 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attbyval            16 -1  1  10 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attstorage        18 -1  1  11 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attalign            18 -1  1  12 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attkind            18 -1  1  13 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attnotnull        16 -1  1  14 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 atthasdef        16 -1  1  15 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attisdropped        16 -1  1  16 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attislocal        16 -1  1  17 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1249 attinhcount        23 -1  4  18 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 attacl          1034 -1 -1  19 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1249 ctid                27 0  6  -1 0 -1 -1 f p s r t f f t 0 _null_));
  /* no OIDs in pg_attribute */
! DATA(insert ( 1249 xmin                28 0  4  -3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 cmin                29 0  4  -4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 xmax                28 0  4  -5 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 cmax                29 0  4  -6 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 tableoid            26 0  4  -7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1249 security_acl      1034 0 -1  -8 1 -1 -1 f x i r t f f t 0 _null_));
! DATA(insert ( 1249 security_label    25 0 -1  -9 0 -1 -1 f x i r t f f t 0 _null_));
!

  /* ----------------
   *        pg_class
   * ----------------
   */
  #define Schema_pg_class \
! { 1259, {"relname"},       19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0
}}, \ 
! { 1259, {"relnamespace"},  26, -1,    4,    2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"reltype"},       26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"relowner"},       26, -1,    4,    4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"relam"},           26, -1,    4,    5, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 }
},\ 
! { 1259, {"relfilenode"},   26, -1,    4,    6, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"reltablespace"}, 26, -1,    4,    7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"relpages"},       23, -1,    4,    8, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"reltuples"},       700, -1, 4,    9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true,
0,{ 0 } }, \ 
! { 1259, {"reltoastrelid"}, 26, -1,    4, 10, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"reltoastidxid"}, 26, -1,    4, 11, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhasindex"},   16, -1,    1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relisshared"},   16, -1,    1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relkind"},       18, -1,    1, 14, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relnatts"},       21, -1,    2, 15, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relchecks"},       21, -1,    2, 16, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } },
\
! { 1259, {"relhasoids"},    16, -1,    1, 17, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhaspkey"},    16, -1,    1, 18, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhasrules"},   16, -1,    1, 19, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhastriggers"},16, -1,    1, 20, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relhassubclass"},16, -1,    1, 21, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relfrozenxid"},  28, -1,    4, 22, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 1259, {"relacl"},         1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } },
\
! { 1259, {"reloptions"},  1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }
!
! DATA(insert ( 1259 relname            19 -1 NAMEDATALEN    1 0 -1 -1 f p c r t f f t 0 _null_));
! DATA(insert ( 1259 relnamespace        26 -1 4   2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 reltype            26 -1 4   3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relowner            26 -1 4   4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relam            26 -1 4   5 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relfilenode        26 -1 4   6 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 reltablespace    26 -1 4   7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relpages            23 -1 4   8 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 reltuples       700 -1 4   9 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_));
! DATA(insert ( 1259 reltoastrelid    26 -1 4  10 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 reltoastidxid    26 -1 4  11 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relhasindex        16 -1 1  12 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relisshared        16 -1 1  13 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relkind            18 -1 1  14 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relnatts            21 -1 2  15 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1259 relchecks        21 -1 2  16 0 -1 -1 t p s r t f f t 0 _null_));
! DATA(insert ( 1259 relhasoids        16 -1 1  17 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relhaspkey        16 -1 1  18 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relhasrules        16 -1 1  19 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relhastriggers    16 -1 1  20 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relhassubclass    16 -1 1  21 0 -1 -1 t p c r t f f t 0 _null_));
! DATA(insert ( 1259 relfrozenxid        28 -1 4  22 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 relacl          1034 -1 -1 23 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1259 reloptions      1009 -1 -1 24 1 -1 -1 f x i r f f f t 0 _null_));
! DATA(insert ( 1259 ctid                27 0  6  -1 0 -1 -1 f p s r t f f t 0 _null_));
! DATA(insert ( 1259 oid                26 0  4  -2 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 xmin                28 0  4  -3 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 cmin                29 0  4  -4 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 xmax                28 0  4  -5 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 cmax                29 0  4  -6 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 tableoid            26 0  4  -7 0 -1 -1 t p i r t f f t 0 _null_));
! DATA(insert ( 1259 security_acl      1034 0 -1  -8 1 -1 -1 f x i r t f f t 0 _null_));
! DATA(insert ( 1259 security_label    25 0 -1  -9 0 -1 -1 f x i r t f f t 0 _null_));
!

  /* ----------------
   *        pg_index
*************** DATA(insert ( 1259 tableoid            26 0  4  -
*** 462,480 ****
   * ----------------
   */
  #define Schema_pg_index \
! { 0, {"indexrelid"},        26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 0, {"indrelid"},            26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 0, {"indnatts"},            21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisunique"},        16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisprimary"},        16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisclustered"},    16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisvalid"},        16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indcheckxmin"},        16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisready"},        16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
! { 0, {"indkey"},            22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 0, {"indclass"},            30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 0, {"indoption"},            22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
! { 0, {"indexprs"},            25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
! { 0, {"indpred"},            25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }

  #endif   /* PG_ATTRIBUTE_H */
--- 482,500 ----
   * ----------------
   */
  #define Schema_pg_index \
! { 0, {"indexrelid"},        26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indrelid"},            26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indnatts"},            21, -1, 2, 3, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisunique"},        16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisprimary"},        16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisclustered"},    16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisvalid"},        16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indcheckxmin"},        16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indisready"},        16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indkey"},            22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \
! { 0, {"indclass"},            30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 0, {"indoption"},            22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } },
\
! { 0, {"indexprs"},            25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } },
\
! { 0, {"indpred"},            25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }

  #endif   /* PG_ATTRIBUTE_H */
diff -Nrpc base/src/include/catalog/pg_class.h sepgsql/src/include/catalog/pg_class.h
*** base/src/include/catalog/pg_class.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/catalog/pg_class.h    Fri Jan 23 11:58:46 2009
*************** typedef FormData_pg_class *Form_pg_class
*** 123,129 ****
  /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
  DATA(insert OID = 1247 (  pg_type        PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute    PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 18 0 f f f f f 3 _null_ _null_ ));
  DESCR("");
  DATA(insert OID = 1255 (  pg_proc        PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
--- 123,129 ----
  /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
  DATA(insert OID = 1247 (  pg_type        PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
! DATA(insert OID = 1249 (  pg_attribute    PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 19 0 f f f f f 3 _null_ _null_ ));
  DESCR("");
  DATA(insert OID = 1255 (  pg_proc        PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ ));
  DESCR("");
diff -Nrpc base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h
*** base/src/include/catalog/pg_proc.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/catalog/pg_proc.h    Mon Jan  5 17:23:27 2009
*************** DESCR("I/O");
*** 4300,4305 ****
--- 4300,4326 ----
  DATA(insert OID = 2963 (  uuid_hash           PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_
_null_uuid_hash _null_ _null_ _null_ )); 
  DESCR("hash");

+ /* PostgreSQL Access Control Extension related functions */
+ DATA(insert OID = 3410 ( lo_get_security        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "26"    _null_ _null_ _null_
_null_lo_get_security _null_ _null_ _null_ )); 
+ DATA(insert OID = 3411 ( lo_set_security        PGNSP PGUID 12 1 0 0 f f f t f v 2 0 16 "26 25" _null_ _null_ _null_
_null_lo_set_security _null_ _null_ _null_ )); 
+
+ /* Row-level Database ACLs related function */
+ DATA(insert OID = 3430 ( rowacl_grant            PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25" _null_
_null__null_ _null_ rowacl_grant _null_ _null_ _null_)); 
+ DATA(insert OID = 3431 ( rowacl_revoke            PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25" _null_
_null__null_ _null_ rowacl_revoke _null_ _null_ _null_)); 
+ DATA(insert OID = 3432 ( rowacl_revoke_cascade        PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25"
_null__null_ _null_ _null_ rowacl_revoke_cascade _null_ _null_ _null_)); 
+
+ /* SE-PostgreSQL related function */
+ DATA(insert OID = 3450 ( sepgsql_getcon            PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_
_null_sepgsql_getcon _null_ _null_ _null_ )); 
+ DATA(insert OID = 3451 ( sepgsql_getservcon        PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_
_null_sepgsql_getservcon _null_ _null_ _null_ )); 
+ DATA(insert OID = 3452 ( sepgsql_get_user        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_
_null_sepgsql_get_user _null_ _null_ _null_ )); 
+ DATA(insert OID = 3453 ( sepgsql_set_user        PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_
_null_sepgsql_set_user _null_ _null_ _null_ )); 
+ DATA(insert OID = 3454 ( sepgsql_get_role        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_
_null_sepgsql_get_role _null_ _null_ _null_ )); 
+ DATA(insert OID = 3455 ( sepgsql_set_role        PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_
_null_sepgsql_set_role _null_ _null_ _null_ )); 
+ DATA(insert OID = 3456 ( sepgsql_get_type        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_
_null_sepgsql_get_type _null_ _null_ _null_ )); 
+ DATA(insert OID = 3457 ( sepgsql_set_type        PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_
_null_sepgsql_set_type _null_ _null_ _null_ )); 
+ DATA(insert OID = 3458 ( sepgsql_get_range        PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_
_null_sepgsql_get_range _null_ _null_ _null_ )); 
+ DATA(insert OID = 3459 ( sepgsql_set_range        PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_
_null__null_ sepgsql_set_range _null_ _null_ _null_ )); 
+
  /* enum related procs */
  DATA(insert OID = 3504 (  anyenum_in    PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_
anyenum_in_null_ _null_ _null_ )); 
  DESCR("I/O");
diff -Nrpc base/src/include/catalog/pg_proc_fn.h sepgsql/src/include/catalog/pg_proc_fn.h
*** base/src/include/catalog/pg_proc_fn.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/catalog/pg_proc_fn.h    Sat Jan  3 15:58:18 2009
*************** extern Oid ProcedureCreate(const char *p
*** 37,43 ****
                  List *parameterDefaults,
                  Datum proconfig,
                  float4 procost,
!                 float4 prorows);

  extern bool function_parse_error_transpose(const char *prosrc);

--- 37,44 ----
                  List *parameterDefaults,
                  Datum proconfig,
                  float4 procost,
!                 float4 prorows,
!                 void *pgaceItem);

  extern bool function_parse_error_transpose(const char *prosrc);

diff -Nrpc base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h
*** base/src/include/catalog/pg_security.h    Thu Jan  1 09:00:00 1970
--- sepgsql/src/include/catalog/pg_security.h    Fri Sep 21 01:08:03 2007
***************
*** 0 ****
--- 1,31 ----
+ /*
+  * src/include/catalog/pg_security.h
+  *    Definition of the security label relation (pg_security)
+  *
+  * Copyright (c) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+  */
+ #ifndef PG_SECURITY_H
+ #define PG_SECURITY_H
+
+ #define SecurityRelationId        3400
+
+ CATALOG(pg_security,3400) BKI_SHARED_RELATION
+ {
+     text        seclabel;        /* text representation of security label */
+ } FormData_pg_security;
+
+ /* ----------------
+  *     Form_pg_security corresponds to a pointer to a tuple with
+  *     the format of pg_security relation.
+  * ----------------
+  */
+ typedef FormData_pg_security *Form_pg_security;
+
+ /* ----------------
+  *        compiler constants for pg_selinux
+  * ----------------
+  */
+ #define Natts_pg_security                1
+ #define Anum_pg_security_seclabel        1
+
+ #endif   /* PG_SELINUX_H */
diff -Nrpc base/src/include/catalog/pg_type.h sepgsql/src/include/catalog/pg_type.h
*** base/src/include/catalog/pg_type.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/catalog/pg_type.h    Sat Jan  3 15:58:18 2009
*************** DATA(insert OID = 1033 (  aclitem     PGNSP
*** 459,464 ****
--- 459,465 ----
  DESCR("access control list");
  #define ACLITEMOID        1033
  DATA(insert OID = 1034 (  _aclitem     PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv
array_send- - - i x f 0 -1 0 _null_ _null_ )); 
+ #define ACLITEMARRAYOID    1034
  DATA(insert OID = 1040 (  _macaddr     PGNSP PGUID -1 f b A f t \054 0  829 0 array_in array_out array_recv
array_send- - - i x f 0 -1 0 _null_ _null_ )); 
  DATA(insert OID = 1041 (  _inet         PGNSP PGUID -1 f b A f t \054 0  869 0 array_in array_out array_recv
array_send- - - i x f 0 -1 0 _null_ _null_ )); 
  DATA(insert OID = 651  (  _cidr         PGNSP PGUID -1 f b A f t \054 0  650 0 array_in array_out array_recv
array_send- - - i x f 0 -1 0 _null_ _null_ )); 
diff -Nrpc base/src/include/executor/executor.h sepgsql/src/include/executor/executor.h
*** base/src/include/executor/executor.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/executor/executor.h    Sat Jan  3 15:58:18 2009
*************** extern TupleHashEntry FindTupleHashEntry
*** 130,137 ****
  /*
   * prototypes from functions in execJunk.c
   */
! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid,
!                    TupleTableSlot *slot);
  extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
                               TupleDesc cleanTupType,
                               TupleTableSlot *slot);
--- 130,138 ----
  /*
   * prototypes from functions in execJunk.c
   */
! extern JunkFilter *ExecInitJunkFilter(List *targetList,
!                                       bool hasoid, bool hassecacl, bool hasseclabel,
!                                       TupleTableSlot *slot);
  extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
                               TupleDesc cleanTupType,
                               TupleTableSlot *slot);
*************** extern void InitResultRelInfo(ResultRelI
*** 163,168 ****
--- 164,171 ----
                    bool doInstrument);
  extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
  extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
+ extern bool ExecContextForcesRowAcl(PlanState *planstate, bool *hasrowacl);
+ extern bool ExecContextForcesSecLabel(PlanState *planstate, bool *hasseclabel);
  extern void ExecConstraints(ResultRelInfo *resultRelInfo,
                  TupleTableSlot *slot, EState *estate);
  extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
*************** extern void ExecInitScanTupleSlot(EState
*** 216,223 ****
  extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
  extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
                        TupleDesc tupType);
! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
  extern TupleDesc ExecTypeFromExprList(List *exprList);
  extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);

--- 219,226 ----
  extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
  extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
                        TupleDesc tupType);
! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecacl, bool hasseclabel);
! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecacl, bool hasseclabel);
  extern TupleDesc ExecTypeFromExprList(List *exprList);
  extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);

diff -Nrpc base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h
*** base/src/include/executor/tuptable.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/executor/tuptable.h    Sat Jan  3 15:58:18 2009
*************** typedef struct TupleTableSlot
*** 118,123 ****
--- 118,127 ----
      MinimalTuple tts_mintuple;    /* set if it's a minimal tuple, else NULL */
      HeapTupleData tts_minhdr;    /* workspace if it's a minimal tuple */
      long        tts_off;        /* saved state for slot_deform_tuple */
+
+     /* temporary storage variables for writable system column */
+     Datum        tts_rowacl;        /* for row level acls */
+     Datum        tts_seclabel;    /* for security label */
  } TupleTableSlot;

  /*
diff -Nrpc base/src/include/fmgr.h sepgsql/src/include/fmgr.h
*** base/src/include/fmgr.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/fmgr.h    Fri Jan 16 17:05:27 2009
*************** typedef struct FmgrInfo
*** 53,58 ****
--- 53,60 ----
      void       *fn_extra;        /* extra space for use by handler */
      MemoryContext fn_mcxt;        /* memory context to store fn_extra in */
      fmNodePtr    fn_expr;        /* expression parse tree for call, or NULL */
+
+     void       *fn_pgaceItem;    /* PGACE opaque field */
  } FmgrInfo;

  /*
*************** extern bool get_call_expr_arg_stable(fmN
*** 524,529 ****
--- 526,532 ----
   */
  extern char *Dynamic_library_path;

+ extern char *expand_dynamic_library_name(const char *name);
  extern PGFunction load_external_function(char *filename, char *funcname,
                         bool signalNotFound, void **filehandle);
  extern PGFunction lookup_external_function(void *filehandle, char *funcname);
diff -Nrpc base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h
*** base/src/include/libpq/be-fsstubs.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/libpq/be-fsstubs.h    Sat Jan  3 15:58:18 2009
*************** extern Datum lo_tell(PG_FUNCTION_ARGS);
*** 37,42 ****
--- 37,45 ----
  extern Datum lo_unlink(PG_FUNCTION_ARGS);
  extern Datum lo_truncate(PG_FUNCTION_ARGS);

+ extern Datum lo_get_security(PG_FUNCTION_ARGS);
+ extern Datum lo_set_security(PG_FUNCTION_ARGS);
+
  /*
   * These are not fmgr-callable, but are available to C code.
   * Probably these should have had the underscore-free names,
diff -Nrpc base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h
*** base/src/include/nodes/nodes.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/nodes/nodes.h    Fri Jan 23 10:55:35 2009
*************** typedef enum NodeTag
*** 390,396 ****
      T_TriggerData = 950,        /* in commands/trigger.h */
      T_ReturnSetInfo,            /* in nodes/execnodes.h */
      T_WindowObjectData,            /* private in nodeWindowAgg.c */
!     T_TIDBitmap                    /* in nodes/tidbitmap.h */
  } NodeTag;

  /*
--- 390,397 ----
      T_TriggerData = 950,        /* in commands/trigger.h */
      T_ReturnSetInfo,            /* in nodes/execnodes.h */
      T_WindowObjectData,            /* private in nodeWindowAgg.c */
!     T_TIDBitmap,                    /* in nodes/tidbitmap.h */
!     T_SelinuxEvalItem,            /* in nodes/security.h */
  } NodeTag;

  /*
diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h
*** base/src/include/nodes/parsenodes.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/nodes/parsenodes.h    Fri Jan 23 10:55:35 2009
*************** typedef struct Query
*** 152,157 ****
--- 152,158 ----

      Node       *setOperations;    /* set-operation tree if this is top level of
                                   * a UNION/INTERSECT/EXCEPT query */
+     Node       *pgaceItem;        /* PGACE: an opaque item for security purpose */
  } Query;


*************** typedef struct ColumnDef
*** 471,476 ****
--- 472,478 ----
      Node       *raw_default;    /* default value (untransformed parse tree) */
      char       *cooked_default; /* nodeToString representation */
      List       *constraints;    /* other constraints on column */
+     Node       *pgaceItem;        /* PGACE: security attribute */
  } ColumnDef;

  /*
*************** typedef struct RangeTblEntry
*** 717,722 ****
--- 719,735 ----
      Oid            checkAsUser;    /* if valid, check access as this role */
      Bitmapset  *selectedCols;    /* columns needing SELECT permission */
      Bitmapset  *modifiedCols;    /* columns needing INSERT/UPDATE permission */
+
+     /*
+      * PGACE allows its guest to use pgaceTuplePerms to mark required
+      * permission set for tuple-level access controls. This field is
+      * copied to Scan node (like SeqScan), and it can be refered within
+      * pgaceExecScan() hook.
+      * Please note that the wired rowacl reserves the least 8-bits
+      * (0x000000ff), so any other optional feature should not use
+      * these bits.
+      */
+     uint32        pgaceTuplePerms;
  } RangeTblEntry;

  /*
*************** typedef enum AlterTableType
*** 1133,1139 ****
      AT_EnableReplicaRule,        /* ENABLE REPLICA RULE name */
      AT_DisableRule,                /* DISABLE RULE name */
      AT_AddInherit,                /* INHERIT parent */
!     AT_DropInherit                /* NO INHERIT parent */
  } AlterTableType;

  typedef struct AlterTableCmd    /* one subcommand of an ALTER TABLE */
--- 1146,1153 ----
      AT_EnableReplicaRule,        /* ENABLE REPLICA RULE name */
      AT_DisableRule,                /* DISABLE RULE name */
      AT_AddInherit,                /* INHERIT parent */
!     AT_DropInherit,                /* NO INHERIT parent */
!     AT_SetSecurityLabel,        /* PGACE: set security label */
  } AlterTableType;

  typedef struct AlterTableCmd    /* one subcommand of an ALTER TABLE */
*************** typedef struct CreateStmt
*** 1336,1341 ****
--- 1350,1356 ----
      List       *options;        /* options from WITH clause */
      OnCommitAction oncommit;    /* what do we do at COMMIT? */
      char       *tablespacename; /* table space to use, or NULL */
+     Node       *pgaceItem;        /* PGACE: security attribute */
  } CreateStmt;

  /* ----------
diff -Nrpc base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h
*** base/src/include/nodes/plannodes.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/nodes/plannodes.h    Sat Jan  3 15:58:18 2009
*************** typedef struct PlannedStmt
*** 76,81 ****
--- 76,83 ----
      List       *invalItems;        /* other dependencies, as PlanInvalItems */

      int            nParamExec;        /* number of PARAM_EXEC Params used */
+
+     Node       *pgaceItem;        /* PGACE: an opaque item for security purpose */
  } PlannedStmt;

  /* macro for fetching the Plan associated with a SubPlan node */
*************** typedef struct Scan
*** 239,244 ****
--- 241,254 ----
  {
      Plan        plan;
      Index        scanrelid;        /* relid is index into the range table */
+
+     /*
+      * pgaceTuplePerms is used to show permission set to be applied to
+      * tuple-leve access controls by security module.
+      * It is copied from related RangeTblEntry's one when Scan structure
+      * is created.
+      */
+     uint32        pgaceTuplePerms;
  } Scan;

  /* ----------------
diff -Nrpc base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h
*** base/src/include/nodes/relation.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/nodes/relation.h    Sat Jan  3 15:58:18 2009
*************** typedef struct RelOptInfo
*** 383,388 ****
--- 383,390 ----
       * list just to avoid recomputing the best inner indexscan repeatedly for
       * similar outer relations.  See comments for InnerIndexscanInfo.
       */
+
+     uint32        pgaceTuplePerms;        /* copied from RangeTblEntry */
  } RelOptInfo;

  /*
diff -Nrpc base/src/include/nodes/security.h sepgsql/src/include/nodes/security.h
*** base/src/include/nodes/security.h    Thu Jan  1 09:00:00 1970
--- sepgsql/src/include/nodes/security.h    Wed Jan 21 16:59:48 2009
***************
*** 0 ****
--- 1,40 ----
+ /*-------------------------------------------------------------------------
+  *
+  * src/include/nodes/security.h
+  *    definitions for security extention related nodes
+  *
+  * Portions Copyright (c) 2007-2008, PostgreSQL Global Development Group
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef NODES_SECURITY_H
+ #define NODES_SECURITY_H
+
+ #include "access/attnum.h"
+ #include "nodes/nodes.h"
+
+ /*
+  * SelinuxEvalItem
+  *
+  * Required permissions on tables/columns used by SE-PostgreSQL.
+  * It is constracted just after query rewriter phase, then its
+  * list is checked based on the security policy of operating
+  * system.
+  *
+  * NOTE: attperms array can contains system attributes and
+  * whole-row-reference, so it is indexed as
+  *   attperms[(attnum) + FirstLowInvalidHeapAttributeNumber - 1]
+  */
+ typedef struct SelinuxEvalItem
+ {
+     NodeTag        type;
+
+     Oid            relid;        /* relation id */
+     bool        inh;        /* flags to inheritable/only */
+
+     uint32        relperms;    /* required permissions on table */
+     uint32        nattrs;        /* length of attperms */
+     uint32       *attperms;    /* required permissions on columns */
+ } SelinuxEvalItem;
+
+ #endif    /* NODES_SECURITY_H */
diff -Nrpc base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in
*** base/src/include/pg_config.h.in    Tue Jan 13 09:22:28 2009
--- sepgsql/src/include/pg_config.h.in    Tue Jan 13 09:39:35 2009
***************
*** 388,393 ****
--- 388,396 ----
  /* Define to 1 if you have the <security/pam_appl.h> header file. */
  #undef HAVE_SECURITY_PAM_APPL_H

+ /* Define to 1 if you enable SELinux support */
+ #undef HAVE_SELINUX
+
  /* Define to 1 if you have the `setproctitle' function. */
  #undef HAVE_SETPROCTITLE

diff -Nrpc base/src/include/security/pgace.h sepgsql/src/include/security/pgace.h
*** base/src/include/security/pgace.h    Thu Jan  1 09:00:00 1970
--- sepgsql/src/include/security/pgace.h    Wed Jan 21 17:22:37 2009
***************
*** 0 ****
--- 1,181 ----
+ /*
+  * include/security/pgace.h
+  *    headers for PostgreSQL Access Control Extension (PGACE)
+  *
+  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  */
+ #ifndef PGACE_H
+ #define PGACE_H
+
+ #include "access/htup.h"
+ #include "commands/trigger.h"
+ #include "executor/execdesc.h"
+ #include "fmgr.h"
+ #include "nodes/params.h"
+ #include "nodes/parsenodes.h"
+ #include "nodes/plannodes.h"
+ #include "storage/large_object.h"
+ #include "utils/rel.h"
+
+ #include "security/rowacl.h"
+ #ifdef HAVE_SELINUX
+ #include "security/sepgsql.h"
+ #endif
+
+ /*
+  * pgace_feature : GUC parameter to choose an enhanced security feature
+  */
+ typedef enum
+ {
+     PGACE_FEATURE_NONE,
+ #ifdef HAVE_SELINUX
+     PGACE_FEATURE_SELINUX,
+ #endif
+ } PgaceFeatureOpts;
+
+ extern int pgace_feature;
+
+ /*
+  * Initialization hooks
+  */
+ extern Size pgaceShmemSize(void);
+ extern void pgaceInitialize(bool is_bootstrap);
+ extern pid_t pgaceStartupWorkerProcess(void);
+
+ /*
+  * SQL proxy hooks
+  */
+ extern List *pgacePostQueryRewrite(List *queryList);
+ extern void pgaceExecutorStart(QueryDesc *queryDesc, int eflags);
+ extern void pgaceProcessUtility(Node *parsetree, ParamListInfo params,
+                                 bool isTopLevel);
+ /*
+  * HeapTuple input/output hooks
+  */
+ extern bool pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot);
+ extern bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+                                  bool is_internal, bool with_returning);
+ extern bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+                                  bool is_internal, bool with_returning);
+ extern bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid,
+                                  bool is_internal, bool with_returning);
+ /*
+  * Enhanced SQL statements
+  */
+ extern bool pgaceIsGramSecurityItem(DefElem *defel);
+ extern void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel);
+ extern void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel);
+
+ /*
+  * Function related hooks
+  */
+ extern void pgaceCallFunction(FmgrInfo *finfo);
+ extern void pgaceCallAggFunction(HeapTuple aggTuple);
+ extern bool pgaceCallTriggerFunction(TriggerData *tgdata);
+ extern void pgaceBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid,
+                                      Datum *rowacl_private, Datum *pgace_private);
+ extern void pgaceEndPerformCheckFK(Relation rel,
+                                    Datum rowacl_private, Datum pgace_private);
+ extern bool pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple);
+
+ /*
+  * Misc hooks
+  */
+ extern void pgaceSetDatabaseParam(const char *name, char *argstring);
+ extern void pgaceGetDatabaseParam(const char *name);
+ extern void pgaceExecTruncate(List *trunc_rels);
+ extern void pgaceLockTable(Oid relid);
+
+ /*
+  * COPY TO/FROM statement hooks
+  */
+ extern void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom);
+ extern void pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom);
+ extern bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple);
+
+ /*
+  * Loadable shared library module hooks
+  */
+ extern void pgaceLoadSharedModule(const char *filename);
+
+ /*
+  * Binary Large Object hooks
+  */
+ extern void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple);
+ extern void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem);
+ extern void pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length);
+ extern void pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length);
+ extern void pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset);
+ extern void pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename);
+ extern void pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename);
+ extern void pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple);
+ extern void pgaceLargeObjectSetSecurity(Relation rel,
+                                         HeapTuple newtup, HeapTuple oldtup);
+ /*
+  * Security Label hooks
+  */
+ extern bool pgaceTupleDescHasRowAcl(Relation rel, List *relopts);
+ extern bool pgaceTupleDescHasSecLabel(Relation rel, List *relopts);
+ extern char *pgaceTranslateSecurityLabelIn(char *seclabel);
+ extern char *pgaceTranslateSecurityLabelOut(char *seclabel);
+ extern bool pgaceCheckValidSecurityLabel(char *seclabel);
+ extern char *pgaceUnlabeledSecurityLabel(void);
+ extern char *pgaceSecurityLabelOfLabel(void);
+
+ /*
+  * PGACE common facilities (not hooks)
+  */
+
+ /* security label management */
+ extern void pgacePostBootstrapingMode(void);
+
+ extern Oid pgaceLookupSecurityId(char *label);
+
+ extern char *pgaceLookupSecurityLabel(Oid sid);
+
+ extern Oid pgaceSecurityLabelToSid(char *label);
+
+ extern char *pgaceSidToSecurityLabel(Oid sid);
+
+ /* Enhanced SQL statements related */
+ extern List *pgaceRelationAttrList(CreateStmt *stmt);
+
+ extern void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple,
+                                       List *pgaceAttrList);
+ extern void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple,
+                                        List *pgaceAttrList);
+ extern void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd);
+
+ /* Export security system columns */
+ extern Datum pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple);
+
+ /*
+  * SQL functions
+  */
+
+ /* SE-PostgreSQL */
+ extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_getservcon(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_get_user(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_get_role(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_get_type(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_get_range(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_set_user(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_set_role(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_set_type(PG_FUNCTION_ARGS);
+ extern Datum sepgsql_set_range(PG_FUNCTION_ARGS);
+
+ /* Row-level ACLs */
+ extern Datum rowacl_grant(PG_FUNCTION_ARGS);
+ extern Datum rowacl_revoke(PG_FUNCTION_ARGS);
+ extern Datum rowacl_revoke_cascade(PG_FUNCTION_ARGS);
+
+ #endif // PGACE_H
diff -Nrpc base/src/include/security/rowacl.h sepgsql/src/include/security/rowacl.h
*** base/src/include/security/rowacl.h    Thu Jan  1 09:00:00 1970
--- sepgsql/src/include/security/rowacl.h    Fri Jan 16 10:31:05 2009
***************
*** 0 ****
--- 1,41 ----
+ /*
+  * src/include/security/rowacl.h
+  *   headers for Row-level database ACL support
+  */
+ #ifndef ROWACL_H
+ #define ROWACL_H
+
+ #include "utils/acl.h"
+
+ extern void rowaclInitialize(bool is_bootstrap);
+
+ extern List *rowaclPostQueryRewrite(List *queryList);
+
+ extern Datum rowaclBeginPerformCheckFK(Relation rel, bool is_primary, Oid userid_saved);
+
+ extern void rowaclEndPerformCheckFK(Relation rel, Datum rowacl_private);
+
+ extern bool rowaclExecScan(Scan *scan, Relation rel, TupleTableSlot *slot);
+
+ extern bool rowaclCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple);
+
+ extern bool rowaclHeapTupleInsert(Relation rel, HeapTuple tuple,
+                                   bool is_internal, bool with_returning);
+
+ extern bool rowaclHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+                                   bool is_internal, bool with_returning);
+
+ extern bool rowaclHeapTupleDelete(Relation rel, ItemPointer otid,
+                                   bool is_internal, bool with_returning);
+
+ extern bool rowaclTupleDescHasRowAcl(Relation rel, List *relopts);
+
+ extern Acl *rowaclSidToSecurityAcl(Oid sid, Oid ownerId);
+
+ extern Oid rowaclSecurityAclToSid(Acl *acl);
+
+ extern Datum rowaclHeapGetSecurityAclSysattr(HeapTuple tuple);
+
+ extern void rawaclValidateDefaultRowAclRelopt(const char *value);
+
+ #endif  /* ROWACL_H */
diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h
*** base/src/include/security/sepgsql.h    Thu Jan  1 09:00:00 1970
--- sepgsql/src/include/security/sepgsql.h    Wed Jan 21 17:22:37 2009
***************
*** 0 ****
--- 1,241 ----
+ /*
+  * src/include/security/sepgsql.h
+  *    headers for Security-Enhanced PostgreSQL (SE-PostgreSQL)
+  *
+  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  */
+ #ifndef SEPGSQL_H
+ #define SEPGSQL_H
+
+ #include <selinux/selinux.h>
+ #include <selinux/flask.h>
+ #include <selinux/av_permissions.h>
+
+ /*
+  * SE-PostgreSQL modes
+  */
+ typedef enum
+ {
+     SEPGSQL_MODE_DEFAULT,
+     SEPGSQL_MODE_ENFORCING,
+     SEPGSQL_MODE_PERMISSIVE,
+     SEPGSQL_MODE_DISABLED,
+ } SepgsqlModeType;
+
+ extern int sepostgresql_mode;
+ extern bool sepostgresql_row_level;
+
+ /*
+  * Permission bits delivered to sepgsqlCheckTuplePerms().
+  * Please note that 0x000000ff of RangeTblEntry->pgaceTuplePerms
+  * are reserved by rowacl. These bits are also stored within
+  * pgaceTuplePerms, we have to avoid to use the lower bits.
+  */
+ #define SEPGSQL_PERMS_USE                (1UL <<  8)
+ #define SEPGSQL_PERMS_SELECT            (1UL <<  9)
+ #define SEPGSQL_PERMS_UPDATE            (1UL << 10)
+ #define SEPGSQL_PERMS_INSERT            (1UL << 11)
+ #define SEPGSQL_PERMS_DELETE            (1UL << 12)
+ #define SEPGSQL_PERMS_RELABELFROM        (1UL << 13)
+ #define SEPGSQL_PERMS_RELABELTO            (1UL << 14)
+ #define SEPGSQL_PERMS_READ                (1UL << 15)
+ #define SEPGSQL_PERMS_MASK                (0xffffff00)
+
+ /*
+  * The implementation of PGACE/SE-PostgreSQL hooks
+  */
+
+ /* Initialize / Finalize related hooks */
+ extern Size sepgsqlShmemSize(void);
+
+ extern void sepgsqlInitialize(bool is_bootstrap);
+
+ extern pid_t sepgsqlStartupWorkerProcess(void);
+
+ /* SQL proxy hooks */
+ extern List *sepgsqlPostQueryRewrite(List *queryList);
+
+ extern void sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags);
+
+ extern void sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel);
+
+ /* ExecScan hooks */
+ extern bool sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot);
+
+ /* HeapTuple modification hooks */
+ extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple,
+                                    bool is_internal, bool with_returning);
+ extern bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid,
+                                    HeapTuple newtup, bool is_internal,
+                                    bool with_returning);
+ extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid,
+                                    bool is_internal, bool with_returning);
+
+ /*    Extended SQL statement hooks */
+ extern bool sepgsqlIsGramSecurityItem(DefElem *defel);
+
+ extern void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple,
+                                       DefElem *defel);
+ extern void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple,
+                                        DefElem *defel);
+ extern void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple,
+                                      DefElem *defel);
+ extern void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple,
+                                       DefElem *defel);
+ extern void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple,
+                                       DefElem *defel);
+ extern void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple,
+                                      DefElem *defel);
+ extern void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple,
+                                       DefElem *defel);
+ extern void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple,
+                                      DefElem *defel);
+
+ /* DATABASE related hooks */
+ extern void sepgsqlSetDatabaseParam(const char *name, char *argstring);
+
+ extern void sepgsqlGetDatabaseParam(const char *name);
+
+ /* FUNCTION related hooks */
+ extern void sepgsqlCallFunction(FmgrInfo *finfo);
+
+ extern void sepgsqlCallAggFunction(HeapTuple aggTuple);
+
+ extern bool sepgsqlCallTriggerFunction(TriggerData *tgdata);
+
+ extern Datum sepgsqlBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid);
+
+ extern void sepgsqlEndPerformCheckFK(Relation rel, Datum save_pgace);
+
+ extern bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple);
+
+ /* TABLE related hooks */
+ extern void sepgsqlLockTable(Oid relid);
+
+ extern void sepgsqlExecTruncate(List *trunc_rels);
+
+ extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd);
+
+ /* COPY TO/COPY FROM statement hooks */
+ extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from);
+
+ extern void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom);
+
+ extern bool sepgsqlCopyToTuple(Relation rel, List *attnumlist, HeapTuple tuple);
+
+ /* Loadable shared library module hooks */
+ extern void sepgsqlLoadSharedModule(const char *filename);
+
+ /* Binary Large Object (BLOB) hooks */
+ extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple);
+
+ extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem);
+
+ extern void sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int length);
+
+ extern void sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int length);
+
+ extern void sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int offset);
+
+ extern void sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename);
+
+ extern void sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename);
+
+ extern void sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple);
+
+ extern void sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup);
+
+ /* Security Label hooks */
+ extern bool  sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts);
+
+ extern char *sepgsqlTranslateSecurityLabelIn(const char *context);
+
+ extern char *sepgsqlTranslateSecurityLabelOut(const char *context);
+
+ extern bool  sepgsqlCheckValidSecurityLabel(char *context);
+
+ extern char *sepgsqlUnlabeledSecurityLabel(void);
+
+ extern char *sepgsqlSecurityLabelOfLabel(void);
+
+ /*
+  * SE-PostgreSQL core functions
+  *     src/backend/security/sepgsql/core.c
+  */
+ extern bool sepgsqlIsEnabled(void);
+
+ extern const security_context_t sepgsqlGetServerContext(void);
+
+ extern const security_context_t sepgsqlGetClientContext(void);
+
+ extern const security_context_t sepgsqlGetDatabaseContext(void);
+
+ extern const security_context_t sepgsqlGetUnlabeledContext(void);
+
+ extern const security_context_t sepgsqlSwitchClientContext(security_context_t newcon);
+
+ extern Oid sepgsqlGetDatabaseSecurityId(void);
+
+ /*
+  * SE-PostgreSQL userspace avc functions
+  *   src/backend/security/sepgsql/avc.c
+  */
+ extern void sepgsqlAvcInit(void);
+
+ extern void sepgsqlAvcSwitchClientContext(security_context_t context);
+
+ extern void sepgsqlClientHasPermission(Oid target_security_id,
+                                        security_class_t tclass,
+                                        access_vector_t perms,
+                                        const char *objname);
+
+ extern bool sepgsqlClientHasPermissionNoAbort(Oid target_security_id,
+                                               security_class_t tclass,
+                                               access_vector_t perms,
+                                               const char *objname);
+
+ extern Oid sepgsqlClientCreateSid(Oid target_security_id,
+                                   security_class_t tclass);
+
+ extern security_context_t
+ sepgsqlClientCreateContext(Oid target_security_id,
+                            security_class_t tclass);
+
+ extern bool sepgsqlComputePermission(const security_context_t scontext,
+                                      const security_context_t tcontext,
+                                      security_class_t tclass,
+                                      access_vector_t perms,
+                                      const char *objname);
+
+ extern security_context_t
+ sepgsqlComputeCreateContext(const security_context_t scontext,
+                             const security_context_t tcontext,
+                             security_class_t tclass);
+
+ /*
+  * SE-PostgreSQL permission evaluation related
+  *     src/backend/security/sepgsql/permission.c
+  */
+ extern const char *sepgsqlTupleName(Oid relid, HeapTuple tuple);
+
+ extern security_class_t sepgsqlFileObjectClass(int fdesc, const char *filename);
+
+ extern security_class_t sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple);
+
+ extern void sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple);
+
+ extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup,
+                                    uint32 perms, bool abort);
+
+ extern void sepgsqlCheckModuleInstallPerms(const char *filename);
+
+ /*
+  * workaround for older libselinux
+  */
+ #ifndef DB_PROCEDURE__INSTALL
+ #define DB_PROCEDURE__INSTALL        0x00000100UL
+ #endif
+
+ #endif   /* SEPGSQL_H */
diff -Nrpc base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h
*** base/src/include/storage/fd.h    Tue Jan 13 09:22:28 2009
--- sepgsql/src/include/storage/fd.h    Tue Jan 13 09:39:35 2009
*************** extern int    FileWrite(File file, char *bu
*** 68,73 ****
--- 68,74 ----
  extern int    FileSync(File file);
  extern off_t FileSeek(File file, off_t offset, int whence);
  extern int    FileTruncate(File file, off_t offset);
+ extern int  FileRawDescriptor(File file);

  /* Operations that allow use of regular stdio --- USE WITH CAUTION */
  extern FILE *AllocateFile(const char *name, const char *mode);
diff -Nrpc base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h
*** base/src/include/storage/lwlock.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/storage/lwlock.h    Sat Jan  3 15:58:18 2009
*************** typedef enum LWLockId
*** 68,73 ****
--- 68,74 ----
      AutovacuumLock,
      AutovacuumScheduleLock,
      SyncScanLock,
+     SepgsqlAvcLock,
      /* Individual lock IDs end here */
      FirstBufMappingLock,
      FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
diff -Nrpc base/src/include/utils/acl.h sepgsql/src/include/utils/acl.h
*** base/src/include/utils/acl.h    Fri Jan 23 10:23:37 2009
--- sepgsql/src/include/utils/acl.h    Fri Jan 23 10:55:35 2009
*************** typedef struct
*** 222,227 ****
--- 222,228 ----
  /*
   * routines used internally
   */
+ extern Acl *allocacl(int n);
  extern Acl *acldefault(GrantObjectType objtype, Oid ownerId);
  extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
            int modechg, Oid ownerId, DropBehavior behavior);
*************** extern bool has_privs_of_role(Oid member
*** 237,242 ****
--- 238,244 ----
  extern bool is_member_of_role(Oid member, Oid role);
  extern bool is_member_of_role_nosuper(Oid member, Oid role);
  extern bool is_admin_of_role(Oid member, Oid role);
+ extern void check_acl(const Acl *acl);
  extern void check_is_member_of_role(Oid member, Oid role);

  extern void select_best_grantor(Oid roleId, AclMode privileges,
*************** extern AclResult pg_tablespace_aclcheck(
*** 295,300 ****
--- 297,307 ----
  extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
  extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);

+ extern Acl *merge_acl_with_grant(Acl *old_acl, bool is_grant,
+                                  bool grant_option, DropBehavior behavior,
+                                  List *grantees, AclMode privileges,
+                                  Oid grantorId, Oid ownerId);
+
  extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
                 const char *objectname);

diff -Nrpc base/src/include/utils/catcache.h sepgsql/src/include/utils/catcache.h
*** base/src/include/utils/catcache.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/utils/catcache.h    Sat Jan  3 15:58:18 2009
*************** extern HeapTuple SearchCatCache(CatCache
*** 172,177 ****
--- 172,178 ----
                 Datum v1, Datum v2,
                 Datum v3, Datum v4);
  extern void ReleaseCatCache(HeapTuple tuple);
+ extern void InsertCatCache(CatCache *cache, HeapTuple tuple);

  extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys,
                     Datum v1, Datum v2,
diff -Nrpc base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h
*** base/src/include/utils/errcodes.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/utils/errcodes.h    Sat Jan  3 15:58:18 2009
***************
*** 346,351 ****
--- 346,358 ----
  #define ERRCODE_NO_DATA_FOUND                MAKE_SQLSTATE('P','0', '0','0','2')
  #define ERRCODE_TOO_MANY_ROWS                MAKE_SQLSTATE('P','0', '0','0','3')

+ /* Class SE - Security Error (PGACE/SE-PostgreSQL error class) */
+ #define ERRCODE_PGACE_ERROR                    MAKE_SQLSTATE('S','E', '0','0','0')
+ #define ERRCODE_ROWACL_ERROR                MAKE_SQLSTATE('S','E', '0','1','1')
+ #define ERRCODE_SELINUX_ERROR                MAKE_SQLSTATE('S','E', '0','2','1')
+ #define ERRCODE_SELINUX_AUDIT                MAKE_SQLSTATE('S','E', '0','2','2')
+ #define ERRCODE_SELINUX_INFO                MAKE_SQLSTATE('S','E', '0','2','3')
+
  /* Class XX - Internal Error (PostgreSQL-specific error class) */
  /* (this is for "can't-happen" conditions and software bugs) */
  #define ERRCODE_INTERNAL_ERROR                MAKE_SQLSTATE('X','X', '0','0','0')
diff -Nrpc base/src/include/utils/rel.h sepgsql/src/include/utils/rel.h
*** base/src/include/utils/rel.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/utils/rel.h    Fri Jan  9 10:54:34 2009
*************** typedef struct StdRdOptions
*** 218,223 ****
--- 218,225 ----
  {
      int32        vl_len_;        /* varlena header (do not touch directly!) */
      int            fillfactor;        /* page fill factor in percent (0..100) */
+     bool        row_level_acl;    /* validator of Row-level ACLs */
+     int            default_row_acl;/* dafault Row-level ACLs */
  } StdRdOptions;

  #define HEAP_MIN_FILLFACTOR            10
*************** typedef struct StdRdOptions
*** 232,237 ****
--- 234,255 ----
       ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))

  /*
+  * RelationGetRowLevelAcl
+  *        Returns the relations's avairability of Row-level ACLs.
+  */
+ #define RelationGetRowLevelAcl(relation)                                \
+     ((relation)->rd_options ?                                            \
+      ((StdRdOptions *) (relation)->rd_options)->row_level_acl : false)
+
+ /*
+  * RelationGetDefaultAcl
+  *        Returns the relations's default Row-level ACLs in text
+  */
+ #define RelationGetDefaultRowAcl(relation)                                \
+     ((relation)->rd_options ?                                            \
+      GET_STRING_RELOPTION(((StdRdOptions *) (relation)->rd_options), default_row_acl) : NULL)
+
+ /*
   * RelationGetTargetPageUsage
   *        Returns the relation's desired space usage per page in bytes.
   */
diff -Nrpc base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h
*** base/src/include/utils/syscache.h    Sat Jan  3 12:25:21 2009
--- sepgsql/src/include/utils/syscache.h    Sat Jan  3 15:58:18 2009
*************** enum SysCacheIdentifier
*** 69,74 ****
--- 69,76 ----
      RELNAMENSP,
      RELOID,
      RULERELNAME,
+     SECURITYOID,
+     SECURITYLABEL,
      STATRELATT,
      TSCONFIGMAP,
      TSCONFIGNAMENSP,
*************** extern HeapTuple SearchSysCache(int cach
*** 92,97 ****
--- 94,101 ----
                 Datum key1, Datum key2, Datum key3, Datum key4);
  extern void ReleaseSysCache(HeapTuple tuple);

+ extern void InsertSysCache(Oid relid, HeapTuple tuple);
+
  /* convenience routines */
  extern HeapTuple SearchSysCacheCopy(int cacheId,
                     Datum key1, Datum key2, Datum key3, Datum key4);

Re: SE-PostgreSQL Updated Revision (r1460)

From
KaiGai Kohei
Date:
Sorry, I attached incorrect patch file.
It is the correct one.

KaiGai Kohei wrote:
> Robert,
>
> The attached patch is a draft to replace RedHat/Fedora RPM centric
> expressions, to add a reference at "Database Roles and Privileges"
> chapter and a bit cleanups for the latest revision (r1467).
> In the previous revision, it noted users to check the version of
> RPM package, but the revised one notes actually required features.
> The version number is rewritten as a hint.
>
> What is your opinion?
>
> Thanks,
>
> KaiGai Kohei wrote:
>> Robert Haas wrote:
>>> On Fri, Jan 23, 2009 at 12:30 AM, KaiGai Kohei <kaigai@ak.jp.nec.com>
>>> wrote:
>>>> The patch set of SE-PostgreSQL and related stuff were updated (r1460).
>>>>
>>>> [1/5]
>>>> http://sepgsql.googlecode.com/files/sepostgresql-sepgsql-8.4devel-3-r1460.patch
>>>>
>>>> [2/5]
>>>> http://sepgsql.googlecode.com/files/sepostgresql-utils-8.4devel-3-r1460.patch
>>>>
>>>> [3/5]
>>>> http://sepgsql.googlecode.com/files/sepostgresql-policy-8.4devel-3-r1460.patch
>>>>
>>>> [4/5]
>>>> http://sepgsql.googlecode.com/files/sepostgresql-docs-8.4devel-3-r1460.patch
>>>>
>>>> [5/5]
>>>> http://sepgsql.googlecode.com/files/sepostgresql-tests-8.4devel-3-r1460.patch
>>>>
>>>
>>> KaiGai -
>>>
>>> I read through your docs patch tonight and did some copy editing.
>>> Please see the attached patches, which I hope you will find helpful.
>>> I have attached my suggested changes both as a patch against v1460
>>> (sepostgresql-docs-rmh-vs-1460.gz) and also as patch against CVS HEAD
>>> (sepostgresql-docs-rmh-vs-cvs-head), since I am not sure which is
>>> easier for you.  I have a couple of general comments about the
>>> documentation:
>>
>> Thanks your feedbacks!
>>
>> I basically applied your fixes as is, expect for the following items:
>> - You replaced
>>   ! Its providing access controls are not _bypassable_ for any clients
>> ...
>>     by
>>   ! The access controls implemented by SE-PostgrSQL may not be
>> _biased_ even ...
>>
>>   I wanted to express it is "unavoidable" here, so I changed as:
>>   ! The access controls implemented by SE-PostgrSQL may not be
>> _bypassed_ even ...
>>
>> - I found a typo: "MAC" is described as "MAc".
>>
>> And, I have a question about documentation manner.
>> - You represented "getpeercon()" function as a system call.
>>   But, it is actually a wrapper function of getsockopt(2) system call,
>>   so the "getpeercon(3)" is not a system call strictly.
>>   Is it necessary to represent these stuffs strictly correct?
>>   (Thus, I wrote it as "API" in the r1460.)
>>
>>
>>> 1. The docs as written are very Red Hat-centric, even to the point of
>>> making reference to specific versions of Red Hat RPMs.  I think that
>>> the community will find this unacceptable, as Red Hat is certainly not
>>> the only SELinux-enabled distribution and I presume that we want to
>>> support all of them to an equal degree.
>>
>> I guess you pointed out about:
>>  1. The "Requirement" section in "Build and Installation" assumes
>>     RedHat/Fedora's RPM package and its version number.
>>  2. The security context and security policy used to explanation
>>     assumes specific security policy.
>>  3. "Labeled IPsec" seciton points to "RedHatEL4 Security Guide",
>>     and it assumes the racoon's configuration files are deployed
>>     as RPM package doing.
>>
>> About 1, is it necessary to rip the RPM specific version number
>> and replace it as:
>>   selinux-policy which includes SE-PostgreSQL related stuffs.
>>
>> About 2, SELinux community provides its default security policy,
>> and distributor's policy (including RedHat's one) is a derivative
>> of the default policy.
>> It is developed independent from distributor's cycle.
>>   http://oss.tresys.com/projects/refpolicy
>>
>> http://oss.tresys.com/repos/refpolicy/trunk/policy/modules/services/postgresql.te
>>
>>
>> You can find some of sepgsql_xxxx identifiers in postgresql.te.
>> All the appeared identifiers are upstreamed, so these are not Red Hat
>> specific.
>>
>> About 3, If it rips the link to Red Hat and does not assume specific
>> path of "racoon.conf", the explnation become neutral.
>>
>>
>>> 2. Some of the information that is documented here properly belongs in
>>> other sections of the documentation.  For example, the information
>>> about GUCs clearly belongs somewhere in the section on server
>>> configuration where all of the other GUCs are documented, not in a
>>> separate sections about SE-PostgreSQL.
>>
>> These explanations are moved to "Security and Authentication" section
>> in "Chapter 18. Server Configuration".
>>
>>> I suspect that all of the
>>> information about row-level ACLs should be ripped out of security.sgml
>>> and inserted into an appropriate portion of the "Database Roles and
>>> Privileges" chapter, leaving this file to talk just about
>>> SE-PostgreSQL.
>>
>> It is indeed an aspect of row-level ACLs.
>> However, it is also a feature on PGACE framework, same as SE-PostgreSQL.
>> An idea is to put a reference to indicate the row-level ACLs section
>> on "Database Roles and Privileges" chapter, like:
>>
>>   PostgreSQL has an enhancement of database roles and privileges
>> mechanism
>>   which allows to database ACLs in row-level granuality. See, <xref ...>
>>   for more details.
>>
>> What do you think?
>>
>>> 3. It seems to me that the analogy between SQL DAC and Unix user/group
>>> DAC is mentioned far too many times, and there are other cases where
>>> information is repeated as well.  I think it might help to reorganize
>>> the document a bit so that you introduce concepts in the right order.
>>
>> Indeed, it was redundant explanation. Thanks for youe edit.
>>
>>> For example, the section that defines MAC and DAC is a ways down in
>>> the document, but you use those terms a whole bunch of times before
>>> defining them.  I'm not 100% sure that we even want to be defining MAC
>>> and DAC in our documentation, since those are general industry terms
>>> that are not PostgreSQL-specific.  But if we are going to define them
>>> then we should try to do so in the clearest way possible.
>>
>> I can add the definitions of terms.
>> However, it is unclear whether PostgreSQL documentation should include
>> them, or not. For example, wikipedia has enough explanation for their
>> generam meanings.
>>   http://en.wikipedia.org/wiki/Discretionary_Access_Control
>>   http://en.wikipedia.org/wiki/Mandatory_Access_Control
>>
>> It seems to me "Discretionary Access Control (DAC)" is an enough key
>> to search its meaning.
>>
>>> Overall, I would say there is a fair amount of work left to be done to
>>> get this documentation up to par, but it's a good start and I hope
>>> that the attached patches and suggestions will be helpful.
>>
>> I'm glad to see your help.
>> I'll pay my efforts for documentations also. But English is not my mother
>> language, so any suggestions are helpful for me.
>>
>> Thanks,
>
>


--
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
Index: sepgsql/doc/src/sgml/security.sgml
===================================================================
*** sepgsql/doc/src/sgml/security.sgml    (revision 1467)
--- sepgsql/doc/src/sgml/security.sgml    (working copy)
***************
*** 684,692 ****
         From this, the client can infer the existence of the invisible foreign
         key, an inference to which he is not entitled.

!        As a practical matter, this scenario can sometimes be avoided by using
!        non-natural primary and foreign keys, such as UUIDs.  This may make it
!        impossible to infer any meaningful data.
       </para>
     </sect2>
  </sect1>
--- 684,692 ----
         From this, the client can infer the existence of the invisible foreign
         key, an inference to which he is not entitled.

!        As a practical matter, this scenario can sometimes be avoided by using
!        non-natural primary and foreign keys, such as UUIDs.  This may make it
!        impossible to infer any meaningful data.
       </para>
     </sect2>
  </sect1>
***************
*** 702,729 ****
       We need the following packages to build and install
       SE-PostgreSQL properly. Please check it at first.
         </para>
!        <itemizedlist>
!      <listitem>
!        <para>
!          Linux kernel (2.6.23, or later)
!        </para>
!      </listitem>
!      <listitem>
!        <para>
!          libselinux and libselinux-devel (2.0.43, or later)
!        </para>
!      </listitem>
!      <listitem>
!        <para>
!          selinux-policy (3.4.2, or later)
!        </para>
!      </listitem>
!      <listitem>
!        <para>
!          policycoreutils (2.0.16, or later)
!        </para>
!      </listitem>
!        </itemizedlist>
       </sect3>

       <sect3>
--- 702,796 ----
       We need the following packages to build and install
       SE-PostgreSQL properly. Please check it at first.
         </para>
!
!        <variablelist>
!      <varlistentry>
!        <term><literal>Linux kernel</literal></term>
!        <listitem>
!          <para>
!            Linux kernel has to support SELinux feature, at least.
!            In addition, it is necessary to provide an interface to
!            obtain a list of supported object classes and permissions
!            via <filename>/selinux/class</filename>, which is available
!            on the Linux kernel 2.6.23 or later.
!          </para>
!        </listitem>
!      </varlistentry>
!
!      <varlistentry>
!        <term><literal>Security policy</literal></term>
!        <listitem>
!          <para>
!            The security policy of SELinux is neccesary to contain access
!            control rules related to database objects.
!            The recent upstreamed security policy already has a set of
!            rules for SE-PostgreSQL, as a part of policy for PostgreSQL.
!          </para>
!          <para>
!            In <literal>Red Hat EL</literal> or <literal>Fedora</literal>,
!            check the version number of <literal>selinux-policy</literal>
!            rpm package is <literal>3.4.2</literal>, or later.
!          </para>
!        </listitem>
!      </varlistentry>
!
!      <varlistentry>
!        <term><literal>libselinux</literal></term>
!        <listitem>
!          <para>
!            <literal>libselinux</literal> is a library to communicate
!            between applications and in-kernel SELinux, so it provides
!            us various kind of APIs and header definitions.
!            It is necessary to provide header definitions of object
!            classes and permissions related to database. Rest of
!            requirements are already included in older version.
!          </para>
!          <para>
!            In <literal>Red Hat EL</literal> or <literal>Fedora</literal>,
!            check the version number of <literal>libselinux</literal>
!            and <literal>libselinux-devel</literal> rpm packages are
!            <literal>2.0.46</literal>, or later.
!          </para>
!        </listitem>
!      </varlistentry>
!
!      <varlistentry>
!        <term><command>checkmodule</command></term>
!        <listitem>
!          <para>
!            The <command>checkmodule</command> is a policy compiler for
!            a modular policy package, such as
!            <literal>sepostgresql-devel.pp</literal> we provided.
!          </para>
!        </listitem>
!      </varlistentry>
!
!      <varlistentry>
!        <term><command>semodule</command></term>
!        <listitem>
!          <para>
!            The <command>semodule</command> is a command to manage
!            modular policy packages. It enables to link/unlink,
!            upgrade or load/unload modular policy packages, such as
!            <literal>sepostgresql-devel.pp</literal> we provided.
!          </para>
!        </listitem>
!      </varlistentry>
!
!      <varlistentry>
!        <term><command>restorecon</command></term>
!        <listitem>
!          <para>
!            The <command>restorecon</command> enables to assign
!            correct security context for files, directories and
!            any other objects on filesystem, based on the security
!            policy configuration.
!            It helps to assign correct security context on
!            installed files by hand.
!          </para>
!        </listitem>
!      </varlistentry>
!        </variablelist>
       </sect3>

       <sect3>
***************
*** 740,749 ****
  <prompt>$ </prompt><userinput>make -C src/backend/security/sepgsql/policy</userinput>
  </screen>
         <para>
!      The current default security policy of SELinux contains a set of
!      rules for SE-PostgreSQL on <literal>selinux-policy-3.4.2</literal>
!      or later. So, we don't need to install special purpose security
!      policy module now.
         </para>
         <para>
       However, SE-PostgreSQL also provides an optinal policy module
--- 807,815 ----
  <prompt>$ </prompt><userinput>make -C src/backend/security/sepgsql/policy</userinput>
  </screen>
         <para>
!      Please note that the recent upstreamed security policy of SELinux
!      contains a set of rules for SE-PostgreSQL, so we are not always
!      necessary to build security policy module.
         </para>
         <para>
       However, SE-PostgreSQL also provides an optinal policy module
***************
*** 914,922 ****
         </para>

         <para>
!      This section introduces the steps to set up labeled ipsec.
!
!      For more detailed information, visit <ulink
url="http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/en-US/Security_Guide/s1-vpn-ipsec.html">RedHat
EnterpriseLinux 4 - Security Guide</ulink> 
         </para>

         <sect4>
--- 980,989 ----
         </para>

         <para>
!      This section introduces the steps to set up labeled ipsec,
!      but it is necessity minimum configuration, so we recommend
!      you to refer external technical documents related to ipsec
!      for more details.
         </para>

         <sect4>
Index: sepgsql/doc/src/sgml/user-manag.sgml
===================================================================
*** sepgsql/doc/src/sgml/user-manag.sgml    (revision 1467)
--- sepgsql/doc/src/sgml/user-manag.sgml    (working copy)
***************
*** 29,34 ****
--- 29,40 ----
    <xref linkend="ddl">.
   </para>

+  <para>
+   PostgreSQL has an enhancement of database roles and privileges mechanism
+   which allows to set database ACLs in row-level granuality.
+   See, <xref linkend="security-row-level-acl"> for more details.
+  </para>
+
   <sect1 id="database-roles">
    <title>Database Roles</title>

Index: sepgsql/doc/src/sgml/config.sgml
===================================================================
*** sepgsql/doc/src/sgml/config.sgml    (revision 1467)
--- sepgsql/doc/src/sgml/config.sgml    (working copy)
***************
*** 750,756 ****
          specified mode, independent from kernel setting. Please note
          that those configuration requires in-kernel SELinux is not
          disabled. The <literal>disabled</literal> disables SE-PostgreSQL.
!     This parameter can only be set at server start.
         </para>
        </listitem>
       </varlistentry>
--- 750,758 ----
          specified mode, independent from kernel setting. Please note
          that those configuration requires in-kernel SELinux is not
          disabled. The <literal>disabled</literal> disables SE-PostgreSQL.
!     This parameter is available on a binary with SELinux support
!     (<literal>--enable-selinux</literal>), and can only be set at
!     server start.
         </para>
        </listitem>
       </varlistentry>
***************
*** 770,776 ****
        for saving storage consumption.
        The default is <literal>on</literal> which means row-level access
        controls are available.
!       This parameter can only be set at server start.
       </para>
         </listitem>
       </varlistentry>
--- 772,780 ----
        for saving storage consumption.
        The default is <literal>on</literal> which means row-level access
        controls are available.
!       This parameter is available on a binary with SELinux support
!       (<literal>--enable-selinux</literal>), and can only be set at
!       server start.
       </para>
         </listitem>
       </varlistentry>

Re: SE-PostgreSQL Updated Revision (r1460)

From
Robert Haas
Date:
> I basically applied your fixes as is, expect for the following items:
> - You replaced
>  ! Its providing access controls are not _bypassable_ for any clients ...
>    by
>  ! The access controls implemented by SE-PostgrSQL may not be _biased_ even
>  I wanted to express it is "unavoidable" here, so I changed as:
>  ! The access controls implemented by SE-PostgrSQL may not be _bypassed_
> even ...

Good catch, my mistake.

> - I found a typo: "MAC" is described as "MAc".

Also my mistake.

> And, I have a question about documentation manner.
> - You represented "getpeercon()" function as a system call.
>  But, it is actually a wrapper function of getsockopt(2) system call,
>  so the "getpeercon(3)" is not a system call strictly.
>  Is it necessary to represent these stuffs strictly correct?
>  (Thus, I wrote it as "API" in the r1460.)

Oh, OK.  It sounds a little awkward to me to refer to it as an API.
Perhaps we could just refer to it as getpeercon(3) and not call it
either an API or a system call.

> About 2, SELinux community provides its default security policy,
> and distributor's policy (including RedHat's one) is a derivative
> of the default policy.
> It is developed independent from distributor's cycle.
>  http://oss.tresys.com/projects/refpolicy
>  http://oss.tresys.com/repos/refpolicy/trunk/policy/modules/services/postgresql.te

OK, I wasn't aware of that.  I think perhaps you could spell this out
a little more in the docs so people understand that there is an
upstream version which includes SE-PostgreSQL support from version
<whatever>.

>> I suspect that all of the
>> information about row-level ACLs should be ripped out of security.sgml
>> and inserted into an appropriate portion of the "Database Roles and
>> Privileges" chapter, leaving this file to talk just about
>> SE-PostgreSQL.
>
> It is indeed an aspect of row-level ACLs.
> However, it is also a feature on PGACE framework, same as SE-PostgreSQL.
> An idea is to put a reference to indicate the row-level ACLs section
> on "Database Roles and Privileges" chapter, like:

Actually, I think this should probably be broken up into three
sections.  All of the stuff about how PGACE is not very interesting to
anyone who isn't a developer, so it should be moved to someplace under
"Internals".  I would suggest just adding a new chapter to the end of
that section, after "How the Planner Uses Statistics".

The database ACL stuff properly belongs in the "Database Roles and
Privileges" section, and needs to be moved there, not just a
cross-reference.

The discussion of enhanced security and SE-PostgreSQL is another new
chapter, probably immediately following "Database Roles and
Privileges".  I would suggest calling it "Enhanced Security and
SE-PostgreSQL".

>> For example, the section that defines MAC and DAC is a ways down in
>> the document, but you use those terms a whole bunch of times before
>> defining them.  I'm not 100% sure that we even want to be defining MAC
>> and DAC in our documentation, since those are general industry terms
>> that are not PostgreSQL-specific.  But if we are going to define them
>> then we should try to do so in the clearest way possible.
>
> I can add the definitions of terms.
> However, it is unclear whether PostgreSQL documentation should include
> them, or not. For example, wikipedia has enough explanation for their
> generam meanings.
>  http://en.wikipedia.org/wiki/Discretionary_Access_Control
>  http://en.wikipedia.org/wiki/Mandatory_Access_Control
>
> It seems to me "Discretionary Access Control (DAC)" is an enough key
> to search its meaning.

I agree.  I think you should go through and rip out all of the
definitions and explanations of what these terms mean, and just use
them in the appropriate context.  I think in general that the current
documentation spends far too much time explaining what SE-PostgreSQL
is and not enough time discussing the issues that are likely to come
up when you're actually using it.  For example, it seems to me that
anyone who has any interest in using SE-PostgreSQL to control access
to functions will need a much more complicated policy than what you
are proposing here, and there doesn't seem to be much discussion of
that issue.  I'm not really looking for specific examples of how to
build a policy so much as general considerations that you should keep
in mind when trying to prevent information leakage via functions.

> I'm glad to see your help.
> I'll pay my efforts for documentations also. But English is not my mother
> language, so any suggestions are helpful for me.

Well, your English is certainly better than my Japanese...

...Robert


Re: SE-PostgreSQL Updated Revision (r1460)

From
KaiGai Kohei
Date:
Robert Haas wrote:
>> And, I have a question about documentation manner.
>> - You represented "getpeercon()" function as a system call.
>>  But, it is actually a wrapper function of getsockopt(2) system call,
>>  so the "getpeercon(3)" is not a system call strictly.
>>  Is it necessary to represent these stuffs strictly correct?
>>  (Thus, I wrote it as "API" in the r1460.)
>
> Oh, OK.  It sounds a little awkward to me to refer to it as an API.
> Perhaps we could just refer to it as getpeercon(3) and not call it
> either an API or a system call.

I replaced all the "getpeercon()" by "getpeercon(3)", and removed
expression both "system call" and "API".

>> About 2, SELinux community provides its default security policy,
>> and distributor's policy (including RedHat's one) is a derivative
>> of the default policy.
>> It is developed independent from distributor's cycle.
>>  http://oss.tresys.com/projects/refpolicy
>>  http://oss.tresys.com/repos/refpolicy/trunk/policy/modules/services/postgresql.te
>
> OK, I wasn't aware of that.  I think perhaps you could spell this out
> a little more in the docs so people understand that there is an
> upstream version which includes SE-PostgreSQL support from version
> <whatever>.

I noted it as:
|  The upstreamed security policy (<literal>20080702</literal>
|  or later) already has a set of rules for SE-PostgreSQL,
|  as a part of PostgreSQL policy.

The "<whatever>" is not a tag, is it?

> Actually, I think this should probably be broken up into three
> sections.  All of the stuff about how PGACE is not very interesting to
> anyone who isn't a developer, so it should be moved to someplace under
> "Internals".  I would suggest just adding a new chapter to the end of
> that section, after "How the Planner Uses Statistics".
>
> The database ACL stuff properly belongs in the "Database Roles and
> Privileges" section, and needs to be moved there, not just a
> cross-reference.
>
> The discussion of enhanced security and SE-PostgreSQL is another new
> chapter, probably immediately following "Database Roles and
> Privileges".  I would suggest calling it "Enhanced Security and
> SE-PostgreSQL".

OK, I deployed these section as you suggested.
- The "Row-level Database ACLs" section is moved to the tail of
   Chapter 20. Database Roles and Privileges.
- The new "Enhanced Security and SE-PostgreSQL" is moved to Chapter.21.
- The new "Chapter 57. PGACE Security Framework" is moved to Chapter.57,
   next to the "56. How the Planner Uses Statistics".

 > I think in general that the current
> documentation spends far too much time explaining what SE-PostgreSQL
> is and not enough time discussing the issues that are likely to come
> up when you're actually using it.  For example, it seems to me that
> anyone who has any interest in using SE-PostgreSQL to control access
> to functions will need a much more complicated policy than what you
> are proposing here, and there doesn't seem to be much discussion of
> that issue.  I'm not really looking for specific examples of how to
> build a policy so much as general considerations that you should keep
> in mind when trying to prevent information leakage via functions.

I added a new section "21.3. Making a Security Policy", but it is
still empty. I think it is not necessary to document comprehensive
information about security policy, since it is not a SELinux document.

My plan is to introduce a simple copy & pastable example and steps to
build it (with standard toolchain) and to install it as an security
policy module.

Please wait for filling up the section...

Thanks,
--
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
diff -Nrpc base/doc/src/sgml/catalogs.sgml sepgsql/doc/src/sgml/catalogs.sgml
*** base/doc/src/sgml/catalogs.sgml    Fri Jan 23 10:23:37 2009
--- sepgsql/doc/src/sgml/catalogs.sgml    Tue Jan 27 18:48:27 2009
***************
*** 204,209 ****
--- 204,214 ----
       </row>

       <row>
+        <entry><link linkend="catalog-pg-security"><structname>pg_security</structname></link></entry>
+        <entry>text representation of security attribute</entry>
+      </row>
+
+      <row>
        <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
        <entry>dependencies on shared objects</entry>
       </row>
***************
*** 977,982 ****
--- 982,996 ----
       </row>

       <row>
+       <entry><structfield>attkind</structfield></entry>
+       <entry><type>char</type></entry>
+       <entry></entry>
+       <entry>
+        A copy of <literal>pg_class.relkind</> of this columns's relation
+       </entry>
+      </row>
+
+      <row>
        <entry><structfield>attnotnull</structfield></entry>
        <entry><type>bool</type></entry>
        <entry></entry>
***************
*** 4195,4200 ****
--- 4209,4270 ----

   </sect1>

+  <sect1 id="catalog-pg-security">
+    <title><structname>pg_security</structname></title>
+
+    <indexterm zone="catalog-pg-security">
+      <primary>pg_security</primary>
+    </indexterm>
+
+    <para>
+      The catalog <structname>pg_security</structname> stores text
+      representations of security attributes managed by the PGACE framework.
+    </para>
+
+    <para>
+      Security modules managed by the PGACE framework can associate security
+      attributes with individual tuples.  Because the same security attribute
+      value is typically associated with many individual objects, all values
+      known to the system are stored here and referenced by OID.  This OID is
+      called a <literal>security id</literal> and is part of the tuple header.
+    </para>
+
+    <para>
+      When an new security attribute is given to the system, a new tuple is
+      added to <structname>pg_security</structname> and a new <literal>security
+      id</literal> is assigned by PGACE.  On output, a
+      <literal>security id</literal> can be rendered as text using this
+      system catalog as a lookup table.
+    </para>
+
+    <para>
+      See <xref linkend="pgace-security-label"> for more information.
+    </para>
+
+    <table>
+      <title><structname>pg_security</>Columns</title>
+
+      <tgroup cols=4>
+        <thead>
+      <row>
+        <entry>Name</entry>
+        <entry>Type</entry>
+        <entry>References</entry>
+        <entry>Description</entry>
+      </row>
+        </thead>
+
+        <tbody>
+      <row>
+        <entry><structfield>seclabel</structfield></entry>
+        <entry><type>text</type></entry>
+        <entry></entry>
+        <entry>Text representation of security attribute</entry>
+      </row>
+        </tbody>
+      </tgroup>
+    </table>
+  </sect1>

   <sect1 id="catalog-pg-shdepend">
    <title><structname>pg_shdepend</structname></title>
diff -Nrpc base/doc/src/sgml/config.sgml sepgsql/doc/src/sgml/config.sgml
*** base/doc/src/sgml/config.sgml    Thu Jan 22 14:34:54 2009
--- sepgsql/doc/src/sgml/config.sgml    Tue Jan 27 18:48:27 2009
*************** SET ENABLE_SEQSCAN TO OFF;
*** 706,711 ****
--- 706,784 ----
        </listitem>
       </varlistentry>

+      <varlistentry id="guc-pgace-feature" xreflabel="pgace-feature">
+       <term><varname>pgace_feature</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>pgace_feature</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+     An identifier name of current working enhanced security feature on
+     PGACE security framework.
+     (see <xref linkend="pgace-security-framework"> for more information)
+     This parameter can only be set at server start.
+        </para>
+        <para>
+     Currently, this parameter has two possible values.
+         The one is <literal>none</literal> not to enable any
+         enhanced security features, and the other is
+         <literal>selinux</literal> to enable SE-PostgreSQL
+         feature.
+        </para>
+        <para>
+     If you run <command>initdb</command> with
+         <option>--pgace-feature=selinux</option>, this parameter is
+         automatically specified to <literal>selinux</literal>.
+         Otherwise, the default is <literal>none</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-sepostgresql" xreflabel="sepostgresql">
+       <term><varname>sepostgresql</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>sepostgresql</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+     Enables to choose the current working mode of SE-PostgreSQL.
+     This parameter has four possible values to specify working mode of
+         SE-PostgreSQL. The <literal>default</literal> is a default
+         selection. It always follows the configuration on operating
+         system. The <literal>enforcing</literal> and
+         <literal>permissive</literal> make SE-PostgreSQL work in the
+         specified mode, independent from kernel setting. Please note
+         that those configuration requires in-kernel SELinux is not
+         disabled. The <literal>disabled</literal> disables SE-PostgreSQL.
+     This parameter is available on a binary with SELinux support
+     (<literal>--enable-selinux</literal>), and can only be set at
+     server start.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry id="guc-sepostgresql-row-level" xreflabel="sepostgresql-row-level">
+        <term><varname>sepostgresql_row_level</varname> (<type>boolean</type>)</term>
+        <indexterm>
+      <primary><varname>sepostgresql_row_level</> configuration parameter</primary>
+        </indexterm>
+        <listitem>
+      <para>
+       Enables to turn on/off row-level access controls via SE-PostgreSQL.
+       The row-level access control feature requires additional 4-bytes
+       field for each tuples, so setting it <literal>off</literal> enables
+       to save the storage consumption, in return for the row-level granularity.
+       But we don't recommend it, unless you faces extremely severe requirements
+       for saving storage consumption.
+       The default is <literal>on</literal> which means row-level access
+       controls are available.
+       This parameter is available on a binary with SELinux support
+       (<literal>--enable-selinux</literal>), and can only be set at
+       server start.
+      </para>
+        </listitem>
+      </varlistentry>
+
      </variablelist>
      </sect2>
     </sect1>
diff -Nrpc base/doc/src/sgml/errcodes.sgml sepgsql/doc/src/sgml/errcodes.sgml
*** base/doc/src/sgml/errcodes.sgml    Mon Dec 29 20:19:56 2008
--- sepgsql/doc/src/sgml/errcodes.sgml    Sat Jan  3 16:58:53 2009
***************
*** 1420,1425 ****
--- 1420,1458 ----
  <entry>too_many_rows</entry>
  </row>

+ <row>
+ <entry spanname="span13"><emphasis role="bold">Class SE — Enhanced Security Error</></entry>
+ </row>
+
+ <row>
+ <entry><literal>SE000</literal></entry>
+ <entry>PGACE ERROR</entry>
+ <entry>pgace_framework_error</entry>
+ </row>
+
+ <row>
+ <entry><literal>SE011</literal></entry>
+ <entry>ROWACL ERROR</entry>
+ <entry>errors_in_row_level_acls</entry>
+ </row>
+
+ <row>
+ <entry><literal>SE021</literal></entry>
+ <entry>SELINUX ERROR</entry>
+ <entry>errors_in_sepostgresql</entry>
+ </row>
+
+ <row>
+ <entry><literal>SE022</literal></entry>
+ <entry>SELINUX AUDIT</entry>
+ <entry>audit_records_of_sepostgresql</entry>
+ </row>
+
+ <row>
+ <entry><literal>SE023</literal></entry>
+ <entry>SELINUX INFO</entry>
+ <entry>information_from_sepostgresql</entry>
+ </row>

  <row>
  <entry spanname="span13"><emphasis role="bold">Class XX — Internal Error</></entry>
diff -Nrpc base/doc/src/sgml/filelist.sgml sepgsql/doc/src/sgml/filelist.sgml
*** base/doc/src/sgml/filelist.sgml    Mon Jan  5 17:36:07 2009
--- sepgsql/doc/src/sgml/filelist.sgml    Tue Jan 27 18:48:27 2009
***************
*** 45,50 ****
--- 45,51 ----
  <!entity runtime       SYSTEM "runtime.sgml">
  <!entity config        SYSTEM "config.sgml">
  <!entity user-manag    SYSTEM "user-manag.sgml">
+ <!entity sepgsql       SYSTEM "sepgsql.sgml">
  <!entity wal           SYSTEM "wal.sgml">

  <!-- programmer's guide -->
***************
*** 84,89 ****
--- 85,91 ----
  <!entity planstats    SYSTEM "planstats.sgml">
  <!entity indexam    SYSTEM "indexam.sgml">
  <!entity nls        SYSTEM "nls.sgml">
+ <!entity pgace      SYSTEM "pgace.sgml">
  <!entity plhandler  SYSTEM "plhandler.sgml">
  <!entity protocol   SYSTEM "protocol.sgml">
  <!entity sources    SYSTEM "sources.sgml">
diff -Nrpc base/doc/src/sgml/pgace.sgml sepgsql/doc/src/sgml/pgace.sgml
*** base/doc/src/sgml/pgace.sgml    Thu Jan  1 09:00:00 1970
--- sepgsql/doc/src/sgml/pgace.sgml    Tue Jan 27 18:48:27 2009
***************
*** 0 ****
--- 1,234 ----
+ <chapter id="pgace-security-framework">
+   <title>PGACE Security Framework</title>
+
+   <para>
+     This chapter introduces the design of PGACE (PostgreSQL Access
+     Control Extension) security framework.
+   </para>
+   <para>
+     It enables to host various kind of enhanced security features
+     based on individual security design, granularity and platforms
+     with minimum impact toward the core PostgreSQL implementation.
+   </para>
+   <para>
+     Currently, it hosts two security features.
+
+     The one is Row-level ACLs feature. It is always enabled as an
+     enhancement of common database ACLs in row-level granularity,
+     as a hardwired DAC (Discretionary Access Control) feature.
+
+     It also allows users to choose a MAC (Mandatory Access Control)
+     via a GUC option <literal>pgace_feature</literal> on the startup
+     time. Its default is <literal>none</literal>, but currently we
+     have <literal>selinux</literal> option as a candidate.
+   </para>
+   <para>
+      PGACE consists of two major facilities.
+
+      The one is a set of security hooks deployed on some of strategic
+      points in the core PostgreSQL. It enables to implement new enhanced
+      security features with minimum impact to the core code.
+      The other is facilities to manage security attribute which can be
+      used to make a decision in access controls.
+   </para>
+
+   <sect1 id="pgace-security-hooks">
+     <title>PGACE Security Hooks</title>
+     <para>
+       This section introduces the overview of security hooks and
+       the way to invoke your security feature via the hooks.
+     </para>
+     <para>
+       All the security hooks are defined at
+       <filename>src/backend/security/pgaceHooks.c</filename>.
+
+       The security hooks are entry point of any enhanced security
+       feature chosen by users, so invoked ones can return its
+       decision on the given strategic point.
+     </para>
+     <para>
+       For example, the following <literal>pgaceHeapTupleInsert</literal>
+       is invoked just before inserting a new tuple into a relation.
+
+       If the hook returns <literal>false</literal> to the caller,
+       this insertion will be skipped. Otherwise, rest of steps will be
+       done normally.
+
+       The enhanced security feature can make its decision based on the
+       given arguments which shows context in the invocation of hook.
+       The <literal>rel</literal> and <literal>tuple</literal> show
+       the target relation and tuple in this example.
+     </para>
+ <programlisting>
+ bool
+ pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+                      bool is_internal, bool with_returning)
+ {
+     /* A wired DAC check */
+     if (!rowaclHeapTupleInsert(rel, tuple,
+                                is_internal,
+                                with_returning))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlHeapTupleInsert(rel, tuple,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+ </programlisting>
+     <para>
+       This example shows a hard-wired security feature (Row-level ACLs)
+       and one or no selectable feature can be invoked on a tuple insertion.
+
+       Is allows users to shoose an enhanced security feature from the
+       candidates via <literal>pgace_feature</literal> GUC option.
+       An integer variable of <literal>pgace_feature</literal> reflects
+       the configuration.
+     </para>
+     <para>
+       In this example, SE-PostgreSQL is an only candidate when the binary
+       is compiled with <option>--enable-selinux</option> option.
+     </para>
+     <para>
+       If you add a new security feature, you should add a new case
+       branch in the <literal>switch</literal> statement and an option
+       to the GUC parameter, for users selection.
+
+       The following example shows a case when someone add a new security
+       feature compiled when <literal>HAVE_FOO_SECURITY</literal> is
+       defined on build-time.
+     </para>
+ <programlisting>
+ bool
+ pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+                      bool is_internal, bool with_returning)
+ {
+     /* A wired DAC check */
+     if (!rowaclHeapTupleInsert(rel, tuple,
+                                is_internal,
+                                with_returning))
+         return false;
+
+     switch (pgace_feature)
+     {
+ #ifdef HAVE_SELINUX
+     case PGACE_FEATURE_SELINUX:
+         if (sepgsqlIsEnabled())
+             return sepgsqlHeapTupleInsert(rel, tuple,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+ #ifdef HAVE_FOO_SECURITY
+     case PGACE_FEATURE_FOO_SECURITY:
+         return fooSecurityHeapTupleInsert(rel, tuple,
+                                           is_internal,
+                                           with_returning);
+         break;
+ #endif
+     default:
+         break;
+     }
+     return true;
+ }
+ </programlisting>
+     <para>
+       Please note that individual specifications of security hooks are
+       described in <filename>src/backend/security/pgaceHooks.c</filename>
+       as a source code comment.
+       It will help you to understand.
+
+       If a part of given hooks are unnecessary for your security design,
+       keep it as is.
+     </para>
+   </sect1>
+
+   <sect1 id="pgace-security-label">
+     <title>Management of Security Label</title>
+     <para>
+       A security label is a human readable security attribute which can
+       be exported/imported via <literal>security_label</literal> system
+       column
+
+       PGACE provides a common facility to manage the security label of
+       tuples for various kind of security features.
+       It enables an enhanced security feature chosen to store and featch
+       a security label of tuples, and allows it to make a decision in
+       access controls based on the label.
+     </para>
+     <para>
+       It enables us to handle security labels in text form, but they
+       are internally handled as an alternative identifier.
+
+       We call it as a <literal>security id</literal>.
+       It is an object identifier (oid) of <literal>pg_security</literal>
+       system catalog which holds human readable text form of security labels.
+
+       So, we can mutually translate security id and security label via
+       <literal>pg_security</literal> system catalog.
+     </para>
+     <para>
+       The <literal>security id</literal> of tuple is stored within the
+       padding field of <literal>HeapTupleHeader</literal>, as if oid doing.
+
+       We can fetch it via <literal>HeapTupleGetSecLabel()</literal>
+       macro. If <literal>pgaceTupleDescHasSecLabel()</literal> security
+       hook returns <literal>true</literal> for the given relation, it
+       means a new tuple within the relation should have a field to store
+       its <literal>security id</literal>.
+
+       We can set it via <literal>HeapTupleSetSecLabel()</literal> macro.
+     </para>
+     <para>
+       A few utility functions are provided.
+
+       <literal>pgaceLookupSecurityId()</literal> returns a security id
+       towards given security label in text format. If it is not on
+       <literal>pg_security</literal>, it automatically inserts a new
+       entry and returns its security id.
+
+       <literal>pgaceLookupSecurityLabel()</literal> returns a security
+       label for the given sid.
+       If it is not on <literal>pg_security</literal>, it returns
+       <literal>NULL</literal>, so the guest should handle it as a
+       something like default or unlabeled.
+     </para>
+     <para>
+       The <literal>security_label</literal> system column allows users
+       to input a security label in text format on <command>INSERT</command>,
+       <command>UPDATE</command>, <command>COPY</command> and so on.
+
+       The given security label is translated into security id automatically,
+       and put on the security field of tuples. If user provides nothing as
+       a security label, it is initialized as <literal>InvalidOid</literal>.
+
+       The <literal>pgaceHeapTupleInsert()</literal> or others are invoked
+       later, so the enhanced security feature can know whether user gives
+       a proper security label or not. If no security label is given, it
+       can assign a default security label. If violated security label is
+       given, it can raise an error.
+
+       Vice versa, when user refers <literal>security_label</literal>,
+       the sid of tuples are automatically translated into text format.
+       If the sid is invalid, the enhanced security feature can return
+       an alternative string using
+       <literal>pgaceUnlabeledSecurityLabel()</literal> hook.
+     </para>
+     <para>
+       The Row-level Database ACLs uses this facility to manage security
+       labels to store its ACLs. Tuples have a capability to store both types
+       of security ids simultaneously, but enhanced security features should
+       not touch the row-level ACLs.
+     </para>
+   </sect1>
+ </chapter>
diff -Nrpc base/doc/src/sgml/postgres.sgml sepgsql/doc/src/sgml/postgres.sgml
*** base/doc/src/sgml/postgres.sgml    Tue May 13 14:48:54 2008
--- sepgsql/doc/src/sgml/postgres.sgml    Tue Jan 27 18:48:27 2009
***************
*** 148,153 ****
--- 148,154 ----
    &config;
    &client-auth;
    &user-manag;
+   &sepgsql;
    &manage-ag;
    &charset;
    &maintenance;
***************
*** 242,247 ****
--- 243,249 ----
    &storage;
    &bki;
    &planstats;
+   &pgace;

   </part>

diff -Nrpc base/doc/src/sgml/ref/initdb.sgml sepgsql/doc/src/sgml/ref/initdb.sgml
*** base/doc/src/sgml/ref/initdb.sgml    Wed Oct  1 14:42:02 2008
--- sepgsql/doc/src/sgml/ref/initdb.sgml    Sun Jan 25 21:01:21 2009
*************** PostgreSQL documentation
*** 234,239 ****
--- 234,252 ----
         </para>
        </listitem>
       </varlistentry>
+
+      <varlistentry>
+       <term><option>--pgace-feature=<replaceable>FEATURE</></option></term>
+       <listitem>
+        <para>
+         Selects an enhanced security feature, and writes it to
+         <filename>postgresql.conf</filename>.
+         The default is <literal>none</literal> which means no enhanced
+         security feature is activated.  Currently, the only available
+         enhanced security feature is <literal>selinux</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
     </para>

diff -Nrpc base/doc/src/sgml/ref/pg_dump.sgml sepgsql/doc/src/sgml/ref/pg_dump.sgml
*** base/doc/src/sgml/ref/pg_dump.sgml    Tue Jan  6 14:45:31 2009
--- sepgsql/doc/src/sgml/ref/pg_dump.sgml    Sun Jan 25 21:01:21 2009
*************** PostgreSQL documentation
*** 634,639 ****
--- 634,662 ----
         </para>
        </listitem>
       </varlistentry>
+
+      <varlistentry>
+       <term><option>--security-acl</option></term>
+       <listitem>
+        <para>
+         Dumps row-level ACLs with table contents.
+         ACLs can be restored via writable system column
+         <literal>security_acl</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><option>--security-label</option></term>
+       <listitem>
+        <para>
+         Dumps security labels managed by available enhanced security feature
+         with table contents.
+         Labels can be restored via writable system column
+         <literal>security_label</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
     </para>

diff -Nrpc base/doc/src/sgml/ref/pg_dumpall.sgml sepgsql/doc/src/sgml/ref/pg_dumpall.sgml
*** base/doc/src/sgml/ref/pg_dumpall.sgml    Tue Jan  6 14:45:31 2009
--- sepgsql/doc/src/sgml/ref/pg_dumpall.sgml    Sun Jan 25 21:01:21 2009
*************** PostgreSQL documentation
*** 334,339 ****
--- 334,361 ----
        </listitem>
       </varlistentry>

+      <varlistentry>
+       <term><option>--security-acl</option></term>
+       <listitem>
+        <para>
+         Dumps row-level ACLs with table contents.
+         ACLs can be restored via writable system column
+         <literal>security_acl</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><option>--security-label</option></term>
+       <listitem>
+        <para>
+         Dumps security labels managed by available enhanced security feature
+         with table contents.
+         Labels can be restored via writable system column
+         <literal>security_label</literal>.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
     </para>

diff -Nrpc base/doc/src/sgml/sepgsql.sgml sepgsql/doc/src/sgml/sepgsql.sgml
*** base/doc/src/sgml/sepgsql.sgml    Thu Jan  1 09:00:00 1970
--- sepgsql/doc/src/sgml/sepgsql.sgml    Tue Jan 27 18:48:27 2009
***************
*** 0 ****
--- 1,1018 ----
+ <chapter id="enhanced-security-and-sepostgresql">
+   <title>Enhanced Security and SE-PostgreSQL</title>
+   <para>
+     This chapter introduces enhanced security features which enable
+     <productname>PostgreSQL</productname> to provide mandatory
+     access controls, fine-grained access controls, and integration
+     of security policy between the operating system and
+     <productname>PostgreSQL</productname>.
+   </para>
+   <para>
+     The current version of <productname>PostgreSQL</productname>
+     has two enhanced security features: Row-level Database ACLs and
+     SE-PostgreSQL (Security Enhanced PostgreSQL).
+
+     Both are implemented using a common security framework called
+     PGACE (PostgreSQL Access Control Extensions), which provides basic
+     facilities to manage security attributes for database objects and
+     hooks to take control at strategic points within the core code.
+
+     See, <xref linkend="pgace-security-framework"> for more details.
+   </para>
+
+   <para>
+     The Row-level Database ACLs feature enables
+     <productname>PostgreSQL</productname> to apply existing DAC
+     (Discretionary Access Control) policy down to the level of individual
+     database rows.
+
+     Permissions can be set individually for <command>SELECT</command>,
+     <command>UPDATE</command> and <command>DELETE</command>.
+     Tuples for which permission is denied are silently filtered from the
+     result set.
+
+     It is also introduced at <xref linkend="row-level-database-acls">.
+   </para>
+
+   <para>
+     SE-PostgreSQL provides additional fine-grained mandatory access
+     controls on accesses to database objects via SQL. It makes a
+     decision whether a given client request should be
+     allowed, or not, by communicating with in-kernel SELinux.
+
+     SELinux holds its security policy which is a set of white listed
+     rules, and provides its access control decisions to the kernel
+     and userspace object manager.
+
+     The key concept of SE-PostgreSQL is integration of access controls
+     between operating system and database management systems.
+     It makes and applies its access control decision based on a single
+     unified security policy of operating system. It applies the security
+     context of peer process as privileges of client, independent from
+     database authentication.
+
+     The access controls implemented by SE-PostgrSQL may not be bypassed even
+     by privileged database users, making it an implementation of
+     MAC (Mandatory Access Control).  This enables it to prevent information
+     leaks and manipulation from a hypothetical malicious insider and reduced
+     the risks posed by SQL injection or other application-level bugs.
+   </para>
+
+   <para>
+     The PGACE security framework can host two security
+     features simultaneously.  Row-level Database ACLs are always provided as
+     a Discretionary Access Control (DAC) feature.  Mandatory Access Control
+     (MAC) must be explicitly enabled, and the only current implementation is
+     SE-PostgreSQL.
+
+     To enable SE-PostgreSQL, specify <option>--enable-selinux</option> option
+     at <command>configure</command> time,
+     and use the <option>--pgace-feature=selinux</option> option to
+     <command>initdb</command>.
+ <screen>
+ <prompt>$ </prompt><userinput>./configure --enable-selinux</userinput>
+ <prompt>$ </prompt><userinput>make install</userinput>
+ <prompt>$ </prompt><userinput>initdb --pgace-feature=selinux</userinput>
+ </screen>
+   </para>
+
+   <sect1 id="sepostgresql-features">
+     <title>SE-PostgreSQL Features</title>
+
+     <sect2 id="sepostgresql-single-unified-security-policy">
+       <title>Single unified security policy</title>
+
+       <para>
+     Because files and databases have similar capabilities to store
+     information assets, similar access controls are needed to maintain
+     confidentiality and data integrity.  Information stored in the
+     filesystem is accessed through system calls, such as
+     <command>write(2)</command>, while database objects are accessed
+     via SQL queries.
+       </para>
+       <para>
+     Without specific support from the database, there is no way for a
+     system-level mandatory access control policy to also apply to database
+     objects.  While access to the database files themselves could be
+     controlled, selective access to the contents of the database would
+     be forced to rely on whatever set of discretionary access controls
+     the database system provided.
+       </para>
+       <para>
+     SE-PostgreSQL overcomes this problem by making its access control
+     decision based on the security policy of SELinux.  This means that
+     the same security contexts and policies which are applied to filesystem
+     objects can also be applied to database objects, providing a
+     reliable foundation for comprehensive and integrated
+     security.
+       </para>
+       <para>
+     For example, when we store information labeled as
+     <literal>Classified</literal>, it cannot be accesed by users without
+     proper privileges regardless of whether it is stored in the filesystem
+     or within the database.  Because the decision to allow or deny access
+     is based only on the kernel's mandatory access control policy, and
+     not on PostgreSQL's existing Discretionary Access Control (DAC)
+     mechanisms, it cannot be subverted even by a database superuser.
+     (Similarly, SELinux in general can restrict privileges of any system
+     user, including <literal>root</literal>.)
+       </para>
+
+       <sect3 id="sepostgresql-security-context">
+     <title>Security Context</title>
+     <para>
+       The SELinux security policy is a set of access controls
+       rules which define who is allowed to perform which operations
+       on which objects.
+
+       These rules are described as a relationship of two entities
+       identified by common format called a security context.
+     </para>
+     <para>
+       The security context is a formatted string independent of
+       the type of object being labeled, such as:
+       <synopsis>system_u:object_r:postgresql_db_t:SystemHigh</synopsis>.
+
+       Security contexts are used to label files, sockets, and other
+       resources, and access decisions are made by testing whether
+       a particular relationship between two labels is permitted by
+       the access control rules.
+     </para>
+     <para>
+       For example, when a process labeled as
+       <literal>system_u:system_r:postgresql_t:s0</literal> tries to
+       write a log message to <filename>/var/log/postgresql.log</filename>
+       labeled as <literal>system_u:object_r:postgresql_log_t:s0</literal>,
+       it issues a <literal>write(2)</literal> system call; SELinux searches
+       its security policy database to determine the relationship between
+       the two security contexts.
+
+       If the entry found entry allows the operation, SELinux takes no
+       action;
+       otherwise, SELinux blocks the write.
+     </para>
+     <para>
+       Any object managers which want a decision come from SELinux
+       (including kernel) should have the capability to manage the security
+       contexts of their objects.
+
+       For example, most of major filesystems support xattr (Extended
+       Attributes) facilities which allows to store individual security
+       context of files.
+
+       SE-PostgreSQL also provides a facility to manage security context
+       of database objects. We can import/export them via
+       <literal>security_label</literal> system column.
+     </para>
+       </sect3>
+
+       <sect3 id="sepostgresql-client-privileges">
+     <title>Client Privileges</title>
+     <para>
+       In order to properly apply SELinux access controls, SE-PostgreSQL
+       needs to know the security context of each connected client.
+       It does this using the SELinux-supplied
+       <literal>getpeercon(3)</literal>, which provides the security
+       context of the process connected to the remote end of a particular
+       socket.
+
+       Note that the peer's security context is unrelated to the database
+       role used to authenticate, although the role-based permissions are
+       still applied and can deny access to database objects independently
+       of SE-PostgreSQL.
+     </para>
+
+     <para>
+       The security context of the connected client can be fetched using
+       the <literal>sepgsql_getcon()</literal> function, like this:
+ <screen>
+ <prompt>$ </prompt><userinput>id -Z</userinput>
+ unconfined_u:unconfined_r:unconfined_t:Classified
+ <prompt>$ </prompt><userinput>psql -q</userinput>
+ <prompt>postgres=# </prompt><userinput>SELECT sepgsql_getcon();</userinput>
+                   sepgsql_getcon
+ ---------------------------------------------------
+  unconfined_u:unconfined_r:unconfined_t:Classified
+ (1 row)
+ </screen>
+     </para>
+
+     <para>
+       When connecting to SE-PostgreSQL via a TCP/IP socket, labeled
+       networking must be set up to ensure that
+       <literal>getpeercon(3)</literal> returns the correct security
+       context.
+       See <xref linkend="sepostgresql-labeled-networks"> for more
+       defails.
+     </para>
+       </sect3>
+
+       <sect3 id="sepostgresql-mac">
+        <title>Mandatory Access Controls</title>
+        <para>
+      MAC (Mandatory Access Controls) a system of security controls that
+      is significantly different from DAC (Discretionary Access Controls)
+      such as filesystem permissions and database ACLs.
+
+      The major difference is that MAC requires that access controls be
+      applied to all managed objects without exception, including,
+      for example, the <literal>root</literal> user.
+
+      Every subject and object must be identified by security
+      labels, and MAC policy is described as a set of relationships
+      between pairs of labels. The security context of SELinux is a
+      sort of label, but includes more identifiers than
+      traditional trusted-operating system.
+
+      Finally, MAC should not allow resource owners to change its
+      access control rights arbitrarily. DAC design allows us
+      to change them using <command>chmod</command> or
+      <command>GRANT</command>. But in a MAC design, security policy
+      restricts these types of changes.
+        </para>
+        <para>
+      SELinux is a MAC feature in operating system, so it applies
+      MAC policy for accesses to resources managed by operating
+      system like files, sockets and so on.
+        </para>
+        <para>
+      SE-PostgreSQL is a symmetric feature in database management
+      system. It also applies MAC policy for accesses to any
+      database objects, managed by database management system
+      as a literal.
+        </para>
+       </sect3>
+     </sect2>
+
+     <sect2 id="sepostgresql-functionality">
+       <title>The functionality of SE-PostgreSQL</title>
+
+       <sect3 id="sepostgresql-security-label">
+        <title>security_label system column</title>
+        <para>
+      SE-PostgreSQL enables to import/export security context of
+      tuples via <literal>security_label</literal> system column
+      which is available v8.4, or later.
+        </para>
+        <para>
+      The following example shows a case when we export security
+      context of tuples and its data via <command>SELECT</command>.
+        </para>
+ <screen>
+ <prompt>postgres=# </prompt><userinput>SELECT security_label, * FROM drink;</userinput>
+                 security_label                | id | name  | price
+ ----------------------------------------------+----+-------+-------
+  system_u:object_r:sepgsql_table_t            |  1 | water |   100
+  system_u:object_r:sepgsql_table_t            |  2 | coke  |   120
+  system_u:object_r:sepgsql_table_t            |  3 | juice |   130
+  system_u:object_r:sepgsql_table_t            |  4 | cofee |   180
+  system_u:object_r:sepgsql_table_t:Classified |  5 | beer  |   240
+  system_u:object_r:sepgsql_table_t:Classified |  6 | sake  |   320
+ (6 rows)
+ </screen>
+        <para>
+      The security policy has rules to determine what security context
+      should be applied on newly inserted tuples.  SE-PostgreSQL applies
+      these rules unless a different context is explicitly specified by
+      including the <literal>security_label</literal> system column as
+      a target.
+        </para>
+ <screen>
+ <prompt># </prompt><userinput>INSERT INTO drink (security_label, id, name, price)
+      VALUES('system_u:object_r:sepgsql_ro_table_t', 7, 'tea', 130);</userinput>
+ </screen>
+        <para>
+      We can also change security context of tuples using
+      <literal>UPDATE</literal> statement.
+        </para>
+ <screen>
+ <prompt># </prompt><userinput>UPDATE drink SET security_label = 'system_u:object_r:sepgsql_ro_table_t' WHERE id <
4;</userinput>
+ </screen>
+        <para>
+      To use this feature, the client must have privileges to insert or
+      update the security context of tuples.  Otherwise, SE-PostgreSQL
+      will block any attempt to change them.
+        </para>
+        <para>
+      <command>SELECT INTO</command> or <command>CREATE TABLE AS</command>
+      including <literal>security_label</literal> system column as a target
+      is handled like a series of <literal>INSERT</literal> statements with
+      explicit security contexts.
+        </para>
+ <screen>
+ <prompt># </prompt><userinput>SELECT security_label, id, name, price / 2 AS price INTO discount FROM
drink;</userinput>
+ SELECT
+ postgres=# SELECT security_label, * FROM discount;
+                 security_label                | id | name  | price
+ ----------------------------------------------+----+-------+-------
+  system_u:object_r:sepgsql_table_t            |  1 | water |    50
+  system_u:object_r:sepgsql_table_t            |  2 | coke  |    60
+  system_u:object_r:sepgsql_table_t            |  3 | juice |    65
+  system_u:object_r:sepgsql_table_t            |  4 | cofee |    90
+  system_u:object_r:sepgsql_table_t:Classified |  5 | beer  |   120
+  system_u:object_r:sepgsql_table_t:Classified |  6 | sake  |   160
+ (6 rows)
+ </screen>
+        <para>
+      Please note that PostgreSQL uses system catalogs to represent
+      metadata of tables, columns and so on.
+      SE-PostgreSQL considers tuples within the system catalogs
+      show the security context of them.
+      For example, security context of tuples within
+      <literal>pg_class</literal> is considered as the one of table.
+        </para>
+      </sect3>
+
+      <sect3>
+        <title>Access controls on Tuples</title>
+        <para>
+      SE-PostgreSQL applies its access controls for each tuple.
+
+      If client tries to select, update or delete violated tuples,
+      these tuples are filtered from the result set or ignored from
+      the target of updates/deletions, as if they are not exist
+      on the table.
+        </para>
+        <para>
+      The following example shows the case when two users with different
+      security contexts try to <command>SELECT</command> the same table
+      with same SQL.
+      A <literal>Classified</literal> user can whole of the table.
+      But <literal>Classified</literal> tuples are invisible from
+      other users.
+        </para>
+ <screen>
+ <prompt>$ </prompt><userinput>id -Z</userinput>
+ unconfined_u:unconfined_r:unconfined_t:Classified
+ <prompt>$ </prompt><userinput>psql -q postgres</userinput>
+ <prompt>postgres=# </prompt><userinput>SELECT security_label, * FROM drink;</userinput>
+                 security_label                | id | name  | price
+ ----------------------------------------------+----+-------+-------
+  system_u:object_r:sepgsql_table_t            |  1 | water |   100
+  system_u:object_r:sepgsql_table_t            |  2 | coke  |   120
+  system_u:object_r:sepgsql_table_t            |  3 | juice |   130
+  system_u:object_r:sepgsql_table_t            |  4 | cofee |   180
+  system_u:object_r:sepgsql_table_t:Classified |  5 | beer  |   240
+  system_u:object_r:sepgsql_table_t:Classified |  6 | sake  |   320
+ (6 rows)
+ </screen>
+        <para>
+      It shows the <literal>Classified</literal> user got 6 tuples
+      with unconditional <command>SELECT</command> on drink table.
+
+      However, the unclassified (not labeled) user got 4 tuples
+      with same SQL, because 2 tuples were filtered out by SE-PostgreSQL.
+        </para>
+ <screen>
+ <prompt>$ </prompt><userinput>id -Z</userinput>
+ unconfined_u:unconfined_r:unconfined_t
+ <prompt>$ </prompt><userinput>psql -q postgres</userinput>
+ <prompt>postgres=# </prompt><userinput>SELECT security_label, * FROM drink;</userinput>
+ postgres=# SELECT security_label, * FROM drink;
+           security_label           | id | name  | price
+ -----------------------------------+----+-------+-------
+  system_u:object_r:sepgsql_table_t |  1 | water |   100
+  system_u:object_r:sepgsql_table_t |  2 | coke  |   120
+  system_u:object_r:sepgsql_table_t |  3 | juice |   130
+  system_u:object_r:sepgsql_table_t |  4 | cofee |   180
+ (4 rows)
+ </screen>
+      </sect3>
+
+      <sect3>
+        <title>Access controls on Tables and Columns</title>
+        <para>
+      SE-PostgreSQL also applies access controls to
+      tables and columns, but these are handled differently than row-level
+      security.
+        </para>
+        <para>
+      SE-PostgreSQL walks each query and considers the security contexts
+      of all tables and columns which appeare therin.  If any of those
+      tables or columns are ones which the client is not permitted to
+      access, SE-PostgreSQL aborts query execution and returns an error
+      to the client.
+        </para>
+        <para>
+      The following example shows a case when we declared a
+      <literal>ccredit</literal> column to store credit card numbers
+      as a secret column, hidden from unclassified users.
+        </para>
+ <screen>
+ CREATE TABLE customer (
+     cid     integer primary key,
+     cname   varchar(32),
+     credit  varchar(32)
+         SECURITY_LABEL = 'system_u:object_r:sepgsql_secret_table_t'
+ );
+ GRANT ALL ON customer TO PUBLIC;
+
+ INSERT INTO customer (cid, cname, credit)
+     VALUES ( 10, 'jack', '1111-2222-3333-4444'),
+            ( 13, 'adam', '5555-6666-7777-8888'),
+            ( 14, 'liza', '9876-5432-1098-7654');
+ </screen>
+
+ <screen>
+ $ id -Z
+ staff_u:staff_r:staff_t
+ $ psql -q postgres
+ postgres=# SELECT * FROM customer;
+ ERROR:  SELinux: denied { select } \
+     scontext=staff_u:staff_r:staff_t \
+     tcontext=system_u:object_r:sepgsql_secret_table_t \
+     tclass=db_column name=customer.credit
+ postgres=# SELECT cid, cname FROM customer;
+  cid | cname
+ -----+-------
+   10 | jack
+   13 | adam
+   14 | liza
+ (3 rows)
+ </screen>
+        <para>
+      In the first query, the user tried to refer all the columns;
+      SE-PostgreSQL prevents query execution because it contains
+      a reference to <literal>ccredit</literal> column which is
+      labeled as <literal>sepgsql_secret_table_t</literal>.
+
+      Then, the user tried to refer to all columns except the
+      <literal>ccredit</literal> column, so SE-PostgreSQL allowed the user
+      to execute the query, because no access control rules were violated.
+        </para>
+      </sect3>
+      <sect3>
+        <title>Access Controls on Functions</title>
+        <para>
+      SE-PostgreSQL also prevents clients from invoking functions for
+      which they do not have the necessary privileges.
+        </para>
+        <para>
+      In the current default security policy, a function declared by
+      administrative users (like <literal>unconfined_t</literal> domain)
+      is labeled as <literal>sepgsql_proc_t</literal>. Those declared
+      by normal users (like <literal>staff_t</literal> domain) are labeled
+      as <literal>staff_sepgsql_proc_exec_t</literal> and therefore can't
+      be invoked by administrative users.
+
+      This system prevents administrative users from invoking malicious
+      function by mistake. They have to confirm its declaration and
+      relabel its security context to <literal>sepgsql_proc_t</literal>.
+        </para>
+        <para>
+      SE-PostgreSQL allows clients to change privileges during
+      execution of specific functions called as trusted procedures.
+      These are similar to security definer functions, but for security
+      contexts rather than database roles.
+
+      In the current default security policy, trusted procedures are
+      functions labeled as <literal>sepgsql_trusted_proc_exec_t</literal>.
+      This enables users to execute these functions with administrative
+      privileges, providing a secure method for accessing confidential
+      objects.
+
+      This provides a simple all or nothing policy, but more complex
+      controls are possible if you install your own security policy module.
+        </para>
+        <para>
+      The following example declares a trusted procedure
+      <literal>show_credit</literal> to expose some, but not all, of the
+      information stored in <literal>customer.credit</literal>.
+
+      Assume that a client within <literal>staff_t</literal> domain
+      cannot access the <literal>credit</literal> column.  A trusted
+      trusted procedure can provide access, because the sub-queries
+      from the procedure are handled as an administrative domain.
+        </para>
+ <screen>
+ # CREATE OR REPLACE FUNCTION show_credit (integer) RETURNS text
+     LANGUAGE 'sql'
+     SECURITY_LABEL = 'system_u:object_r:sepgsql_trusted_proc_exec_t'
+     AS 'SELECT substring(credit from ''^[0-9]+-'') || ''xxxx-xxxx-xxxx''
+             FROM customer WHERE CID = $1';
+ CREATE FUNCTION
+ # \q
+ </screen>
+ <screen>
+ $ id -Z
+ staff_u:staff_r:staff_t
+ $ psql postgres
+ postgres=# SELECT * FROM customer;
+ ERROR:  SELinux: denied { select }      \
+     scontext=staff_u:staff_r:staff_t    \
+     tcontext=system_u:object_r:sepgsql_secret_table_t   \
+     tclass=db_column name=customer.credit
+ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
+  cid | cname |     show_credit
+ -----+-------+---------------------
+   10 | jack  | 1111-xxxx-xxxx-xxxx
+   13 | adam  | 5555-xxxx-xxxx-xxxx
+   14 | liza  | 9876-xxxx-xxxx-xxxx
+ (3 rows)
+
+ postgres=#
+ </screen>
+      </sect3>
+     </sect2>
+
+     <sect2>
+       <title>Limitations</title>
+       <para>
+     SE-PostgreSQL does not prevent information leaks via covert channels.
+     In other words, clients may be able to infer information about data
+     they cannot directly access.
+
+     If your requirements include elimination of covert channels,
+     SE-PostgreSQL may not be an adequate solution.  Covert channel
+     analysis was required by the upper B2 class of TCSEC and by
+     ISO/IEC15408 now, also known as CC (Common Criteria).
+       </para>
+       <para>
+     Covert channels are possible when, for example, a client attempts
+     to update or delete a tuple with primary key refered by
+     one or more invisible foreign keys. SE-PostgreSQL will prevent the
+     action to preserve reference integrity, even though the client
+     cannot see the key.
+
+     From this, the client can infer the existence of the invisible foreign
+     key, an inference to which he is not entitled.
+
+     As a practical matter, this scenario can sometimes be avoided by using
+     non-natural primary and foreign keys, such as UUIDs.  This may make it
+     impossible to infer any meaningful data.
+       </para>
+     </sect2>
+   </sect1>
+
+   <sect1 id="sepostgresql-administration">
+     <title>SE-PostgreSQL Administration</title>
+
+     <sect2>
+       <title>Build and Installation</title>
+       <sect3>
+     <title>Requirement</title>
+     <para>
+       We need the following packages to build and install
+       SE-PostgreSQL properly. Please check it at first.
+     </para>
+
+     <variablelist>
+       <varlistentry>
+         <term><literal>Linux kernel</literal></term>
+         <listitem>
+           <para>
+         Linux kernel has to support SELinux feature, at least.
+         In addition, it is necessary to provide an interface to
+         obtain a list of supported object classes and permissions
+         via <filename>/selinux/class</filename>, which is available
+         on the Linux kernel 2.6.23 or later.
+           </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
+         <term><literal>Security policy</literal></term>
+         <listitem>
+           <para>
+         The security policy of SELinux is neccesary to contain access
+         control rules related to database objects.
+         The upstreamed security policy (<literal>20080702</literal>
+         or later) already has a set of rules for SE-PostgreSQL,
+         as a part of PostgreSQL policy.
+           </para>
+           <para>
+         In <literal>Red Hat EL</literal> or <literal>Fedora</literal>,
+         check the version number of <literal>selinux-policy</literal>
+         rpm package is <literal>3.4.2</literal>, or later.
+           </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
+         <term><literal>libselinux</literal></term>
+         <listitem>
+           <para>
+         <literal>libselinux</literal> is a library to communicate
+         between applications and in-kernel SELinux, so it provides
+         us various kind of APIs and header definitions.
+         It is necessary to provide header definitions of object
+         classes and permissions related to database. Rest of
+         requirements are already included in older version.
+           </para>
+           <para>
+         In <literal>Red Hat EL</literal> or <literal>Fedora</literal>,
+         check the version number of <literal>libselinux</literal>
+         and <literal>libselinux-devel</literal> rpm packages are
+         <literal>2.0.46</literal>, or later.
+           </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
+         <term><command>checkmodule</command></term>
+         <listitem>
+           <para>
+         The <command>checkmodule</command> is a policy compiler
+         for a modular policy package, such as
+         <literal>sepostgresql-devel.pp</literal> we provided.
+           </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
+         <term><command>semodule</command></term>
+         <listitem>
+           <para>
+         The <command>semodule</command> is a command to manage
+         modular policy packages. It enables to link/unlink,
+         upgrade or load/unload modular policy packages, such as
+         <literal>sepostgresql-devel.pp</literal> we provided.
+           </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
+         <term><command>restorecon</command></term>
+         <listitem>
+           <para>
+         The <command>restorecon</command> enables to assign
+         correct security context for files, directories and
+         any other objects on filesystem, based on the security
+         policy configuration.
+         It helps to assign correct security context on
+         installed files by hand.
+           </para>
+         </listitem>
+       </varlistentry>
+     </variablelist>
+       </sect3>
+
+       <sect3>
+     <title>Build</title>
+     <para>
+       SE-PostgreSQL feature is disabled in the default build.
+       So, we have to add <option>--enable-selinux</option> option
+       to configure script. It enables to build your PostgreSQL
+       with a feature to suppor SELinux.
+     </para>
+ <screen>
+ <prompt>$ </prompt><userinput>./configure --enable-selinux</userinput>
+ <prompt>$ </prompt><userinput>make</userinput>
+ <prompt>$ </prompt><userinput>make -C src/backend/security/sepgsql/policy</userinput>
+ </screen>
+         <para>
+       Please note that the recent upstreamed security policy of SELinux
+       contains a set of rules for SE-PostgreSQL, so we are not always
+       necessary to build security policy module.
+     </para>
+     <para>
+       However, SE-PostgreSQL also provides an optinal policy module
+       for development Purpose. It enables several operations like
+       regression test, toggle audit logs and so on.
+     </para>
+     <para>
+       It is stored in
+       <filename>src/backend/security/sepgsql/policy</filename>,
+       and can be also build and installed as a binary security policy
+       module.
+     </para>
+       </sect3>
+
+       <sect3>
+     <title>Installation</title>
+     <para>
+       Please note that SELinux requires installed files, directories and
+       others should be labeled properly. RPM installation do it implicitly.
+
+       But, when you install PostgreSQL from a tarball, you should assign
+       proper security context for them. The development policy module
+       has a list that shows what security context should be assigned to
+       what files, and will help you to manage them, so we recommend to
+       install the <filename>sepostgresql-devel.pp</filename> also.
+     </para>
+ <screen>
+ <prompt># </prompt><userinput>make install</userinput>
+ <prompt># </prompt><userinput>/usr/sbin/semodule -i
src/backend/security/sepgsql/policy/sepostgresql-devel.pp</userinput>
+ <prompt># </prompt><userinput>/sbin/restorecon -R /usr/local/pgsql</userinput>
+ </screen>
+         <para>
+       <command>semodule</command> is a command to install and uninstall
+       a security policy module. <command>restorecon</command> is a command
+       to assign given directories its default security context recursively.
+     </para>
+
+     <para>
+       As we note later, all the files and directories to store database
+       cluster should be labeled as <literal>postgresql_db_t</literal>.
+       The default security policy assumes it is constructed at
+       <filename>/var/lib/pgsql/data</filename> as RPM doing.
+
+       If you want to set up it on another directory, you need to label
+       it by hand.
+     </para>
+ <screen>
+ <prompt># </prompt><userinput>mkdir -p $PGDATA</userinput>
+ <prompt># </prompt><userinput>chcon -t postgresql_db_t -R $PGDATA</userinput>
+ </screen>
+         <para>
+       Then, we need to run <command>initdb</command> to initialize
+       the database cluster. It is necessary to specify an option of
+       <option>--pgace-feature=selinux</option> which enables to initialize
+       database cluster with proper security context.
+
+       Without this option, no enhanced security feature will be activated
+       for the database cluster.
+     </para>
+ <screen>
+ <prompt>$ </prompt><userinput>initdb --pgace-feature=selinux</userinput>
+ <prompt>$ </prompt><userinput>pg_ctl start</userinput>
+ </screen>
+       </sect3>
+     </sect2>
+
+     <sect2>
+       <title>Backup and Restore</title>
+       <para>
+     When we restore a database from backups, we have to restore
+     its security context correctly as GRANT statement doing on
+     the restored table.
+       </para>
+
+       <para>
+     The <command>pg_dump</command> and <command>pg_dumpall</command>
+     have <option>--security-label</option> option to dump databases
+     with its security context.
+
+     The dumped image can be restored with proper security context.
+       </para>
+
+       <para>
+     Needless to say, a process which invokes <command>pg_dump</command>
+     must be allowed to refer whole of the database,
+     because SE-PostgreSQL filters all inaccessible tuples from the result
+     set, or aborts query execution if it references inaccessible
+     columns, tables, or functions.
+
+     These requirements are same when we restore the image.
+     A process which invokes <command>pg_restore</command> has to have
+     enough permission to create/insert these objects with the specified
+     security context.
+       </para>
+ <screen>
+ <prompt>$ </prompt><userinput>pg_dump --security-label postgres</userinput>
+     :
+       (snip)
+     :
+ --
+ -- Name: drink; Type: TABLE; Schema: public; Owner: kaigai; Tablespace:
+ --
+
+ CREATE TABLE drink (
+     id integer NOT NULL,
+     name character varying(48),
+     price integer
+ ) SECURITY_LABEL = 'system_u:object_r:sepgsql_table_t';
+     :
+       (snip)
+     :
+ --
+ -- Data for Name: drink; Type: TABLE DATA; Schema: public; Owner: kaigai
+ --
+
+ COPY drink (security_label, id, name, price) FROM stdin;
+ system_u:object_r:sepgsql_table_t       1       water   100
+ system_u:object_r:sepgsql_table_t       2       coke    120
+ system_u:object_r:sepgsql_table_t       3       juice   130
+ system_u:object_r:sepgsql_table_t       4       cofee   180
+ system_u:object_r:sepgsql_table_t:Classified    5       beer    240
+ system_u:object_r:sepgsql_table_t:Classified    6       sake    320
+ \.
+     :
+       (snip)
+     :
+ </screen>
+       <para>
+     This example shows a backup image with explicitly specified security
+     context on tables and tuples.
+
+     If a column has different security context from that of the table, it
+     must be specified explicitly in the <command>CREATE TABLE</command>
+     statement.
+       </para>
+     </sect2>
+
+     <sect2 id="sepostgresql-labeled-networks">
+       <title>Labeled Networks</title>
+       <para>
+     Labeled Networks is a technology which makes it possible to
+     obtain the security context of peer process communicating
+     via a TCP/IP connection.
+       </para>
+
+       <para>
+     This technology contains two facilities.
+     One is <literal>Labeled IPsec</literal>,
+     and the other is <literal>Static Fallback Context</literal>.
+
+     This section shows the way to set up both labeled networks
+     technology.
+       </para>
+
+       <sect3>
+     <title>Labeled IPsec</title>
+     <para>
+       When communicating using an encrypted channel
+       with IPsec, the key-exchange daemon <command>racoon</command>
+       on each machine exchanges encryption keys with its peer.
+
+       In addition, the security context of the connecting process is
+       delivered to the peer.
+
+       The delivered security context is cached on the kernel, and
+       it is exported into applications via the
+       <command>getpeercon(3)</command>.
+     </para>
+
+     <para>
+       This section introduces the steps to set up labeled ipsec,
+       but it is necessity minimum configuration, so we recommend
+       you to refer external technical documents related to ipsec
+       for more details.
+     </para>
+
+     <sect4>
+       <title>Example Environment</title>
+       <para>
+         In this introduction, we assume the server host where SE-PostgreSQL
+         works has an IP address of <literal>192.168.1.10</literal> and
+         the client host has IP address of <literal>192.168.1.200</literal>.
+
+         They are wired to same network, and can communicate each other
+         directly, without any router.
+       </para>
+
+       <para>
+         The purpose of the following configuration is to obtain
+         the security context of the process working on the client host
+         (<literal>192.168.1.200</literal>) from SE-PostgreSQL daemon
+         working on the server host (<literal>192.168.1.10</literal>),
+         when a connection is established.
+       </para>
+     </sect4>
+
+     <sect4>
+       <title>Adding a SPD entry</title>
+       <para>
+         It is needed to add an SPD (Security Policy Database) entry
+         to indicate applying IPsec on channels between two host.
+
+         Note that SPD is not a term of SELinux, it is a configuration
+         parameter of IPsec, although it's a bit confusable.
+       </para>
+
+       <para>
+         This example shows esp/transport mode should be applied on
+         outbounding packets from <literal>192.168.1.10</literal>
+         to <literal>192.168.1.200</literal>, and inbounding packets
+         from <literal>192.168.1.200</literal> to
+         <literal>192.168.1.10</literal>.
+ <programlisting>
+ spdadd 192.168.1.10 192.168.1.200 any
+ -ctx 1 1 "system_u:object_r:ipsec_spd_t:s0"
+ -P out ipsec
+ esp/transport//require;
+
+ spdadd 192.168.1.200 192.168.1.10 any
+ -ctx 1 1 "system_u:object_r:ipsec_spd_t:s0"
+ -P in ipsec
+ esp/transport//require;
+ </programlisting>
+             In addition, the second line of each entries enables to
+             turn on security context delivery during key exchanging.
+       </para>
+       <para>
+         You can load the above configuration using
+         <command>setkey</command> command.
+
+         Save your configuration into a temporary file,
+         and invoke <command>setkey</command> with the file
+         as an argument.
+ <screen>
+ <prompt># </prompt><userinput>/sbin/setkey -f <your configuration file></userinput>
+ </screen>
+       </para>
+       <para>
+         It is necessary to do this on both server side and client side.
+
+         Note that you have to switch the IP addreses on the client side.
+       </para>
+     </sect4>
+
+     <sect4>
+       <title>Racoon configuration</title>
+       <para>
+         In the next, we have to edit a configuration file of
+         <command>racoon</command> to specify encryption algorithm,
+         authentication method and so on.
+
+         This example uses pre shared key to authenticate the peer
+         host (<literal>192.168.1.200</literal>) for simplification.
+
+         Note that you have to switch the IP addresses on the client side.
+       </para>
+
+       <para>
+         An additional entry to
+         <filename>/etc/racoon/racoon.conf</filename>.
+ <programlisting>
+ remote 192.168.1.200
+ {
+     exchange_mode aggressive, main;
+     my_identifier address;
+     proposal {
+         encryption_algorithm 3des;
+         hash_algorithm sha1;
+         authentication_method pre_shared_key;
+         dh_group 2 ;
+     }
+ }
+ </programlisting>
+       </para>
+       <para>
+         We have to put a set of key string and IP address of destination
+         in <filename>/etc/racoon/psk.txt</filename>.
+
+         The key string has also to be same in the client side.
+ <programlisting>
+ # file for pre-shared keys used for IKE authentication
+ # format is: 'identifier' 'key'
+ # For example:
+ #
+ # 10.1.1.1 flibbertigibbet
+ # www.example.com 12345
+ # foo@www.example.com micropachycephalosaurus
+ 192.168.1.200    somethingsecrettext
+ </programlisting>
+           </para>
+     </sect4>
+
+     <sect4>
+       <title>Restart Racoon</title>
+       <para>
+         Restart racoon daemon in both peers.
+       </para>
+ <screen>
+ <prompt># </prompt><userinput>service racoon restart</userinput>
+ </screen>
+     </sect4>
+       </sect3>
+
+       <sect3>
+     <title>Static Fallback Context</title>
+     <para>
+       We cannot apply labeled IPsec for any situation.
+
+       It requires both server and cliets being SElinux'ed and IPsec is
+       available on their communication channel.
+     </para>
+     <para>
+       SELinux provides an alternative method to identify a security
+       context of peer process inside the <literal>getpeercon(3)</literal>,
+       when a connection come from unlabeled networks.
+
+       This facility is called as static fallback context.
+       It enables to return a security context associated with IP address
+       or network interfaces, as if a new connection come from labeled
+       networks.
+     </para>
+     <para>
+       <command>netlabelctl</command> within
+       <literal>netlabel_tools</literal>
+       package can be used to set up static fallback context.
+     </para>
+     <para>
+       The following example shows a case when we associate a security
+       context with connections come from <literal>192.168.1.0/24</literal>
+       via all network interfaces.
+ <screen>
+ <prompt># </prompt><userinput>/sbin/netlabelctl unlbl add default address:192.168.1.0/24
label:user_u:user_r:user_t:s0</userinput>
+ </screen>
+           The following example shows a case when we associate a security
+           context with connections come from <literal>192.168.2.0/24</literal>
+           via <literal>eth0</literal>.
+ <screen>
+ <prompt># </prompt><userinput>/sbin/netlabelctl unlbl add dev:eth0 address:192.168.2.0/24
label:staff_u:staff_r:staff_t:s0:c0</userinput>
+ </screen>
+     </para>
+       </sect3>
+     </sect2>
+   </sect1>
+
+   <sect1 id="sepostgresql-policy">
+     <title>Making a Security Policy</title>
+     <para>
+       This section introduces steps to make your own security policy
+       modules, and to install them.
+     </para>
+   </sect1>
+ </chapter>
diff -Nrpc base/doc/src/sgml/user-manag.sgml sepgsql/doc/src/sgml/user-manag.sgml
*** base/doc/src/sgml/user-manag.sgml    Wed Oct 29 13:39:02 2008
--- sepgsql/doc/src/sgml/user-manag.sgml    Tue Jan 27 18:48:27 2009
*************** DROP ROLE <replaceable>name</replaceable
*** 502,505 ****
--- 502,655 ----
    </para>
   </sect1>

+  <sect1 id="row-level-database-acls">
+    <title>Row-level Database ACLs</title>
+    <para>
+      This section introduces design and implementation of the Row-level
+      Database ACLs in PostgreSQL. It makes it possible to assign ACLs
+      to each individual tuple, and works as a supplemental facility to
+      existing database ACLs applied to tables and columns.
+    </para>
+
+    <sect2>
+      <title>Design</title>
+      <para>
+        The design of Row-level Database ACLs feature inherits many things
+        from existing access control mechanism. For example, it is a sort
+        of discretionary access control mechanism, so it allows the owner of
+        a resource to change its access control rights, and does not affect
+        database superusers.
+      </para>
+      <para>
+        This feature is implemented as a guest of PGACE security framework,
+        but wired feature, not a selectable one.
+      </para>
+      <para>
+        It works as if there is a filter to drop inaccessible tuples on scanning
+        the relation. Just before the executor scans relation, tables and
+        columns level ACLs are also checked, and the feature makes a decision
+        as to whether the tuple should be returned, ot not.
+      </para>
+      <para>
+        We provides four kind of permissions on tuples.
+        These are <literal>SELECT</literal>, <literal>UPDATE</literal>,
+        <literal>DELETE</literal> and <literal>REFERENCES</literal>.
+
+        An <literal>INSERT</literal> permission would not make sense,
+        because the target does not exist when it should be checked.
+
+        You should control insertion of tuples via table-level ACLs.
+      </para>
+      <para>
+        PostgreSQL implements foreign key constraints as built-in trigger
+        functions, and it invokes another query to maintain referential
+        integrity.
+
+        It is an exception case of the filtering. In this case, it raises
+        an error to abort current transation, if the query tries to fetch
+        an inaccessible tuple.
+
+        As with SE-PostgreSQL, unprivileged users may be able to infer
+        information about tuples they cannot access directly.
+      </para>
+      <para>
+        This feature is activated via table option of
+        <literal>row_level_acl</literal>. It can heve either of
+        <literal>on</literal> or <literal>off</literal> in the default.
+
+        It enables us to refer or set per-tuple ACLs via the
+        <literal>security_acl</literal> system column.
+        We can modify it via <literal>UPDATE</literal>,
+        <literal>INSERT</literal> with explict ACLs on the system column.
+
+        A feature to set default ACLs is also available.
+        A new table option of <literal>default_row_acl</literal> enables to
+        specify a default ACLs of for newly inserted tuples.
+      </para>
+    </sect2>
+
+    <sect2>
+      <title>Administration</title>
+      <sect3>
+        <title>Setup Row-level ACLs</title>
+        <para>
+      The <literal>row_level_acl</literal> table option on
+      <literal>CREATE TABLE</literal> or <literal>ALTER TABLE</literal>
+      is used to activate the Row-level Database ACLs.
+        </para>
+ <screen>
+ <prompt># </prompt><userinput>CREATE TABLE drink (
+       id      int primary key,
+       name    text,
+       price   int
+   ) WITH (row_level_acl=on);</userinput>
+ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "drink_pkey" for table "drink"
+ CREATE TABLE
+ </screen>
+        <para>
+      We can set Row-level ACLs via <literal>security_acl</literal>
+      system column.  For example:
+        </para>
+ <screen>
+ <prompt># </prompt><userinput>UPDATE drink SET security_acl = '{=r/postgres}' WHERE id in (3,4,5);</userinput>
+ UPDATE 3
+ <prompt># </prompt><userinput>SELECT security_acl, * FROM drink order by id;</userinput>
+          security_acl           | id | name  | price
+ --------------------------------+----+-------+-------
+  {kaigai=rwd/kaigai}            |  1 | water |   100
+  {kaigai=rwd/kaigai}            |  2 | coke  |   120
+  {=r/kaigai}                    |  3 | juice |   130
+  {=r/kaigai}                    |  4 | cofee |   180
+  {=r/kaigai}                    |  5 | beer  |   240
+  {kaigai=r/kaigai,ymj=r/kaigai} |  6 | sake  |   320
+ (6 rows)
+ </screen>
+        <para>
+      The database user must have ownership of the relation or privileges
+      of database superuser in order to update
+      <literal>security_acl</literal>.
+
+      Please note that this feature considers the owner of a tuple to be
+      the owner of the table in which they are stored, rather than the
+      client who inserted the tuple.
+        </para>
+      </sect3>
+
+      <sect3>
+        <title>Backup and Restore</title>
+        <para>
+      The <literal>--security-acl</literal> option to
+      <command>pg_dump</command> and <command>pg_dumpall</command>
+      can be used to dump tables with row-level ACLs.  With this option,
+      the output will contain the <literal>security_acl</literal> system
+      column if the row-level ACLs feature is enabled on the target
+      table.
+        </para>
+ <screen>
+ <prompt>$ </prompt><userinput>pg_dump --security-acl postgres</userinput>
+           :
+ CREATE TABLE drink (
+     id integer NOT NULL,
+     name text,
+     price integer
+ )
+ WITH (row_level_acl=on);
+           :
+ --
+ -- Data for Name: drink; Type: TABLE DATA; Schema: public; Owner: kaigai
+ --
+
+ COPY drink (security_acl, id, name, price) FROM stdin;
+ {kaigai=rwd/kaigai}     1       water   100
+ {kaigai=rwd/kaigai}     2       coke    120
+ {=r/kaigai}     3       juice   130
+ {=r/kaigai}     4       coffee  180
+ {=r/kaigai}     5       beer    240
+ {kaigai=r/kaigai,ymj=rw/kaigai} 6       sake    320
+ \.
+           :
+ </screen>
+      </sect3>
+    </sect2>
+  </sect1>
  </chapter>