security/safesetid/lsm.c

Source file repositories/reference/linux-study-clean/security/safesetid/lsm.c

File Facts

System
Linux kernel
Corpus path
security/safesetid/lsm.c
Extension
.c
Size
8875 bytes
Lines
294
Domain
Core OS
Bucket
Security And Isolation
Inferred role
Core OS: implementation source
Status
source implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

hash_for_each_possible(policy->rules, rule, next, __kuid_val(src.uid)) {
			if (!uid_eq(rule->src_id.uid, src.uid))
				continue;
			if (uid_eq(rule->dst_id.uid, dst.uid))
				return SIDPOL_ALLOWED;
			result = SIDPOL_CONSTRAINED;
		}
	} else if (policy->type == GID) {
		hash_for_each_possible(policy->rules, rule, next, __kgid_val(src.gid)) {
			if (!gid_eq(rule->src_id.gid, src.gid))
				continue;
			if (gid_eq(rule->dst_id.gid, dst.gid)){
				return SIDPOL_ALLOWED;
			}
			result = SIDPOL_CONSTRAINED;
		}
	} else {
		/* Should not reach here, report the ID as contrainsted */
		result = SIDPOL_CONSTRAINED;
	}
	return result;
}

/*
 * Compute a decision for a transition from @src to @dst under the active
 * policy.
 */
static enum sid_policy_type setid_policy_lookup(kid_t src, kid_t dst, enum setid_type new_type)
{
	enum sid_policy_type result = SIDPOL_DEFAULT;
	struct setid_ruleset *pol;

	rcu_read_lock();
	if (new_type == UID)
		pol = rcu_dereference(safesetid_setuid_rules);
	else if (new_type == GID)
		pol = rcu_dereference(safesetid_setgid_rules);
	else { /* Should not reach here */
		result = SIDPOL_CONSTRAINED;
		rcu_read_unlock();
		return result;
	}

	if (pol) {
		pol->type = new_type;
		result = _setid_policy_lookup(pol, src, dst);
	}
	rcu_read_unlock();
	return result;
}

static int safesetid_security_capable(const struct cred *cred,
				      struct user_namespace *ns,
				      int cap,
				      unsigned int opts)
{
	/* We're only interested in CAP_SETUID and CAP_SETGID. */
	if (cap != CAP_SETUID && cap != CAP_SETGID)
		return 0;

	/*
	 * If CAP_SET{U/G}ID is currently used for a setid or setgroups syscall, we
	 * want to let it go through here; the real security check happens later, in
	 * the task_fix_set{u/g}id or task_fix_setgroups hooks.
	 */
	if ((opts & CAP_OPT_INSETID) != 0)
		return 0;

	switch (cap) {
	case CAP_SETUID:
		/*
		* If no policy applies to this task, allow the use of CAP_SETUID for
		* other purposes.
		*/
		if (setid_policy_lookup((kid_t){.uid = cred->uid}, INVALID_ID, UID) == SIDPOL_DEFAULT)
			return 0;
		/*
		 * Reject use of CAP_SETUID for functionality other than calling
		 * set*uid() (e.g. setting up userns uid mappings).
		 */
		pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
			__kuid_val(cred->uid));
		return -EPERM;
	case CAP_SETGID:
		/*
		* If no policy applies to this task, allow the use of CAP_SETGID for
		* other purposes.
		*/
		if (setid_policy_lookup((kid_t){.gid = cred->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT)
			return 0;

Annotation

Implementation Notes