/* * 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: true, if caller wants to raise an error on access violation */ bool sepgsql_check_perms(const char *scontext, const char *tcontext, uint16 tclass, uint32 required, const char *audit_name, bool abort) { 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) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("SELinux: security policy violation"))); return result; }
/* * 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); /* * It also caches a security label to be switched when a client labeled as * 'scontext' executes a procedure labeled as 'tcontext', not only access * control decision on the procedure. The security label to be switched * shall be computed uniquely on a pair of 'scontext' and 'tcontext', * thus, it is reasonable to cache the new label on avc, and enables to * reduce unnecessary system calls. It shall be referenced at * sepgsql_needs_fmgr_hook to check whether the supplied function is a * trusted procedure, or not. */ if (tclass == SEPG_CLASS_DB_PROCEDURE) { if (!ucontext) ncontext = sepgsql_compute_create(scontext, tcontext, SEPG_CLASS_PROCESS, NULL); else ncontext = sepgsql_compute_create(scontext, ucontext, SEPG_CLASS_PROCESS, NULL); 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; }