]> granicus.if.org Git - postgresql/commitdiff
Userspace access vector cache for contrib/sepgsql.
authorRobert Haas <rhaas@postgresql.org>
Thu, 1 Sep 2011 12:37:33 +0000 (08:37 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 1 Sep 2011 12:38:40 +0000 (08:38 -0400)
KaiGai Kohei

12 files changed:
configure
configure.in
contrib/sepgsql/Makefile
contrib/sepgsql/dml.c
contrib/sepgsql/hooks.c
contrib/sepgsql/proc.c
contrib/sepgsql/relation.c
contrib/sepgsql/schema.c
contrib/sepgsql/selinux.c
contrib/sepgsql/sepgsql.h
contrib/sepgsql/uavc.c [new file with mode: 0644]
doc/src/sgml/sepgsql.sgml

index 039d5f863330b1adf6c3bc0817228d0c45f49a52..31bc8fb374bb01ec5ac417c0afb7bea42316ae6a 100755 (executable)
--- a/configure
+++ b/configure
@@ -9481,9 +9481,9 @@ fi
 # for contrib/sepgsql
 if test "$with_selinux" = yes; then
 
-{ $as_echo "$as_me:$LINENO: checking for selinux_sepgsql_context_path in -lselinux" >&5
-$as_echo_n "checking for selinux_sepgsql_context_path in -lselinux... " >&6; }
-if test "${ac_cv_lib_selinux_selinux_sepgsql_context_path+set}" = set; then
+{ $as_echo "$as_me:$LINENO: checking for selinux_status_open in -lselinux" >&5
+$as_echo_n "checking for selinux_status_open in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_selinux_status_open+set}" = set; then
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
@@ -9501,11 +9501,11 @@ cat >>conftest.$ac_ext <<_ACEOF
 #ifdef __cplusplus
 extern "C"
 #endif
-char selinux_sepgsql_context_path ();
+char selinux_status_open ();
 int
 main ()
 {
-return selinux_sepgsql_context_path ();
+return selinux_status_open ();
   ;
   return 0;
 }
@@ -9531,12 +9531,12 @@ $as_echo "$ac_try_echo") >&5
         test "$cross_compiling" = yes ||
         $as_test_x conftest$ac_exeext
        }; then
-  ac_cv_lib_selinux_selinux_sepgsql_context_path=yes
+  ac_cv_lib_selinux_selinux_status_open=yes
 else
   $as_echo "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
-       ac_cv_lib_selinux_selinux_sepgsql_context_path=no
+       ac_cv_lib_selinux_selinux_status_open=no
 fi
 
 rm -rf conftest.dSYM
@@ -9544,9 +9544,9 @@ 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
-{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_selinux_sepgsql_context_path" >&5
-$as_echo "$ac_cv_lib_selinux_selinux_sepgsql_context_path" >&6; }
-if test "x$ac_cv_lib_selinux_selinux_sepgsql_context_path" = x""yes; then
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_selinux_status_open" >&5
+$as_echo "$ac_cv_lib_selinux_selinux_status_open" >&6; }
+if test "x$ac_cv_lib_selinux_selinux_status_open" = x""yes; then
   cat >>confdefs.h <<_ACEOF
 #define HAVE_LIBSELINUX 1
 _ACEOF
@@ -9554,8 +9554,8 @@ _ACEOF
   LIBS="-lselinux $LIBS"
 
 else
