diff options
Diffstat (limited to 'contrib/sepgsql/label.c')
-rw-r--r-- | contrib/sepgsql/label.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c new file mode 100644 index 00000000000..bc28adfea55 --- /dev/null +++ b/contrib/sepgsql/label.c @@ -0,0 +1,477 @@ +/* ------------------------------------------------------------------------- + * + * contrib/sepgsql/label.c + * + * Routines to support SELinux labels (security context) + * + * Copyright (c) 2010-2011, PostgreSQL Global Development Group + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/genam.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "commands/dbcommands.h" +#include "commands/seclabel.h" +#include "libpq/libpq-be.h" +#include "miscadmin.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/tqual.h" + +#include "sepgsql.h" + +#include <selinux/label.h> + +/* + * client_label + * + * security label of the client process + */ +static char *client_label = NULL; + +char * +sepgsql_get_client_label(void) +{ + return client_label; +} + +char * +sepgsql_set_client_label(char *new_label) +{ + char *old_label = client_label; + + client_label = new_label; + + return old_label; +} + +/* + * sepgsql_get_label + * + * It returns a security context of the specified database object. + * If unlabeled or incorrectly labeled, the system "unlabeled" label + * shall be returned. + */ +char * +sepgsql_get_label(Oid classId, Oid objectId, int32 subId) +{ + ObjectAddress object; + char *label; + + object.classId = classId; + object.objectId = objectId; + object.objectSubId = subId; + + label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG); + if (!label || security_check_context_raw((security_context_t)label)) + { + security_context_t unlabeled; + + if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("selinux: unable to get initial security label"))); + PG_TRY(); + { + label = pstrdup(unlabeled); + } + PG_CATCH(); + { + freecon(unlabeled); + PG_RE_THROW(); + } + PG_END_TRY(); + + freecon(unlabeled); + } + return label; +} + +/* + * sepgsql_object_relabel + * + * An entrypoint of SECURITY LABEL statement + */ +void +sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel) +{ + /* + * validate format of the supplied security label, + * if it is security context of selinux. + */ + if (seclabel && + security_check_context_raw((security_context_t) seclabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("invalid security label: \"%s\"", seclabel))); + /* + * Do actual permission checks for each object classes + */ + switch (object->classId) + { + case NamespaceRelationId: + sepgsql_schema_relabel(object->objectId, seclabel); + break; + case RelationRelationId: + if (object->objectSubId == 0) + sepgsql_relation_relabel(object->objectId, + seclabel); + else + sepgsql_attribute_relabel(object->objectId, + object->objectSubId, + seclabel); + break; + case ProcedureRelationId: + sepgsql_proc_relabel(object->objectId, seclabel); + break; + + default: + elog(ERROR, "unsupported object type: %u", object->classId); + break; + } +} + +/* + * TEXT sepgsql_getcon(VOID) + * + * It returns the security label of the client. + */ +PG_FUNCTION_INFO_V1(sepgsql_getcon); +Datum +sepgsql_getcon(PG_FUNCTION_ARGS) +{ + char *client_label; + + if (!sepgsql_is_enabled()) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELinux: now disabled"))); + + client_label = sepgsql_get_client_label(); + + PG_RETURN_POINTER(cstring_to_text(client_label)); +} + +/* + * TEXT sepgsql_mcstrans_in(TEXT) + * + * It translate the given qualified MLS/MCS range into raw format + * when mcstrans daemon is working. + */ +PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in); +Datum +sepgsql_mcstrans_in(PG_FUNCTION_ARGS) +{ + text *label = PG_GETARG_TEXT_P(0); + char *raw_label; + char *result; + + if (!sepgsql_is_enabled()) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELinux: now disabled"))); + + if (selinux_trans_to_raw_context(text_to_cstring(label), + &raw_label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: internal error on mcstrans"))); + + PG_TRY(); + { + result = pstrdup(raw_label); + } + PG_CATCH(); + { + freecon(raw_label); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(raw_label); + + PG_RETURN_POINTER(cstring_to_text(result)); +} + +/* + * TEXT sepgsql_mcstrans_out(TEXT) + * + * It translate the given raw MLS/MCS range into qualified format + * when mcstrans daemon is working. + */ +PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out); +Datum +sepgsql_mcstrans_out(PG_FUNCTION_ARGS) +{ + text *label = PG_GETARG_TEXT_P(0); + char *qual_label; + char *result; + + if (!sepgsql_is_enabled()) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELinux: now disabled"))); + + if (selinux_raw_to_trans_context(text_to_cstring(label), + &qual_label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: internal error on mcstrans"))); + + PG_TRY(); + { + result = pstrdup(qual_label); + } + PG_CATCH(); + { + freecon(qual_label); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(qual_label); + + PG_RETURN_POINTER(cstring_to_text(result)); +} + +/* + * exec_object_restorecon + * + * This routine is a helper called by sepgsql_restorecon; it set up + * initial security labels of database objects within the supplied + * catalog OID. + */ +static void +exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId) +{ + Relation rel; + SysScanDesc sscan; + HeapTuple tuple; + char *database_name = get_database_name(MyDatabaseId); + char *namespace_name; + Oid namespace_id; + char *relation_name; + + /* + * Open the target catalog. We don't want to allow writable + * accesses by other session during initial labeling. + */ + rel = heap_open(catalogId, AccessShareLock); + + sscan = systable_beginscan(rel, InvalidOid, false, + SnapshotNow, 0, NULL); + while (HeapTupleIsValid(tuple = systable_getnext(sscan))) + { + Form_pg_namespace nspForm; + Form_pg_class relForm; + Form_pg_attribute attForm; + Form_pg_proc proForm; + char objname[NAMEDATALEN * 4 + 10]; + int objtype = 1234; + ObjectAddress object; + security_context_t context; + + /* + * The way to determine object name depends on object classes. + * So, any branches set up `objtype', `objname' and `object' here. + */ + switch (catalogId) + { + case NamespaceRelationId: + nspForm = (Form_pg_namespace) GETSTRUCT(tuple); + + objtype = SELABEL_DB_SCHEMA; + snprintf(objname, sizeof(objname), "%s.%s", + database_name, NameStr(nspForm->nspname)); + + object.classId = NamespaceRelationId; + object.objectId = HeapTupleGetOid(tuple); + object.objectSubId = 0; + break; + + case RelationRelationId: + relForm = (Form_pg_class) GETSTRUCT(tuple); + + if (relForm->relkind == RELKIND_RELATION) + objtype = SELABEL_DB_TABLE; + else if (relForm->relkind == RELKIND_SEQUENCE) + objtype = SELABEL_DB_SEQUENCE; + else if (relForm->relkind == RELKIND_VIEW) + objtype = SELABEL_DB_VIEW; + else + continue; /* no need to assign security label */ + + namespace_name = get_namespace_name(relForm->relnamespace); + snprintf(objname, sizeof(objname), "%s.%s.%s", + database_name, namespace_name, + NameStr(relForm->relname)); + pfree(namespace_name); + + object.classId = RelationRelationId; + object.objectId = HeapTupleGetOid(tuple); + object.objectSubId = 0; + break; + + case AttributeRelationId: + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + + if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION) + continue; /* no need to assign security label */ + + objtype = SELABEL_DB_COLUMN; + + namespace_id = get_rel_namespace(attForm->attrelid); + namespace_name = get_namespace_name(namespace_id); + relation_name = get_rel_name(attForm->attrelid); + snprintf(objname, sizeof(objname), "%s.%s.%s.%s", + database_name, namespace_name, + relation_name, NameStr(attForm->attname)); + pfree(relation_name); + pfree(namespace_name); + + object.classId = RelationRelationId; + object.objectId = attForm->attrelid; + object.objectSubId = attForm->attnum; + break; + + case ProcedureRelationId: + proForm = (Form_pg_proc) GETSTRUCT(tuple); + + objtype = SELABEL_DB_PROCEDURE; + + namespace_name = get_namespace_name(proForm->pronamespace); + snprintf(objname, sizeof(objname), "%s.%s.%s", + database_name, namespace_name, + NameStr(proForm->proname)); + pfree(namespace_name); + + object.classId = ProcedureRelationId; + object.objectId = HeapTupleGetOid(tuple); + object.objectSubId = 0; + break; + + default: + elog(ERROR, "Bug? %u is not supported to set initial labels", + catalogId); + break; + } + + if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0) + { + PG_TRY(); + { + /* + * Check SELinux permission to relabel the fetched object, + * then do the actual relabeling. + */ + sepgsql_object_relabel(&object, context); + + SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); + } + else if (errno == ENOENT) + ereport(WARNING, + (errmsg("no valid initial label on %s (type=%d), skipped", + objname, objtype))); + else + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("libselinux: internal error"))); + } + systable_endscan(sscan); + + heap_close(rel, NoLock); +} + +/* + * BOOL sepgsql_restorecon(TEXT specfile) + * + * This function tries to assign initial security labels on all the object + * within the current database, according to the system setting. + * It is typically invoked by sepgsql-install script just after initdb, to + * assign initial security labels. + * + * If @specfile is not NULL, it uses explicitly specified specfile, instead + * of the system default. + */ +PG_FUNCTION_INFO_V1(sepgsql_restorecon); +Datum +sepgsql_restorecon(PG_FUNCTION_ARGS) +{ + struct selabel_handle *sehnd; + struct selinux_opt seopts; + + /* + * SELinux has to be enabled on the running platform. + */ + if (!sepgsql_is_enabled()) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SELinux: now disabled"))); + /* + * Check DAC permission. Only superuser can set up initial + * security labels, like root-user in filesystems + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to restore initial contexts"))); + + /* + * Open selabel_lookup(3) stuff. It provides a set of mapping + * between an initial security label and object class/name due + * to the system setting. + */ + if (PG_ARGISNULL(0)) + { + seopts.type = SELABEL_OPT_UNUSED; + seopts.value = NULL; + } + else + { + seopts.type = SELABEL_OPT_PATH; + seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0)); + } + sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1); + if (!sehnd) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux internal error"))); + PG_TRY(); + { + /* + * Right now, we have no support labeling on the shared + * database objects, such as database, role, or tablespace. + */ + exec_object_restorecon(sehnd, NamespaceRelationId); + exec_object_restorecon(sehnd, RelationRelationId); + exec_object_restorecon(sehnd, AttributeRelationId); + exec_object_restorecon(sehnd, ProcedureRelationId); + } + PG_CATCH(); + { + selabel_close(sehnd); + PG_RE_THROW(); + } + PG_END_TRY(); + + selabel_close(sehnd); + + PG_RETURN_BOOL(true); +} |