3.5.7 函数avc_has_perm分析
函数avc_has_perm从avc中查找决策信息,如果没找到,使用内核SELinux计算决策,并将决策信息相应条目插入到avc中,记录审核信息。决策的计算是通过读写/selinux/acess文件,调用内核SELinux接口函数完成的。
函数avc_has_perm列出如下(在libselinux/src/avc.c中):
int avc_has_perm(security_id_t ssid, security_id_t tsid,
security_class_t tclass, access_vector_t requested,
struct avc_entry_ref *aeref, void *auditdata)
{
struct av_decision avd = { 0, 0, 0, 0, 0 };
int rc;
//获得决策信息
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
//如果决策否决了操作,则将安全上下文、决策信息等写入log文件或输出
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
|
函数avc_has_perm_noaudit从avc中查询决策信息,如果没找到相应条目,就通过内核SELinux计算决策信息,然后,将决策信息的条目插入到avc中,并返回决策信息到avd中。
函数avc_has_perm_noaudit列出如下(在libselinux/src/avc.c中):
int avc_has_perm_noaudit(security_id_t ssid,
security_id_t tsid,
security_class_t tclass,
access_vector_t requested,
struct avc_entry_ref *aeref, struct av_decision *avd)
{
struct avc_entry *ae;
int rc = 0;
struct avc_entry entry;
access_vector_t denied;
struct avc_entry_ref ref;
//如果不使用线程,则每调用一次此函数,从内核SELinux中接收一次消息
if (!avc_using_threads) {
(void)avc_netlink_check_nb();
}
if (!aeref) {
avc_entry_ref_init(&ref);
aeref = &ref;
}
avc_get_lock(avc_lock);
avc_cache_stats_incr(entry_lookups);
ae = aeref->ae;
if (ae) { //查看ae是否被使用
if (ae->ssid == ssid &&
ae->tsid == tsid &&
ae->tclass == tclass &&
((ae->avd.decided & requested) == requested)) {
avc_cache_stats_incr(entry_hits);
ae->used = 1;
} else {
avc_cache_stats_incr(entry_discards);
ae = 0;
}
}
if (!ae) {
avc_cache_stats_incr(entry_misses);
rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
if (rc) {//从avc中没找到对应的条目
if ((ssid->refcnt <= 0) || (tsid->refcnt <= 0)) {
errno = EINVAL;
rc = -1;
goto out;
}
rc = security_compute_av(ssid->ctx, tsid->ctx, tclass,
requested, &entry.avd);
if (rc)
goto out;
//将条目插入到avc中
rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
if (rc)
goto out;
}
ae = aeref->ae;
}
if (avd)
memcpy(avd, &ae->avd, sizeof(*avd));
denied = requested & ~(ae->avd.allowed);
if ((!requested || denied) && avc_enforcing) {
errno = EACCES;
rc = -1;
}
out:
avc_release_lock(avc_lock);
return rc;
}
|
函数security_compute_av通过socket与setrans后台进程通信,将安全上下文转移到原始安全上下文格式,然后,通过内核SELinux计算决策,将决策信息返回到avd中。
函数security_compute_av列出如下(在libselinux/src/compute_av.c中):
int security_compute_av(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested, struct av_decision *avd)
{
int ret;
security_context_t rscon = scon;
security_context_t rtcon = tcon;
//将安全上下文通过setrans后台进程转换到原始安全上下文
if (selinux_trans_to_raw_context(scon, &rscon))
return -1;
if (selinux_trans_to_raw_context(tcon, &rtcon)) {
freecon(rscon);
return -1;
}
//通过内核SELinux计算决策
ret = security_compute_av_raw(rscon, rtcon, tclass, requested, avd);
freecon(rscon);
freecon(rtcon);
return ret;
}
|
函数security_compute_av_raw通过写文件/selinux/acess,调用内核SELinux的函数根据安全上下文对计算决策,然后,再通过读这个文件,读取决策信息。
函数security_compute_av_raw列出如下(在libselinux/src/compute_av.c中):
int security_compute_av_raw(security_context_t scon,
security_context_t tcon,
security_class_t tclass,
access_vector_t requested, struct av_decision *avd)
{
char path[PATH_MAX];
char *buf;
size_t len;
int fd, ret;
if (!selinux_mnt) {
errno = ENOENT;
return -1;
}
snprintf(path, sizeof path, "%s/access", selinux_mnt); //得到文件路径/selinux/acess
fd = open(path, O_RDWR); //打开文件
if (fd < 0)
return -1;
len = selinux_page_size; //系统的页大小
buf = malloc(len);
if (!buf) {
ret = -1;
goto out;
}
//将数据格式化写入buf
snprintf(buf, len, "%s %s %hu %x", scon, tcon, tclass, requested);
ret = write(fd, buf, strlen(buf)); //将buf写入文件
if (ret < 0)
goto out2;
memset(buf, 0, len);
ret = read(fd, buf, len - 1); //读出文件到buf
if (ret < 0)
goto out2;
//将文件中读出的数据进行格式转换
if (sscanf(buf, "%x %x %x %x %u", &avd->allowed,
&avd->decided, &avd->auditallow, &avd->auditdeny,
&avd->seqno) != 5) {
ret = -1;
goto out2;
}
ret = 0;
out2:
free(buf);
out:
close(fd);
return ret;
}
|
| 回书目 上一节 下一节 |