-  { { $as_echo "$as_me:$LINENO: error: library 'libselinux', version 2.0.93 or newer, is required for SELinux support" >&5
-$as_echo "$as_me: error: library 'libselinux', version 2.0.93 or newer, is required for SELinux support" >&2;}
+  { { $as_echo "$as_me:$LINENO: error: library 'libselinux', version 2.0.99 or newer, is required for SELinux support" >&5
+$as_echo "$as_me: error: library 'libselinux', version 2.0.99 or newer, is required for SELinux support" >&2;}
    { (exit 1); exit 1; }; }
 fi
 
index 15a206b092f1f7947d0f576dd94233907a40bf20..05259cb86942bec684641716534f7fcb2ab87b9b 100644 (file)
@@ -964,8 +964,8 @@ fi
 
 # for contrib/sepgsql
 if test "$with_selinux" = yes; then
-  AC_CHECK_LIB(selinux, selinux_sepgsql_context_path, [],
-               [AC_MSG_ERROR([library 'libselinux', version 2.0.93 or newer, is required for SELinux support])])
+  AC_CHECK_LIB(selinux, selinux_status_open, [],
+               [AC_MSG_ERROR([library 'libselinux', version 2.0.99 or newer, is required for SELinux support])])
 fi
 
 # for contrib/uuid-ossp
index 248b1dd7f401c3b5972b0720210292e92d9bda06..c83b2e3cef80d826b83d41c37aa7d905a050037c 100644 (file)
@@ -1,7 +1,7 @@
 # contrib/sepgsql/Makefile
 
 MODULE_big = sepgsql
-OBJS = hooks.o selinux.o label.o dml.o \
+OBJS = hooks.o selinux.o uavc.o label.o dml.o \
        schema.o relation.o proc.o
 DATA_built = sepgsql.sql
 
index 22666b708e96912b2701cb895c21db1de07d1c86..319933747c835da25bff088b8e332f5179f668ca 100644 (file)
@@ -150,12 +150,11 @@ check_relation_privileges(Oid relOid,
                                                  uint32 required,
                                                  bool abort)
 {
-       char            relkind = get_rel_relkind(relOid);
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
+       ObjectAddress   object;
        char       *audit_name;
        Bitmapset  *columns;
        int                     index;
+       char            relkind = get_rel_relkind(relOid);
        bool            result = true;
 
        /*
@@ -184,45 +183,43 @@ check_relation_privileges(Oid relOid,
        /*
         * Check permissions on the relation
         */
-       tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
-       audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
+       object.classId = RelationRelationId;
+       object.objectId = relOid;
+       object.objectSubId = 0;
+       audit_name = getObjectDescription(&object);
        switch (relkind)
        {
                case RELKIND_RELATION:
-                       result = sepgsql_check_perms(scontext,
-                                                                                tcontext,
-                                                                                SEPG_CLASS_DB_TABLE,
-                                                                                required,
-                                                                                audit_name,
-                                                                                abort);
+                       result = sepgsql_avc_check_perms(&object,
+                                                                                        SEPG_CLASS_DB_TABLE,
+                                                                                        required,
+                                                                                        audit_name,
+                                                                                        abort);
                        break;
 
                case RELKIND_SEQUENCE:
                        Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
 
                        if (required & SEPG_DB_TABLE__SELECT)
-                               result = sepgsql_check_perms(scontext,
-                                                                                        tcontext,
-                                                                                        SEPG_CLASS_DB_SEQUENCE,
-                                                                                        SEPG_DB_SEQUENCE__GET_VALUE,
-                                                                                        audit_name,
-                                                                                        abort);
+                               result = sepgsql_avc_check_perms(&object,
+                                                                                                SEPG_CLASS_DB_SEQUENCE,
+                                                                                                SEPG_DB_SEQUENCE__GET_VALUE,
+                                                                                                audit_name,
+                                                                                                abort);
                        break;
 
                case RELKIND_VIEW:
-                       result = sepgsql_check_perms(scontext,
-                                                                                tcontext,
-                                                                                SEPG_CLASS_DB_VIEW,
-                                                                                SEPG_DB_VIEW__EXPAND,
-                                                                                audit_name,
-                                                                                abort);
+                       result = sepgsql_avc_check_perms(&object,
+                                                                                        SEPG_CLASS_DB_VIEW,
+                                                                                        SEPG_DB_VIEW__EXPAND,
+                                                                                        audit_name,
+                                                                                        abort);
                        break;
 
                default:
                        /* nothing to be checked */
                        break;
        }
-       pfree(tcontext);
        pfree(audit_name);
 
        /*
@@ -242,7 +239,6 @@ check_relation_privileges(Oid relOid,
        {
                AttrNumber      attnum;
                uint32          column_perms = 0;
-               ObjectAddress object;
 
                if (bms_is_member(index, selected))
                        column_perms |= SEPG_DB_COLUMN__SELECT;
@@ -258,20 +254,17 @@ check_relation_privileges(Oid relOid,
 
                /* obtain column's permission */
                attnum = index + FirstLowInvalidHeapAttributeNumber;
-               tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
 
                object.classId = RelationRelationId;
                object.objectId = relOid;
                object.objectSubId = attnum;
                audit_name = getObjectDescription(&object);
 
-               result = sepgsql_check_perms(scontext,
-                                                                        tcontext,
-                                                                        SEPG_CLASS_DB_COLUMN,
-                                                                        column_perms,
-                                                                        audit_name,
-                                                                        abort);
-               pfree(tcontext);
+               result = sepgsql_avc_check_perms(&object,
+                                                                                SEPG_CLASS_DB_COLUMN,
+                                                                                column_perms,
+                                                                                audit_name,
+                                                                                abort);
                pfree(audit_name);
 
                if (!result)
index 7797ccb199f088ed101b0c77b41d0b9f98a63d2d..ca6ce99808001cc74925f9033e466a103802fb0d 100644 (file)
@@ -184,9 +184,7 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort)
 static bool
 sepgsql_needs_fmgr_hook(Oid functionId)
 {
-       char       *old_label;
-       char       *new_label;
-       char       *function_label;
+       ObjectAddress   object;
 
        if (next_needs_fmgr_hook &&
                (*next_needs_fmgr_hook) (functionId))
@@ -198,14 +196,8 @@ sepgsql_needs_fmgr_hook(Oid functionId)
         * functions as trusted-procedure, if the security policy has a rule that
         * switches security label of the client on execution.
         */
-       old_label = sepgsql_get_client_label();
-       new_label = sepgsql_proc_get_domtrans(functionId);
-       if (strcmp(old_label, new_label) != 0)
-       {
-               pfree(new_label);
+       if (sepgsql_avc_trusted_proc(functionId) != NULL)
                return true;
-       }
-       pfree(new_label);
 
        /*
         * Even if not a trusted-procedure, this function should not be inlined
@@ -213,17 +205,15 @@ sepgsql_needs_fmgr_hook(Oid functionId)
         * that it shall be actually failed later because of same reason with
         * ACL_EXECUTE.
         */
-       function_label = sepgsql_get_label(ProcedureRelationId, functionId, 0);
-       if (sepgsql_check_perms(sepgsql_get_client_label(),
-                                                       function_label,
-                                                       SEPG_CLASS_DB_PROCEDURE,
-                                                       SEPG_DB_PROCEDURE__EXECUTE,
-                                                       NULL, false) != true)
-       {
-               pfree(function_label);
+       object.classId = ProcedureRelationId;
+       object.objectId = functionId;
+       object.objectSubId = 0;
+       if (!sepgsql_avc_check_perms(&object,
+                                                                SEPG_CLASS_DB_PROCEDURE,
+                                                                SEPG_DB_PROCEDURE__EXECUTE,
+                                                                SEPGSQL_AVC_NOAUDIT, false))
                return true;
-       }
-       pfree(function_label);
+
        return false;
 }
 
@@ -251,33 +241,31 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
                        if (!stack)
                        {
                                MemoryContext oldcxt;
-                               const char *cur_label = sepgsql_get_client_label();
 
                                oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
                                stack = palloc(sizeof(*stack));
                                stack->old_label = NULL;
-                               stack->new_label = sepgsql_proc_get_domtrans(flinfo->fn_oid);
+                               stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
                                stack->next_private = 0;
 
                                MemoryContextSwitchTo(oldcxt);
 
-                               if (strcmp(cur_label, stack->new_label) != 0)
-                               {
-                                       /*
-                                        * process:transition permission between old and new
-                                        * label, when user tries to switch security label of the
-                                        * client on execution of trusted procedure.
-                                        */
-                                       sepgsql_check_perms(cur_label, stack->new_label,
-                                                                               SEPG_CLASS_PROCESS,
-                                                                               SEPG_PROCESS__TRANSITION,
-                                                                               NULL, true);
-                               }
+                               /*
+                                * process:transition permission between old and new label,
+                                * when user tries to switch security label of the client
+                                * on execution of trusted procedure.
+                                */
+                               if (stack->new_label)
+                                       sepgsql_avc_check_perms_label(stack->new_label,
+                                                                                                 SEPG_CLASS_PROCESS,
+                                                                                                 SEPG_PROCESS__TRANSITION,
+                                                                                                 NULL, true);
 
                                *private = PointerGetDatum(stack);
                        }
                        Assert(!stack->old_label);
-                       stack->old_label = sepgsql_set_client_label(stack->new_label);
+                       if (stack->new_label)
+                               stack->old_label = sepgsql_set_client_label(stack->new_label);
 
                        if (next_fmgr_hook)
                                (*next_fmgr_hook) (event, flinfo, &stack->next_private);
@@ -290,7 +278,8 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
                        if (next_fmgr_hook)
                                (*next_fmgr_hook) (event, flinfo, &stack->next_private);
 
-                       sepgsql_set_client_label(stack->old_label);
+                       if (stack->old_label)
+                               sepgsql_set_client_label(stack->old_label);
                        stack->old_label = NULL;
                        break;
 
@@ -433,6 +422,9 @@ _PG_init(void)
                                 errmsg("SELinux: failed to get server security label: %m")));
        sepgsql_set_client_label(context);
 
