Header And Logo

PostgreSQL
| The world's most advanced open source database.

dml.c

Go to the documentation of this file.
00001 /* -------------------------------------------------------------------------
00002  *
00003  * contrib/sepgsql/dml.c
00004  *
00005  * Routines to handle DML permission checks
00006  *
00007  * Copyright (c) 2010-2013, PostgreSQL Global Development Group
00008  *
00009  * -------------------------------------------------------------------------
00010  */
00011 #include "postgres.h"
00012 
00013 #include "access/htup_details.h"
00014 #include "access/sysattr.h"
00015 #include "access/tupdesc.h"
00016 #include "catalog/catalog.h"
00017 #include "catalog/heap.h"
00018 #include "catalog/dependency.h"
00019 #include "catalog/pg_attribute.h"
00020 #include "catalog/pg_class.h"
00021 #include "catalog/pg_inherits_fn.h"
00022 #include "commands/seclabel.h"
00023 #include "commands/tablecmds.h"
00024 #include "executor/executor.h"
00025 #include "nodes/bitmapset.h"
00026 #include "utils/lsyscache.h"
00027 #include "utils/syscache.h"
00028 
00029 #include "sepgsql.h"
00030 
00031 /*
00032  * fixup_whole_row_references
00033  *
00034  * When user reference a whole of row, it is equivalent to reference to
00035  * all the user columns (not system columns). So, we need to fix up the
00036  * given bitmapset, if it contains a whole of the row reference.
00037  */
00038 static Bitmapset *
00039 fixup_whole_row_references(Oid relOid, Bitmapset *columns)
00040 {
00041     Bitmapset  *result;
00042     HeapTuple   tuple;
00043     AttrNumber  natts;
00044     AttrNumber  attno;
00045     int         index;
00046 
00047     /* if no whole of row references, do not anything */
00048     index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
00049     if (!bms_is_member(index, columns))
00050         return columns;
00051 
00052     /* obtain number of attributes */
00053     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
00054     if (!HeapTupleIsValid(tuple))
00055         elog(ERROR, "cache lookup failed for relation %u", relOid);
00056     natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
00057     ReleaseSysCache(tuple);
00058 
00059     /* fix up the given columns */
00060     result = bms_copy(columns);
00061     result = bms_del_member(result, index);
00062 
00063     for (attno = 1; attno <= natts; attno++)
00064     {
00065         tuple = SearchSysCache2(ATTNUM,
00066                                 ObjectIdGetDatum(relOid),
00067                                 Int16GetDatum(attno));
00068         if (!HeapTupleIsValid(tuple))
00069             continue;
00070 
00071         if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
00072             continue;
00073 
00074         index = attno - FirstLowInvalidHeapAttributeNumber;
00075 
00076         result = bms_add_member(result, index);
00077 
00078         ReleaseSysCache(tuple);
00079     }
00080     return result;
00081 }
00082 
00083 /*
00084  * fixup_inherited_columns
00085  *
00086  * When user is querying on a table with children, it implicitly accesses
00087  * child tables also. So, we also need to check security label of child
00088  * tables and columns, but here is no guarantee attribute numbers are
00089  * same between the parent ans children.
00090  * It returns a bitmapset which contains attribute number of the child
00091  * table based on the given bitmapset of the parent.
00092  */
00093 static Bitmapset *
00094 fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
00095 {
00096     AttrNumber  attno;
00097     Bitmapset  *tmpset;
00098     Bitmapset  *result = NULL;
00099     char       *attname;
00100     int         index;
00101 
00102     /*
00103      * obviously, no need to do anything here
00104      */
00105     if (parentId == childId)
00106         return columns;
00107 
00108     tmpset = bms_copy(columns);
00109     while ((index = bms_first_member(tmpset)) > 0)
00110     {
00111         attno = index + FirstLowInvalidHeapAttributeNumber;
00112 
00113         /*
00114          * whole-row-reference shall be fixed-up later
00115          */
00116         if (attno == InvalidAttrNumber)
00117         {
00118             result = bms_add_member(result, index);
00119             continue;
00120         }
00121 
00122         attname = get_attname(parentId, attno);
00123         if (!attname)
00124             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
00125                  attno, parentId);
00126         attno = get_attnum(childId, attname);
00127         if (attno == InvalidAttrNumber)
00128             elog(ERROR, "cache lookup failed for attribute %s of relation %u",
00129                  attname, childId);
00130 
00131         index = attno - FirstLowInvalidHeapAttributeNumber;
00132         result = bms_add_member(result, index);
00133 
00134         pfree(attname);
00135     }
00136     bms_free(tmpset);
00137 
00138     return result;
00139 }
00140 
00141 /*
00142  * check_relation_privileges
00143  *
00144  * It actually checks required permissions on a certain relation
00145  * and its columns.
00146  */
00147 static bool
00148 check_relation_privileges(Oid relOid,
00149                           Bitmapset *selected,
00150                           Bitmapset *modified,
00151                           uint32 required,
00152                           bool abort_on_violation)
00153 {
00154     ObjectAddress object;
00155     char       *audit_name;
00156     Bitmapset  *columns;
00157     int         index;
00158     char        relkind = get_rel_relkind(relOid);
00159     bool        result = true;
00160 
00161     /*
00162      * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
00163      * system catalogs using DMLs - clients cannot reference/modify toast
00164      * relations using DMLs
00165      */
00166     if (sepgsql_getenforce() > 0)
00167     {
00168         Oid         relnamespace = get_rel_namespace(relOid);
00169 
00170         if (IsSystemNamespace(relnamespace) &&
00171             (required & (SEPG_DB_TABLE__UPDATE |
00172                          SEPG_DB_TABLE__INSERT |
00173                          SEPG_DB_TABLE__DELETE)) != 0)
00174             ereport(ERROR,
00175                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00176                      errmsg("SELinux: hardwired security policy violation")));
00177 
00178         if (relkind == RELKIND_TOASTVALUE)
00179             ereport(ERROR,
00180                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00181                      errmsg("SELinux: hardwired security policy violation")));
00182     }
00183 
00184     /*
00185      * Check permissions on the relation
00186      */
00187     object.classId = RelationRelationId;
00188     object.objectId = relOid;
00189     object.objectSubId = 0;
00190     audit_name = getObjectIdentity(&object);
00191     switch (relkind)
00192     {
00193         case RELKIND_RELATION:
00194             result = sepgsql_avc_check_perms(&object,
00195                                              SEPG_CLASS_DB_TABLE,
00196                                              required,
00197                                              audit_name,
00198                                              abort_on_violation);
00199             break;
00200 
00201         case RELKIND_SEQUENCE:
00202             Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
00203 
00204             if (required & SEPG_DB_TABLE__SELECT)
00205                 result = sepgsql_avc_check_perms(&object,
00206                                                  SEPG_CLASS_DB_SEQUENCE,
00207                                                  SEPG_DB_SEQUENCE__GET_VALUE,
00208                                                  audit_name,
00209                                                  abort_on_violation);
00210             break;
00211 
00212         case RELKIND_VIEW:
00213             result = sepgsql_avc_check_perms(&object,
00214                                              SEPG_CLASS_DB_VIEW,
00215                                              SEPG_DB_VIEW__EXPAND,
00216                                              audit_name,
00217                                              abort_on_violation);
00218             break;
00219 
00220         default:
00221             /* nothing to be checked */
00222             break;
00223     }
00224     pfree(audit_name);
00225 
00226     /*
00227      * Only columns owned by relations shall be checked
00228      */
00229     if (relkind != RELKIND_RELATION)
00230         return true;
00231 
00232     /*
00233      * Check permissions on the columns
00234      */
00235     selected = fixup_whole_row_references(relOid, selected);
00236     modified = fixup_whole_row_references(relOid, modified);
00237     columns = bms_union(selected, modified);
00238 
00239     while ((index = bms_first_member(columns)) >= 0)
00240     {
00241         AttrNumber  attnum;
00242         uint32      column_perms = 0;
00243 
00244         if (bms_is_member(index, selected))
00245             column_perms |= SEPG_DB_COLUMN__SELECT;
00246         if (bms_is_member(index, modified))
00247         {
00248             if (required & SEPG_DB_TABLE__UPDATE)
00249                 column_perms |= SEPG_DB_COLUMN__UPDATE;
00250             if (required & SEPG_DB_TABLE__INSERT)
00251                 column_perms |= SEPG_DB_COLUMN__INSERT;
00252         }
00253         if (column_perms == 0)
00254             continue;
00255 
00256         /* obtain column's permission */
00257         attnum = index + FirstLowInvalidHeapAttributeNumber;
00258 
00259         object.classId = RelationRelationId;
00260         object.objectId = relOid;
00261         object.objectSubId = attnum;
00262         audit_name = getObjectDescription(&object);
00263 
00264         result = sepgsql_avc_check_perms(&object,
00265                                          SEPG_CLASS_DB_COLUMN,
00266                                          column_perms,
00267                                          audit_name,
00268                                          abort_on_violation);
00269         pfree(audit_name);
00270 
00271         if (!result)
00272             return result;
00273     }
00274     return true;
00275 }
00276 
00277 /*
00278  * sepgsql_dml_privileges
00279  *
00280  * Entrypoint of the DML permission checks
00281  */
00282 bool
00283 sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
00284 {
00285     ListCell   *lr;
00286 
00287     foreach(lr, rangeTabls)
00288     {
00289         RangeTblEntry *rte = lfirst(lr);
00290         uint32      required = 0;
00291         List       *tableIds;
00292         ListCell   *li;
00293 
00294         /*
00295          * Only regular relations shall be checked
00296          */
00297         if (rte->rtekind != RTE_RELATION)
00298             continue;
00299 
00300         /*
00301          * Find out required permissions
00302          */
00303         if (rte->requiredPerms & ACL_SELECT)
00304             required |= SEPG_DB_TABLE__SELECT;
00305         if (rte->requiredPerms & ACL_INSERT)
00306             required |= SEPG_DB_TABLE__INSERT;
00307         if (rte->requiredPerms & ACL_UPDATE)
00308         {
00309             if (!bms_is_empty(rte->modifiedCols))
00310                 required |= SEPG_DB_TABLE__UPDATE;
00311             else
00312                 required |= SEPG_DB_TABLE__LOCK;
00313         }
00314         if (rte->requiredPerms & ACL_DELETE)
00315             required |= SEPG_DB_TABLE__DELETE;
00316 
00317         /*
00318          * Skip, if nothing to be checked
00319          */
00320         if (required == 0)
00321             continue;
00322 
00323         /*
00324          * If this RangeTblEntry is also supposed to reference inherited
00325          * tables, we need to check security label of the child tables. So, we
00326          * expand rte->relid into list of OIDs of inheritance hierarchy, then
00327          * checker routine will be invoked for each relations.
00328          */
00329         if (!rte->inh)
00330             tableIds = list_make1_oid(rte->relid);
00331         else
00332             tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
00333 
00334         foreach(li, tableIds)
00335         {
00336             Oid         tableOid = lfirst_oid(li);
00337             Bitmapset  *selectedCols;
00338             Bitmapset  *modifiedCols;
00339 
00340             /*
00341              * child table has different attribute numbers, so we need to fix
00342              * up them.
00343              */
00344             selectedCols = fixup_inherited_columns(rte->relid, tableOid,
00345                                                    rte->selectedCols);
00346             modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
00347                                                    rte->modifiedCols);
00348 
00349             /*
00350              * check permissions on individual tables
00351              */
00352             if (!check_relation_privileges(tableOid,
00353                                            selectedCols,
00354                                            modifiedCols,
00355                                            required, abort_on_violation))
00356                 return false;
00357         }
00358         list_free(tableIds);
00359     }
00360     return true;
00361 }