greenplumn selinux 源码

  • 2022-08-18
  • 浏览 (428)

greenplumn selinux 代码

文件路径:/contrib/sepgsql/selinux.c

/* -------------------------------------------------------------------------
 *
 * contrib/sepgsql/selinux.c
 *
 * Interactions between userspace and selinux in kernelspace,
 * using libselinux api.
 *
 * Copyright (c) 2010-2019, PostgreSQL Global Development Group
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"

#include "lib/stringinfo.h"

#include "sepgsql.h"

/*
 * selinux_catalog
 *
 * This mapping table enables to translate the name of object classes and
 * access vectors to/from their own codes.
 * When we ask SELinux whether the required privileges are allowed or not,
 * we use security_compute_av(3). It needs us to represent object classes
 * and access vectors using 'external' codes defined in the security policy.
 * It is determined in the runtime, not build time. So, it needs an internal
 * service to translate object class/access vectors which we want to check
 * into the code which kernel want to be given.
 */
static struct
{
	const char *class_name;
	uint16		class_code;
	struct
	{
		const char *av_name;
		uint32		av_code;
	}			av[32];
}			selinux_catalog[] =

{
	{
		"process", SEPG_CLASS_PROCESS,
		{
			{
				"transition", SEPG_PROCESS__TRANSITION
			},
			{
				"dyntransition", SEPG_PROCESS__DYNTRANSITION
			},
			{
				"setcurrent", SEPG_PROCESS__SETCURRENT
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"file", SEPG_CLASS_FILE,
		{
			{
				"read", SEPG_FILE__READ
			},
			{
				"write", SEPG_FILE__WRITE
			},
			{
				"create", SEPG_FILE__CREATE
			},
			{
				"getattr", SEPG_FILE__GETATTR
			},
			{
				"unlink", SEPG_FILE__UNLINK
			},
			{
				"rename", SEPG_FILE__RENAME
			},
			{
				"append", SEPG_FILE__APPEND
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"dir", SEPG_CLASS_DIR,
		{
			{
				"read", SEPG_DIR__READ
			},
			{
				"write", SEPG_DIR__WRITE
			},
			{
				"create", SEPG_DIR__CREATE
			},
			{
				"getattr", SEPG_DIR__GETATTR
			},
			{
				"unlink", SEPG_DIR__UNLINK
			},
			{
				"rename", SEPG_DIR__RENAME
			},
			{
				"search", SEPG_DIR__SEARCH
			},
			{
				"add_name", SEPG_DIR__ADD_NAME
			},
			{
				"remove_name", SEPG_DIR__REMOVE_NAME
			},
			{
				"rmdir", SEPG_DIR__RMDIR
			},
			{
				"reparent", SEPG_DIR__REPARENT
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"lnk_file", SEPG_CLASS_LNK_FILE,
		{
			{
				"read", SEPG_LNK_FILE__READ
			},
			{
				"write", SEPG_LNK_FILE__WRITE
			},
			{
				"create", SEPG_LNK_FILE__CREATE
			},
			{
				"getattr", SEPG_LNK_FILE__GETATTR
			},
			{
				"unlink", SEPG_LNK_FILE__UNLINK
			},
			{
				"rename", SEPG_LNK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"chr_file", SEPG_CLASS_CHR_FILE,
		{
			{
				"read", SEPG_CHR_FILE__READ
			},
			{
				"write", SEPG_CHR_FILE__WRITE
			},
			{
				"create", SEPG_CHR_FILE__CREATE
			},
			{
				"getattr", SEPG_CHR_FILE__GETATTR
			},
			{
				"unlink", SEPG_CHR_FILE__UNLINK
			},
			{
				"rename", SEPG_CHR_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"blk_file", SEPG_CLASS_BLK_FILE,
		{
			{
				"read", SEPG_BLK_FILE__READ
			},
			{
				"write", SEPG_BLK_FILE__WRITE
			},
			{
				"create", SEPG_BLK_FILE__CREATE
			},
			{
				"getattr", SEPG_BLK_FILE__GETATTR
			},
			{
				"unlink", SEPG_BLK_FILE__UNLINK
			},
			{
				"rename", SEPG_BLK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"sock_file", SEPG_CLASS_SOCK_FILE,
		{
			{
				"read", SEPG_SOCK_FILE__READ
			},
			{
				"write", SEPG_SOCK_FILE__WRITE
			},
			{
				"create", SEPG_SOCK_FILE__CREATE
			},
			{
				"getattr", SEPG_SOCK_FILE__GETATTR
			},
			{
				"unlink", SEPG_SOCK_FILE__UNLINK
			},
			{
				"rename", SEPG_SOCK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"fifo_file", SEPG_CLASS_FIFO_FILE,
		{
			{
				"read", SEPG_FIFO_FILE__READ
			},
			{
				"write", SEPG_FIFO_FILE__WRITE
			},
			{
				"create", SEPG_FIFO_FILE__CREATE
			},
			{
				"getattr", SEPG_FIFO_FILE__GETATTR
			},
			{
				"unlink", SEPG_FIFO_FILE__UNLINK
			},
			{
				"rename", SEPG_FIFO_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"db_database", SEPG_CLASS_DB_DATABASE,
		{
			{
				"create", SEPG_DB_DATABASE__CREATE
			},
			{
				"drop", SEPG_DB_DATABASE__DROP
			},
			{
				"getattr", SEPG_DB_DATABASE__GETATTR
			},
			{
				"setattr", SEPG_DB_DATABASE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_DATABASE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_DATABASE__RELABELTO
			},
			{
				"access", SEPG_DB_DATABASE__ACCESS
			},
			{
				"load_module", SEPG_DB_DATABASE__LOAD_MODULE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_schema", SEPG_CLASS_DB_SCHEMA,
		{
			{
				"create", SEPG_DB_SCHEMA__CREATE
			},
			{
				"drop", SEPG_DB_SCHEMA__DROP
			},
			{
				"getattr", SEPG_DB_SCHEMA__GETATTR
			},
			{
				"setattr", SEPG_DB_SCHEMA__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_SCHEMA__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_SCHEMA__RELABELTO
			},
			{
				"search", SEPG_DB_SCHEMA__SEARCH
			},
			{
				"add_name", SEPG_DB_SCHEMA__ADD_NAME
			},
			{
				"remove_name", SEPG_DB_SCHEMA__REMOVE_NAME
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_table", SEPG_CLASS_DB_TABLE,
		{
			{
				"create", SEPG_DB_TABLE__CREATE
			},
			{
				"drop", SEPG_DB_TABLE__DROP
			},
			{
				"getattr", SEPG_DB_TABLE__GETATTR
			},
			{
				"setattr", SEPG_DB_TABLE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_TABLE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_TABLE__RELABELTO
			},
			{
				"select", SEPG_DB_TABLE__SELECT
			},
			{
				"update", SEPG_DB_TABLE__UPDATE
			},
			{
				"insert", SEPG_DB_TABLE__INSERT
			},
			{
				"delete", SEPG_DB_TABLE__DELETE
			},
			{
				"lock", SEPG_DB_TABLE__LOCK
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_sequence", SEPG_CLASS_DB_SEQUENCE,
		{
			{
				"create", SEPG_DB_SEQUENCE__CREATE
			},
			{
				"drop", SEPG_DB_SEQUENCE__DROP
			},
			{
				"getattr", SEPG_DB_SEQUENCE__GETATTR
			},
			{
				"setattr", SEPG_DB_SEQUENCE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_SEQUENCE__RELABELTO
			},
			{
				"get_value", SEPG_DB_SEQUENCE__GET_VALUE
			},
			{
				"next_value", SEPG_DB_SEQUENCE__NEXT_VALUE
			},
			{
				"set_value", SEPG_DB_SEQUENCE__SET_VALUE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_procedure", SEPG_CLASS_DB_PROCEDURE,
		{
			{
				"create", SEPG_DB_PROCEDURE__CREATE
			},
			{
				"drop", SEPG_DB_PROCEDURE__DROP
			},
			{
				"getattr", SEPG_DB_PROCEDURE__GETATTR
			},
			{
				"setattr", SEPG_DB_PROCEDURE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_PROCEDURE__RELABELTO
			},
			{
				"execute", SEPG_DB_PROCEDURE__EXECUTE
			},
			{
				"entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT
			},
			{
				"install", SEPG_DB_PROCEDURE__INSTALL
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_column", SEPG_CLASS_DB_COLUMN,
		{
			{
				"create", SEPG_DB_COLUMN__CREATE
			},
			{
				"drop", SEPG_DB_COLUMN__DROP
			},
			{
				"getattr", SEPG_DB_COLUMN__GETATTR
			},
			{
				"setattr", SEPG_DB_COLUMN__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_COLUMN__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_COLUMN__RELABELTO
			},
			{
				"select", SEPG_DB_COLUMN__SELECT
			},
			{
				"update", SEPG_DB_COLUMN__UPDATE
			},
			{
				"insert", SEPG_DB_COLUMN__INSERT
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_tuple", SEPG_CLASS_DB_TUPLE,
		{
			{
				"relabelfrom", SEPG_DB_TUPLE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_TUPLE__RELABELTO
			},
			{
				"select", SEPG_DB_TUPLE__SELECT
			},
			{
				"update", SEPG_DB_TUPLE__UPDATE
			},
			{
				"insert", SEPG_DB_TUPLE__INSERT
			},
			{
				"delete", SEPG_DB_TUPLE__DELETE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_blob", SEPG_CLASS_DB_BLOB,
		{
			{
				"create", SEPG_DB_BLOB__CREATE
			},
			{
				"drop", SEPG_DB_BLOB__DROP
			},
			{
				"getattr", SEPG_DB_BLOB__GETATTR
			},
			{
				"setattr", SEPG_DB_BLOB__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_BLOB__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_BLOB__RELABELTO
			},
			{
				"read", SEPG_DB_BLOB__READ
			},
			{
				"write", SEPG_DB_BLOB__WRITE
			},
			{
				"import", SEPG_DB_BLOB__IMPORT
			},
			{
				"export", SEPG_DB_BLOB__EXPORT
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_language", SEPG_CLASS_DB_LANGUAGE,
		{
			{
				"create", SEPG_DB_LANGUAGE__CREATE
			},
			{
				"drop", SEPG_DB_LANGUAGE__DROP
			},
			{
				"getattr", SEPG_DB_LANGUAGE__GETATTR
			},
			{
				"setattr", SEPG_DB_LANGUAGE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_LANGUAGE__RELABELTO
			},
			{
				"implement", SEPG_DB_LANGUAGE__IMPLEMENT
			},
			{
				"execute", SEPG_DB_LANGUAGE__EXECUTE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_view", SEPG_CLASS_DB_VIEW,
		{
			{
				"create", SEPG_DB_VIEW__CREATE
			},
			{
				"drop", SEPG_DB_VIEW__DROP
			},
			{
				"getattr", SEPG_DB_VIEW__GETATTR
			},
			{
				"setattr", SEPG_DB_VIEW__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_VIEW__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_VIEW__RELABELTO
			},
			{
				"expand", SEPG_DB_VIEW__EXPAND
			},
			{
				NULL, 0UL
			},
		}
	},
};

/*
 * sepgsql_mode
 *
 * SEPGSQL_MODE_DISABLED: Disabled on runtime
 * SEPGSQL_MODE_DEFAULT: Same as system settings
 * SEPGSQL_MODE_PERMISSIVE: Always permissive mode
 * SEPGSQL_MODE_INTERNAL: Same as permissive, except for no audit logs
 */
static int	sepgsql_mode = SEPGSQL_MODE_INTERNAL;

/*
 * sepgsql_is_enabled
 */
bool
sepgsql_is_enabled(void)
{
	return (sepgsql_mode != SEPGSQL_MODE_DISABLED ? true : false);
}

/*
 * sepgsql_get_mode
 */
int
sepgsql_get_mode(void)
{
	return sepgsql_mode;
}

/*
 * sepgsql_set_mode
 */
int
sepgsql_set_mode(int new_mode)
{
	int			old_mode = sepgsql_mode;

	sepgsql_mode = new_mode;

	return old_mode;
}

/*
 * sepgsql_getenforce
 *
 * It returns whether the current working mode tries to enforce access
 * control decision, or not. It shall be enforced when sepgsql_mode is
 * SEPGSQL_MODE_DEFAULT and system is running in enforcing mode.
 */
bool
sepgsql_getenforce(void)
{
	if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
		selinux_status_getenforce() > 0)
		return true;

	return false;
}

/*
 * sepgsql_audit_log
 *
 * It generates a security audit record. In the default, it writes out
 * audit records into standard PG's logfile. It also allows to set up
 * external audit log receiver, such as auditd in Linux, using the
 * sepgsql_audit_hook.
 *
 * SELinux can control what should be audited and should not using
 * "auditdeny" and "auditallow" rules in the security policy. In the
 * default, all the access violations are audited, and all the access
 * allowed are not audited. But we can set up the security policy, so
 * we can have exceptions. So, it is necessary to follow the suggestion
 * come from the security policy. (av_decision.auditallow and auditdeny)
 *
 * Security audit is an important feature, because it enables us to check
 * what was happen if we have a security incident. In fact, ISO/IEC15408
 * defines several security functionalities for audit features.
 */
void
sepgsql_audit_log(bool denied,
				  const char *scontext,
				  const char *tcontext,
				  uint16 tclass,
				  uint32 audited,
				  const char *audit_name)
{
	StringInfoData buf;
	const char *class_name;
	const char *av_name;
	int			i;

	/* lookup name of the object class */
	Assert(tclass < SEPG_CLASS_MAX);
	class_name = selinux_catalog[tclass].class_name;

	/* lookup name of the permissions */
	initStringInfo(&buf);
	appendStringInfo(&buf, "%s {",
					 (denied ? "denied" : "allowed"));
	for (i = 0; selinux_catalog[tclass].av[i].av_name; i++)
	{
		if (audited & (1UL << i))
		{
			av_name = selinux_catalog[tclass].av[i].av_name;
			appendStringInfo(&buf, " %s", av_name);
		}
	}
	appendStringInfo(&buf, " }");

	/*
	 * Call external audit module, if loaded
	 */
	appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
					 scontext, tcontext, class_name);
	if (audit_name)
		appendStringInfo(&buf, " name=\"%s\"", audit_name);

	ereport(LOG, (errmsg("SELinux: %s", buf.data)));
}

/*
 * sepgsql_compute_avd
 *
 * It actually asks SELinux what permissions are allowed on a pair of
 * the security contexts and object class. It also returns what permissions
 * should be audited on access violation or allowed.
 * In most cases, subject's security context (scontext) is a client, and
 * target security context (tcontext) is a database object.
 *
 * The access control decision shall be set on the given av_decision.
 * The av_decision.allowed has a bitmask of SEPG_<class>__<perms>
 * to suggest a set of allowed actions in this object class.
 */
void
sepgsql_compute_avd(const char *scontext,
					const char *tcontext,
					uint16 tclass,
					struct av_decision *avd)
{
	const char *tclass_name;
	security_class_t tclass_ex;
	struct av_decision avd_ex;
	int			i,
				deny_unknown = security_deny_unknown();

	/* Get external code of the object class */
	Assert(tclass < SEPG_CLASS_MAX);
	Assert(tclass == selinux_catalog[tclass].class_code);

	tclass_name = selinux_catalog[tclass].class_name;
	tclass_ex = string_to_security_class(tclass_name);

	if (tclass_ex == 0)
	{
		/*
		 * If the current security policy does not support permissions
		 * corresponding to database objects, we fill up them with dummy data.
		 * If security_deny_unknown() returns positive value, undefined
		 * permissions should be denied. Otherwise, allowed
		 */
		avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
		avd->auditallow = 0U;
		avd->auditdeny = ~0U;
		avd->flags = 0;

		return;
	}

	/*
	 * Ask SELinux what is allowed set of permissions on a pair of the
	 * security contexts and the given object class.
	 */
	if (security_compute_av_flags_raw((security_context_t) scontext,
									  (security_context_t) tcontext,
									  tclass_ex, 0, &avd_ex) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute av_decision: "
						"scontext=%s tcontext=%s tclass=%s: %m",
						scontext, tcontext, tclass_name)));

	/*
	 * SELinux returns its access control decision as a set of permissions
	 * represented in external code which depends on run-time environment. So,
	 * we need to translate it to the internal representation before returning
	 * results for the caller.
	 */
	memset(avd, 0, sizeof(struct av_decision));

	for (i = 0; selinux_catalog[tclass].av[i].av_name; i++)
	{
		access_vector_t av_code_ex;
		const char *av_name = selinux_catalog[tclass].av[i].av_name;
		uint32		av_code = selinux_catalog[tclass].av[i].av_code;

		av_code_ex = string_to_av_perm(tclass_ex, av_name);
		if (av_code_ex == 0)
		{
			/* fill up undefined permissions */
			if (!deny_unknown)
				avd->allowed |= av_code;
			avd->auditdeny |= av_code;

			continue;
		}

		if (avd_ex.allowed & av_code_ex)
			avd->allowed |= av_code;
		if (avd_ex.auditallow & av_code_ex)
			avd->auditallow |= av_code;
		if (avd_ex.auditdeny & av_code_ex)
			avd->auditdeny |= av_code;
	}

	return;
}

/*
 * sepgsql_compute_create
 *
 * It returns a default security context to be assigned on a new database
 * object. SELinux compute it based on a combination of client, upper object
 * which owns the new object and object class.
 *
 * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create
 * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0),
 * SELinux looks-up its security policy. If it has a special rule on the
 * combination of these security contexts and object class (db_table),
 * it returns the security context suggested by the special rule.
 * Otherwise, it returns the security context of schema, as is.
 *
 * We expect the caller already applies sanity/validation checks on the
 * given security context.
 *
 * scontext: security context of the subject (mostly, peer process).
 * tcontext: security context of the upper database object.
 * tclass: class code (SEPG_CLASS_*) of the new object in creation
 */
char *
sepgsql_compute_create(const char *scontext,
					   const char *tcontext,
					   uint16 tclass,
					   const char *objname)
{
	security_context_t ncontext;
	security_class_t tclass_ex;
	const char *tclass_name;
	char	   *result;

	/* Get external code of the object class */
	Assert(tclass < SEPG_CLASS_MAX);

	tclass_name = selinux_catalog[tclass].class_name;
	tclass_ex = string_to_security_class(tclass_name);

	/*
	 * Ask SELinux what is the default context for the given object class on a
	 * pair of security contexts
	 */
	if (security_compute_create_name_raw((security_context_t) scontext,
										 (security_context_t) tcontext,
										 tclass_ex,
										 objname,
										 &ncontext) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute a new context: "
						"scontext=%s tcontext=%s tclass=%s: %m",
						scontext, tcontext, tclass_name)));

	/*
	 * libselinux returns malloc()'ed string, so we need to copy it on the
	 * palloc()'ed region.
	 */
	PG_TRY();
	{
		result = pstrdup(ncontext);
	}
	PG_CATCH();
	{
		freecon(ncontext);
		PG_RE_THROW();
	}
	PG_END_TRY();
	freecon(ncontext);

	return result;
}

/*
 * sepgsql_check_perms
 *
 * It makes access control decision without userspace caching mechanism.
 * If SELinux denied the required accesses on the pair of security labels,
 * it raises an error or returns false.
 *
 * scontext: security label of the subject (mostly, peer process)
 * tcontext: security label of the object being referenced
 * tclass: class code (SEPG_CLASS_*) of the object being referenced
 * required: a mask of required permissions (SEPG_<class>__<perm>)
 * audit_name: a human readable object name for audit logs, or NULL.
 * abort_on_violation: true, if error shall be raised on access violation
 */
bool
sepgsql_check_perms(const char *scontext,
					const char *tcontext,
					uint16 tclass,
					uint32 required,
					const char *audit_name,
					bool abort_on_violation)
{
	struct av_decision avd;
	uint32		denied;
	uint32		audited;
	bool		result = true;

	sepgsql_compute_avd(scontext, tcontext, tclass, &avd);

	denied = required & ~avd.allowed;

	if (sepgsql_get_debug_audit())
		audited = (denied ? denied : required);
	else
		audited = (denied ? (denied & avd.auditdeny)
				   : (required & avd.auditallow));

	if (denied &&
		sepgsql_getenforce() > 0 &&
		(avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0)
		result = false;

	/*
	 * It records a security audit for the request, if needed. But, when
	 * SE-PgSQL performs 'internal' mode, it needs to keep silent.
	 */
	if (audited && sepgsql_mode != SEPGSQL_MODE_INTERNAL)
	{
		sepgsql_audit_log(denied,
						  scontext,
						  tcontext,
						  tclass,
						  audited,
						  audit_name);
	}

	if (!result && abort_on_violation)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("SELinux: security policy violation")));
	return result;
}

相关信息

greenplumn 源码目录

相关文章

greenplumn database 源码

greenplumn dml 源码

greenplumn hooks 源码

greenplumn label 源码

greenplumn proc 源码

greenplumn relation 源码

greenplumn schema 源码

greenplumn sepgsql 源码

greenplumn uavc 源码

0  赞