+       /* Initialize userspace access vector cache */
+       sepgsql_avc_init();
+
        /* Security label provider hook */
        register_label_provider(SEPGSQL_LABEL_TAG,
                                                        sepgsql_object_relabel);
index 3b8bf23ba393cfe77bb956b2662c6698a9598aa3..9630d456896ad97aab15bb43acb5960051950b1f 100644 (file)
@@ -96,64 +96,30 @@ sepgsql_proc_post_create(Oid functionId)
 void
 sepgsql_proc_relabel(Oid functionId, const char *seclabel)
 {
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
-       char       *audit_name;
+       ObjectAddress   object;
+       char               *audit_name;
 
-       audit_name = getObjectDescriptionOids(ProcedureRelationId, functionId);
+       object.classId = ProcedureRelationId;
+       object.objectId = functionId;
+       object.objectSubId = 0;
+       audit_name = getObjectDescription(&object);
 
        /*
         * check db_procedure:{setattr relabelfrom} permission
         */
-       tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
-       sepgsql_check_perms(scontext,
-                                               tcontext,
-                                               SEPG_CLASS_DB_PROCEDURE,
-                                               SEPG_DB_PROCEDURE__SETATTR |
-                                               SEPG_DB_PROCEDURE__RELABELFROM,
-                                               audit_name,
-                                               true);
-       pfree(tcontext);
-
+       sepgsql_avc_check_perms(&object,
+                                                       SEPG_CLASS_DB_PROCEDURE,
+                                                       SEPG_DB_PROCEDURE__SETATTR |
+                                                       SEPG_DB_PROCEDURE__RELABELFROM,
+                                                       audit_name,
+                                                       true);
        /*
         * check db_procedure:{relabelto} permission
         */
-       sepgsql_check_perms(scontext,
-                                               seclabel,
-                                               SEPG_CLASS_DB_PROCEDURE,
-                                               SEPG_DB_PROCEDURE__RELABELTO,
-                                               audit_name,
-                                               true);
+       sepgsql_avc_check_perms_label(seclabel,
+                                                                 SEPG_CLASS_DB_PROCEDURE,
+                                                                 SEPG_DB_PROCEDURE__RELABELTO,
+                                                                 audit_name,
+                                                                 true);
        pfree(audit_name);
 }
-
-/*
- * sepgsql_proc_get_domtrans
- *
- * It computes security label of the client that shall be applied when
- * the current client invokes the supplied function.
- * This computed label is either same or different from the current one.
- * If security policy informed the function is a trusted-procedure,
- * we need to switch security label of the client during execution of
- * the function.
- *
- * Also note that the translated label shall be allocated using palloc().
- * So, need to switch memory context, if you want to hold the string in
- * someone except for CurrentMemoryContext.
- */
-char *
-sepgsql_proc_get_domtrans(Oid functionId)
-{
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
-       char       *ncontext;
-
-       tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
-
-       ncontext = sepgsql_compute_create(scontext,
-                                                                         tcontext,
-                                                                         SEPG_CLASS_PROCESS);
-       pfree(tcontext);
-
-       return ncontext;
-}
index 963cfdf9f10c7ba9845f7a1f3a0eb0538791b910..07673825e5d335e89773d590058cfdaa5c262c8a 100644 (file)
@@ -79,10 +79,8 @@ void
 sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
                                                  const char *seclabel)
 {
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
-       char       *audit_name;
        ObjectAddress object;
+       char             *audit_name;
 
        if (get_rel_relkind(relOid) != RELKIND_RELATION)
                ereport(ERROR,
@@ -97,26 +95,20 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
        /*
         * check db_column:{setattr relabelfrom} permission
         */
-       tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
-       sepgsql_check_perms(scontext,
-                                               tcontext,
-                                               SEPG_CLASS_DB_COLUMN,
-                                               SEPG_DB_COLUMN__SETATTR |
-                                               SEPG_DB_COLUMN__RELABELFROM,
-                                               audit_name,
-                                               true);
-
+       sepgsql_avc_check_perms(&object,
+                                                       SEPG_CLASS_DB_COLUMN,
+                                                       SEPG_DB_COLUMN__SETATTR |
+                                                       SEPG_DB_COLUMN__RELABELFROM,
+                                                       audit_name,
+                                                       true);
        /*
         * check db_column:{relabelto} permission
         */
-       sepgsql_check_perms(scontext,
-                                               seclabel,
-                                               SEPG_CLASS_DB_COLUMN,
-                                               SEPG_DB_PROCEDURE__RELABELTO,
-                                               audit_name,
-                                               true);
-
-       pfree(tcontext);
+       sepgsql_avc_check_perms_label(seclabel,
+                                                                 SEPG_CLASS_DB_COLUMN,
+                                                                 SEPG_DB_PROCEDURE__RELABELTO,
+                                                                 audit_name,
+                                                                 true);
        pfree(audit_name);
 }
 
@@ -227,8 +219,7 @@ out:
 void
 sepgsql_relation_relabel(Oid relOid, const char *seclabel)
 {
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
+       ObjectAddress   object;
        char       *audit_name;
        char            relkind;
        uint16_t        tclass = 0;
@@ -246,31 +237,27 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
                                 errmsg("cannot set security labels on relations except "
                                                "for tables, sequences or views")));
 
-       audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
+       object.classId = RelationRelationId;
+       object.objectId = relOid;
+       object.objectSubId = 0;
+       audit_name = getObjectDescription(&object);
 
        /*
         * check db_xxx:{setattr relabelfrom} permission
         */
-       tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
-
-       sepgsql_check_perms(scontext,
-                                               tcontext,
-                                               tclass,
-                                               SEPG_DB_TABLE__SETATTR |
-                                               SEPG_DB_TABLE__RELABELFROM,
-                                               audit_name,
-                                               true);
-
+       sepgsql_avc_check_perms(&object,
+                                                       tclass,
+                                                       SEPG_DB_TABLE__SETATTR |
+                                                       SEPG_DB_TABLE__RELABELFROM,
+                                                       audit_name,
+                                                       true);
        /*
         * check db_xxx:{relabelto} permission
         */
-       sepgsql_check_perms(scontext,
-                                               seclabel,
-                                               tclass,
-                                               SEPG_DB_TABLE__RELABELTO,
-                                               audit_name,
-                                               true);
-
-       pfree(tcontext);
+       sepgsql_avc_check_perms_label(seclabel,
+                                                                 tclass,
+                                                                 SEPG_DB_TABLE__RELABELTO,
+                                                                 audit_name,
+                                                                 true);
        pfree(audit_name);
 }
index 0de89971fbc15c879e7bbac765ebd26dea62460b..aae68ef964bc2867aee45e0ebd5fcf2bb212a4c2 100644 (file)
@@ -65,35 +65,30 @@ sepgsql_schema_post_create(Oid namespaceId)
 void
 sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
 {
-       char       *scontext = sepgsql_get_client_label();
-       char       *tcontext;
-       char       *audit_name;
+       ObjectAddress   object;
+       char               *audit_name;
 
-       audit_name = getObjectDescriptionOids(NamespaceRelationId, namespaceId);
+       object.classId = NamespaceRelationId;
+       object.objectId = namespaceId;
+       object.objectSubId = 0;
+       audit_name = getObjectDescription(&object);
 
        /*
         * check db_schema:{setattr relabelfrom} permission
         */
-       tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
-
-       sepgsql_check_perms(scontext,
-                                               tcontext,
-                                               SEPG_CLASS_DB_SCHEMA,
-                                               SEPG_DB_SCHEMA__SETATTR |
-                                               SEPG_DB_SCHEMA__RELABELFROM,
-                                               audit_name,
-                                               true);
-
+       sepgsql_avc_check_perms(&object,
+                                                       SEPG_CLASS_DB_SCHEMA,
+                                                       SEPG_DB_SCHEMA__SETATTR |
+                                                       SEPG_DB_SCHEMA__RELABELFROM,
+                                                       audit_name,
+                                                       true);
        /*
         * check db_schema:{relabelto} permission
         */
-       sepgsql_check_perms(scontext,
-                                               seclabel,
-                                               SEPG_CLASS_DB_SCHEMA,
-                                               SEPG_DB_SCHEMA__RELABELTO,
-                                               audit_name,
-                                               true);
-
-       pfree(tcontext);
+       sepgsql_avc_check_perms_label(seclabel,
+                                                                 SEPG_CLASS_DB_SCHEMA,
+                                                                 SEPG_DB_SCHEMA__RELABELTO,
+                                                                 audit_name,
+                                                                 true);
        pfree(audit_name);
 }
index 1f5a97e878ab1d67bd66a5a02500a6c1ecb3c989..d693d63ed0e844bab4daa362a1ea4b4036f91ac8 100644 (file)
@@ -642,7 +642,7 @@ bool
 sepgsql_getenforce(void)
 {
        if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
-               security_getenforce() > 0)
+               selinux_status_getenforce() > 0)
                return true;
 
        return false;
index 71688ab784f7fba46c882b5a3f701937eb6b7d1e..35b500c3ffa8a4d7b067e45cfebfd61ab05f373f 100644 (file)
@@ -15,6 +15,7 @@
 #include "fmgr.h"
 
 #include <selinux/selinux.h>
+#include <selinux/avc.h>
 
 /*
  * SE-PostgreSQL Label Tag
@@ -245,6 +246,22 @@ extern bool sepgsql_check_perms(const char *scontext,
                                        uint32 required,
                                        const char *audit_name,
                                        bool abort);
+/*
+ * uavc.c
+ */
+#define SEPGSQL_AVC_NOAUDIT                    ((void *)(-1))
+extern bool sepgsql_avc_check_perms_label(const char *tcontext,
+                                                                                 uint16 tclass,
+                                                                                 uint32 required,
+                                                                                 const char *audit_name,
+                                                                                 bool abort);
+extern bool sepgsql_avc_check_perms(const ObjectAddress *tobject,
+                                                                       uint16 tclass,
+                                                                       uint32 required,
+                                                                       const char *audit_name,
+                                                                       bool abort);
+extern char *sepgsql_avc_trusted_proc(Oid functionId);
+extern void sepgsql_avc_init(void);
 
 /*
  * label.c
@@ -286,6 +303,5 @@ extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
  */
 extern void sepgsql_proc_post_create(Oid functionId);
 extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
-extern char *sepgsql_proc_get_domtrans(Oid functionId);
 
 #endif   /* SEPGSQL_H */
diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c
new file mode 100644 (file)
index 0000000..bcf0d4c
--- /dev/null
@@ -0,0 +1,511 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/uavc.c
+ *
+ * Implementation of userspace access vector cache; that enables to cache
+ * access control decisions recently used, and reduce number of kernel
+ * invocations to avoid unnecessary performance hit.
+ *
+ * Copyright (c) 2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "catalog/pg_proc.h"
+#include "commands/seclabel.h"
+#include "storage/ipc.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+
+#include "sepgsql.h"
+
+/*
+ * avc_cache
+ *
+ * It enables to cache access control decision (and behavior on execution of
+ * trusted procedure, db_procedure class only) for a particular pair of
+ * security labels and object class in userspace.
+ */
+typedef struct
+{
+       uint32          hash;           /* hash value of this cache entry */
+       char       *scontext;   /* security context of the subject */
+       char       *tcontext;   /* security context of the target */
+       uint16          tclass;         /* object class of the target */
+
+       uint32          allowed;        /* permissions to be allowed */
+       uint32          auditallow;     /* permissions to be audited on allowed */
+       uint32          auditdeny;      /* permissions to be audited on denied */
+
+       bool            permissive;     /* true, if permissive rule */
+       bool            hot_cache;      /* true, if recently referenced */
+       bool            tcontext_is_valid;
+                                                       /* true, if tcontext is valid */
+       char       *ncontext;   /* temporary scontext on execution of trusted
+                                                        * procedure, or NULL elsewhere */
+} avc_cache;
+
+/*
+ * Declaration of static variables
+ */
+#define AVC_NUM_SLOTS          512
+#define AVC_NUM_RECLAIM                16
+#define AVC_DEF_THRESHOLD      384
+
+static MemoryContext   avc_mem_cxt;
+static List       *avc_slots[AVC_NUM_SLOTS];   /* avc's hash buckets */
+static int             avc_num_caches; /* number of caches currently used */
+static int             avc_lru_hint;   /* index of the buckets to be reclaimed next */
+static int             avc_threshold;  /* threshold to launch cache-reclaiming  */
+static char       *avc_unlabeled;      /* system 'unlabeled' label */
+
+/*
+ * Hash function
+ */
+static uint32
+sepgsql_avc_hash(const char *scontext, const char *tcontext, uint16 tclass)
+{
+       return hash_any((const unsigned char *)scontext, strlen(scontext))
+               ^ hash_any((const unsigned char *)tcontext, strlen(tcontext))
+               ^ tclass;
+}
+
+/*
+ * Reset all the avc caches
+ */
+static void
+sepgsql_avc_reset(void)
+{
+       MemoryContextReset(avc_mem_cxt);
+
+       memset(avc_slots, 0, sizeof(List *) * AVC_NUM_SLOTS);
+       avc_num_caches = 0;
+       avc_lru_hint = 0;
+       avc_unlabeled = NULL;
+}
+
+/*
+ * Reclaim caches recently unreferenced
+ */    
+static void
+sepgsql_avc_reclaim(void)
+{
+       ListCell   *cell;
+       ListCell   *next;
+       ListCell   *prev;
+       int                     index;
+
+       while (avc_num_caches >= avc_threshold - AVC_NUM_RECLAIM)
+       {
+               index = avc_lru_hint;
+
+               prev = NULL;
+               for (cell = list_head(avc_slots[index]); cell; cell = next)
+               {
+                       avc_cache  *cache = lfirst(cell);
+
+                       next = lnext(cell);
+                       if (!cache->hot_cache)
+                       {
+                               avc_slots[index]
+                                       = list_delete_cell(avc_slots[index], cell, prev);
+
+                               pfree(cache->scontext);
+                               pfree(cache->tcontext);
+                               if (cache->ncontext)
+                                       pfree(cache->ncontext);
+                               pfree(cache);
+
+                               avc_num_caches--;
+                       }
+                       else
+                       {
+                               cache->hot_cache = false;
+                               prev = cell;
+                       }
+               }
+               avc_lru_hint = (avc_lru_hint + 1) % AVC_NUM_SLOTS;
+       }
+}
+
+/*
+ * sepgsql_avc_check_valid
+ *
+ * It checks whether the cached entries are still valid, or not.
+ * If security policy has been reloaded since last reference of access
+ * vector cache, we have to release all the entries, because they are
+ * not valid yet.
+ */
+static bool
+sepgsql_avc_check_valid(void)
+{
+       if (selinux_status_updated() > 0)
+       {
+               sepgsql_avc_reset();
+
+               return false;
+       }
+       return true;
+}
+
+/*
+ * sepgsql_avc_unlabeled
+ *
+ * It returns an alternative label to be applied when no label or invalid 
+ * label would be assigned on objects.
+ */
+static char *
+sepgsql_avc_unlabeled(void)
+{
+       if (!avc_unlabeled)
+       {
+               security_context_t      unlabeled;
+
+               if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
+                       ereport(ERROR,
+                    (errcode(ERRCODE_INTERNAL_ERROR),
+                     errmsg("SELinux: failed to get initial security label: %m")));
+               PG_TRY();
+               {
+                       avc_unlabeled = MemoryContextStrdup(avc_mem_cxt, unlabeled);
+               }
+               PG_CATCH();
+               {
+                       freecon(unlabeled);
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
+
+               freecon(unlabeled);
+       }
+       return avc_unlabeled;
+}
+
+/*
+ * sepgsql_avc_compute 
+ *
+ * A fallback path, when cache mishit. It asks SELinux its access control
+ * decision for the supplied pair of security context and object class.
+ */
+static avc_cache *
+sepgsql_avc_compute(const char *scontext, const char *tcontext, uint16 tclass)
+{
+       char               *ucontext = NULL;
+       char               *ncontext = NULL;
+       MemoryContext   oldctx;
+       avc_cache          *cache;
+       uint32                  hash;
+       int                             index;
+       struct av_decision      avd;
+
+       hash = sepgsql_avc_hash(scontext, tcontext, tclass);
+       index = hash % AVC_NUM_SLOTS;
+
+       /*
+        * Validation check of the supplied security context.
+        * Because it always invoke system-call, frequent check should be avoided.
+        * Unless security policy is reloaded, validation status shall be kept, so
+        * we also cache whether the supplied security context was valid, or not.
+        */
+       if (security_check_context_raw((security_context_t)tcontext) != 0)
+               ucontext = sepgsql_avc_unlabeled();
+
+       /*
+        * Ask SELinux its access control decision
+        */
+       if (!ucontext)
+               sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
+       else
+               sepgsql_compute_avd(scontext, ucontext, tclass, &avd);
+
+       /*
+        * To boost up trusted procedure checks on db_procedure object
+        * class, we also confirm the decision when user calls a procedure
+        * labeled as 'tcontext'.
+        */
+       if (tclass == SEPG_CLASS_DB_PROCEDURE)
+       {
+               if (!ucontext)
+                       ncontext = sepgsql_compute_create(scontext, tcontext,
+                                                                                         SEPG_CLASS_PROCESS);
+               else
+                       ncontext = sepgsql_compute_create(scontext, ucontext,
+                                                                                         SEPG_CLASS_PROCESS);
+               if (strcmp(scontext, ncontext) == 0)
+               {
+                       pfree(ncontext);
+                       ncontext = NULL;
+               }
+       }
+
+       /*
+        * Set up an avc_cache object
+        */
+       oldctx = MemoryContextSwitchTo(avc_mem_cxt);
+
+       cache = palloc0(sizeof(avc_cache));
+
+       cache->hash     = hash;
+       cache->scontext = pstrdup(scontext);
+       cache->tcontext = pstrdup(tcontext);
+       cache->tclass = tclass;
+
+       cache->allowed = avd.allowed;
+       cache->auditallow = avd.auditallow;
+       cache->auditdeny = avd.auditdeny;
+       cache->hot_cache = true;
+       if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
+               cache->permissive = true;
+       if (!ucontext)
+               cache->tcontext_is_valid = true;
+       if (ncontext)
+               cache->ncontext = pstrdup(ncontext);
+
+       avc_num_caches++;
+
+       if (avc_num_caches > avc_threshold)
+               sepgsql_avc_reclaim();
+
+       avc_slots[index] = lcons(cache, avc_slots[index]);
+
+       MemoryContextSwitchTo(oldctx);
+
+       return cache;
+}
+
+/*
+ * sepgsql_avc_lookup
+ *
+ * It lookups a cache entry that matches with the supplied object
+ * identifiers and object class. If not found, it tries to create
+ * a new cache entry.
+ */
+static avc_cache *
+sepgsql_avc_lookup(const char *scontext, const char *tcontext, uint16 tclass)
+{
+       avc_cache  *cache;
+       ListCell   *cell;
+       uint32          hash;
+       int                     index;
+
+       hash = sepgsql_avc_hash(scontext, tcontext, tclass);
+       index = hash % AVC_NUM_SLOTS;
+
+       foreach (cell, avc_slots[index])
+       {
+               cache = lfirst(cell);
+
+               if (cache->hash == hash &&
+                       cache->tclass == tclass &&
+                       strcmp(cache->tcontext, tcontext) == 0 &&
+                       strcmp(cache->scontext, scontext) == 0)
+               {
+                       cache->hot_cache = true;
+                       return cache;
+               }
+       }
+       /* not found, so insert a new cache */
+       return sepgsql_avc_compute(scontext, tcontext, tclass);
+}
+
+/*
+ * sepgsql_avc_check_perms(_label)
+ *
+ * It returns 'true', if the security policy suggested to allow the required
+ * permissions. Otherwise, it returns 'false' or raises an error according
+ * to the 'abort' argument.
+ * The 'tobject' and 'tclass' identify the target object being referenced,
+ * and 'required' is a bitmask of permissions (SEPG_*__*) defined for each
+ * object classes.
+ * The 'audit_name' is the object name (optional). If SEPGSQL_AVC_NOAUDIT
+ * was supplied, it means to skip all the audit messages.
+ */
+bool
+sepgsql_avc_check_perms_label(const char *tcontext,
+                                                         uint16 tclass, uint32 required,
+                                                         const char *audit_name, bool abort)
+{
+       char *scontext = sepgsql_get_client_label();
+       avc_cache  *cache;
+       uint32          denied;
+       uint32          audited;
+       bool            result;
+
+       sepgsql_avc_check_valid();
+       do {
+               result = true;
+
+               /*
+                * If target object is unlabeled, we assume it has
+                * system 'unlabeled' security context instead.
+                */
+               if (tcontext)
+                       cache = sepgsql_avc_lookup(scontext, tcontext, tclass);
+               else
+                       cache = sepgsql_avc_lookup(scontext,
+                                                                          sepgsql_avc_unlabeled(), tclass);
+
+               denied = required & ~cache->allowed;
+
+               /*
+                * Compute permissions to be audited
+                */
+               if (sepgsql_get_debug_audit())
+                       audited = (denied ? (denied & ~0) : (required & ~0));
+               else
+                       audited = denied ? (denied & cache->auditdeny)
+                                                        : (required & cache->auditallow);
+
+               if (denied)
+               {
+                       /*
+                        * In permissive mode or permissive domain, violated permissions
+                        * shall be audited on the log files at once, and implicitly
+                        * allowed them to avoid flood of access denied logs, because
+                        * the purpose of permissive mode/domain is to collect violation
+                        * log to fix up security policy itself.
+                        */
+                       if (!sepgsql_getenforce() || cache->permissive)
+                               cache->allowed |= required;
+                       else
+                               result = false;
+               }
+       } while (!sepgsql_avc_check_valid());
+
+       /*
+        * In the case when we have something auditable actions here,
+        * sepgsql_audit_log shall be called with text representation of
+        * security labels for both of subject and object.
+        * It records this access violation, so DBA will be able to find
+        * out unexpected security problems later.
+        */
+       if (audited != 0 &&
+               audit_name != SEPGSQL_AVC_NOAUDIT &&
+               sepgsql_get_mode() != SEPGSQL_MODE_INTERNAL)
+       {
+               sepgsql_audit_log(!!denied,
+                                                 cache->scontext,
+                                                 cache->tcontext_is_valid ?
+                                                 cache->tcontext : sepgsql_avc_unlabeled(),
+                                                 cache->tclass,
+                                                 audited,
+                                                 audit_name);
+       }
+
+       if (abort && !result)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("SELinux: security policy violation")));
+
+       return result;
+}
+
+bool
+sepgsql_avc_check_perms(const ObjectAddress *tobject,
+                                               uint16 tclass, uint32 required,
+                                               const char *audit_name, bool abort)
+{
+       char   *tcontext = GetSecurityLabel(tobject, SEPGSQL_LABEL_TAG);
+       bool    rc;
+
+       rc = sepgsql_avc_check_perms_label(tcontext,
+                                                                          tclass, required,
+                                                                          audit_name, abort);
+       if (tcontext)
+               pfree(tcontext);
+
+       return rc;
+}
+
+/*
+ * sepgsql_avc_trusted_proc
+ *
+ * It returns a security label to be switched on execution of the supplied
+ * procedure, if it was configured as a trusted procedure. Otherwise, NULL
+ * shall be returned.
+ */
+char *
+sepgsql_avc_trusted_proc(Oid functionId)
+{
+       char               *scontext = sepgsql_get_client_label();
+       char               *tcontext;
+       ObjectAddress   tobject;
+       avc_cache          *cache;
+
+       tobject.classId = ProcedureRelationId;
+       tobject.objectId = functionId;
+       tobject.objectSubId = 0;
+       tcontext = GetSecurityLabel(&tobject, SEPGSQL_LABEL_TAG);
+
+       sepgsql_avc_check_valid();
+       do {
+               if (tcontext)
+                       cache = sepgsql_avc_lookup(scontext, tcontext,
+                                                                          SEPG_CLASS_DB_PROCEDURE);
+               else
+                       cache = sepgsql_avc_lookup(scontext, sepgsql_avc_unlabeled(),
+                                                                          SEPG_CLASS_DB_PROCEDURE);
+       } while (!sepgsql_avc_check_valid());
+
+       return cache->ncontext;
+}
+
+/*
+ * sepgsql_avc_exit
+ *
+ * It clean up userspace avc stuff on process exit
+ */
+static void
+sepgsql_avc_exit(int code, Datum arg)
+{
+       selinux_status_close();
+}
+
+/*
+ * sepgsql_avc_init
+ *
+ * It shall be invoked at once from _PG_init routine to initialize
+ * userspace access vector cache stuff.
+ */
+void
+sepgsql_avc_init(void)
+{
+       int     rc;
+
+       /*
+        * All the avc stuff shall be allocated on avc_mem_cxt
+        */
+       avc_mem_cxt = AllocSetContextCreate(TopMemoryContext,
+                                                                               "userspace access vector cache",
+                                                                               ALLOCSET_DEFAULT_MINSIZE,
+                                                                               ALLOCSET_DEFAULT_INITSIZE,
+                                                                               ALLOCSET_DEFAULT_MAXSIZE);
+       memset(avc_slots, 0, sizeof(avc_slots));
+       avc_num_caches = 0;
+       avc_lru_hint = 0;
+       avc_threshold = AVC_DEF_THRESHOLD;
+
+       /*
+        * SELinux allows to mmap(2) its kernel status page in read-only mode
+        * to inform userspace applications its status updating (such as
+        * policy reloading) without system-call invocations.
+        * This feature is only supported in Linux-2.6.38 or later, however,
+        * libselinux provides a fallback mode to know its status using
+        * netlink sockets.
+        */
+       rc = selinux_status_open(1);
+       if (rc < 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INTERNAL_ERROR),
+                                errmsg("SELinux: could not open selinux status : %m")));
+       else if (rc > 0)
+               ereport(LOG,
+                               (errmsg("SELinux: kernel status page uses fallback mode")));
+
+       /*
+        * To close selinux status page on process exit
+        */
+       on_proc_exit(sepgsql_avc_exit, 0);
+}
index fc379885d8b0389923984b17ef1b59b8fe743393..0a02edb624a490ff356ded38358eb9924bee06d2 100644 (file)
@@ -64,7 +64,7 @@
     or higher with <productname>SELinux</productname> enabled.  It is not
     available on any other platform, and must be explicitly enabled using
     <literal>--with-selinux</>.  You will also need <productname>libselinux</>
-    2.0.93 or higher and <productname>selinux-policy</> 3.9.13 or higher
+    2.0.99 or higher and <productname>selinux-policy</> 3.9.13 or higher
     (some distributions may backport the necessary rules into older policy
     versions).
   </para>
@@ -473,16 +473,6 @@ postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
   <title>Limitations</title>
 
   <variablelist>
-   <varlistentry>
-    <term>Userspace access vector cache</term>
-    <listitem>
-     <para>
-      <productname>sepgsql</> does not yet support an access vector cache.
-      This would likely improve performance.
-     </para>
-    </listitem>
-   </varlistentry>
-
    <varlistentry>
     <term>Data Definition Language (DDL) Permissions</term>
     <listitem>