设计思路
1.模仿SMACK的编码风格,所以首先要精读一遍SMACK的代码,找到有用的部分。(虽然之前也读过并模仿过,但是复杂度不是一个等级)。
2.确定数据结构和hook函数。
3.初步实现部分功能。
4.调试bug,完善功能
SMACK的用户空间工具:smackload
chsmack:
显示或设置 Smack 扩展属性值
smackctl:
加载 Smack 访问规则
smackaccess:
报告带有一个标签的进程是否可以访问带有另一个标签的对象
Smack 使用扩展属性 (xattrs) 在文件系统对象上存储标签。属性存储在扩展属性安全名称空间中。只有拥有CAP_MAC_ADMIN的进程才能改变这些属性中的任何一个。
Smack 使用的扩展属性是:
SMACK64
用于做出访问控制决策。在几乎所有情况下,赋予新文件系统对象的标签将是创建它的进程的标签。
SMACK64EXEC
执行具有此属性集的程序文件的进程的 Smack 标签将使用此属性的值运行。
SMACK64MMAP
不允许文件被一个进程映射,该进程的 Smack 标签不允许对该属性中包含的标签的进程进行所有允许的访问。这是共享库的一个非常具体的用例。
SMACK64TRANSMUTE
只能有值“TRUE”。如果在目录中创建对象时该属性存在于目录中,并且允许对该目录进行写访问的 Smack 规则(下文详述)包括转换(“t”)模式,则该对象将获得目录的标签创建过程的标签。如果创建的对象是一个目录,则还设置 SMACK64TRANSMUTE 属性。
SMACK64IPIN
此属性仅适用于套接字的文件描述符。使用此属性中的 Smack 标签对传递到此套接字的数据包进行访问控制决策。
SMACK64IPOUT
此属性仅适用于套接字的文件描述符。使用此属性中的 Smack 标签对来自此套接字的数据包进行访问控制决策。
工作1 阅读源码,弄清楚如何通过伪文件系统管理rules_list
SMACK的安全标签
为task打上标签: 我们知道Smack利用了LSM机制,LSM是要改造内核对象,为其添加安全域,对于任务而言,它的结构体task_struct里有一个成员是cred型,而cred结构体中有一项是void *security,这正是task的安全域,它指向了task_smack这个结构体,这个结构体里的成员就是父进程和子进程的安全标签,smack_lsm.c中的代码static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp)是用来设置task_smack的父子进程smack标签,参数task指向父进程smack标签,forked指向子进程的smack标签,task_struct是以Linux内核链表形式组织起来,当设置完进程的安全标签后,该函数会初始化task_smack链表。
获取task的安全标签:在smack_lsm.c中,有一个很重要的宏–task_security(task),它的作用是获取进程的安全域,即void *security所指向的task_smack结构体,而在smack.h中有一个静态函数smk_of_current(),它用来获取当前进程的smack标签。
为inode打上标签:inode结构体中安全域是指向了inode_smack结构体,它不仅包含了文件对象的smack标签,而且包含了创建该inode的进程的smack标签,在smack_lsm.c中,钩子函数smack_inode_post_setxattr为inode打上标签,但是要注意只有以超级用户运行程序,才能在用户空间将文件打上Smack标签,因为像setxattr(设置安全标签)或者xattr这样的函数或命令都被安插了Smack的钩子smack_inode_setxattr,而此钩子会检查当前运行的程序有没有超级用户的权限。
获取inode标签:smk_fetch, smk_of_inode, smack_inode_getsecurity用来获取inode的smack标签。注意,Smack标签形式是键–值,键是XATTR_NAME_SMACK,XATTR_NAME_SMACKEXEC(进程标签),XATTR_NAME_SMACKIPIN(接收包的标签),XATTR_NAME_SMACKIPOUT(外发包的标签)它们在Linux的源码xattr.h中作为宏定义,而这里的值是Smack标签的值,即Smack的字符串,钩子函数一般是根据键(name)来获取值(value)。
伪文件系统部分源码解析
在SMACKfs.c中,完成了superblock的定义和操作实现,以最简单的“/smack/mapped"为例,修改“/smack/mapped"实际上就是对内存中的一个变量smack_cipso_mapped进行read和write,实现方式为
/**
* smk_read_mapped - read() for /smack/mapped
* @filp: file pointer, not actually used
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
static ssize_t smk_read_mapped(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
ssize_t rc;
if (*ppos != 0)
return 0;
sprintf(temp, "%d", smack_cipso_mapped);
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
return rc;
}
最后把这些操作放在file_operations结构体中:
static const struct file_operations smk_mapped_ops = {
.read = smk_read_mapped,
.write = smk_write_mapped,
.llseek = default_llseek,
};
smack通过调用simple_fill_super填充superblock:
static int smk_fill_super(struct super_block *sb, void *data, int silent)
{
int rc;
struct inode *root_inode;
static const struct tree_descr smack_files[] = {
[SMK_LOAD] = {
"load", &smk_load_ops, S_IRUGO|S_IWUSR},
[SMK_CIPSO] = {
"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR},
[SMK_DOI] = {
"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
[SMK_DIRECT] = {
"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
[SMK_AMBIENT] = {
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NET4ADDR] = {
"netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] = {
"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
[SMK_LOGGING] = {
"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
[SMK_LOAD_SELF] = {
"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
[SMK_ACCESSES] = {
"access", &smk_access_ops, S_IRUGO|S_IWUGO},
[SMK_MAPPED] = {
"mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR},
[SMK_LOAD2] = {
"load2", &smk_load2_ops, S_IRUGO|S_IWUSR},
[SMK_LOAD_SELF2] = {
"load-self2", &smk_load_self2_ops, S_IRUGO|S_IWUGO},
[SMK_ACCESS2] = {
"access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
[SMK_CIPSO2] = {
"cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
[SMK_REVOKE_SUBJ] = {
"revoke-subject", &smk_revoke_subj_ops,
S_IRUGO|S_IWUSR},
[SMK_CHANGE_RULE] = {
"change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR},
[SMK_SYSLOG] = {
"syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR},
[SMK_PTRACE] = {
"ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR},
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
[SMK_UNCONFINED] = {
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
#endif
#if IS_ENABLED(CONFIG_IPV6)
[SMK_NET6ADDR] = {
"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
#endif /* CONFIG_IPV6 */
[SMK_RELABEL_SELF] = {
"relabel-self", &smk_relabel_self_ops,
S_IRUGO|S_IWUGO},
/* last one */
{""}
};
rc = simple_fill_super(sb, SMACK_MAGIC, smack_files);
if (rc != 0) {
printk(KERN_ERR "%s failed %d while creating inodes\n",
__func__, rc);
return rc;
}
root_inode = d_inode(sb->s_root);
return 0;
}
smk_mount是对superblock进行的自定义mount操作。
/**
* smk_mount - get the smackfs superblock
* @fs_type: passed along without comment
* @flags: passed along without comment
* @dev_name: passed along without comment
* @data: passed along without comment
*
* Just passes everything along.
*
* Returns what the lower level code does.
*/
static struct dentry *smk_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_single(fs_type, flags, data, smk_fill_super);
}
然后定义superblock的file_system_type结构体,和挂载的位置。
file_system_type结构体代表Linux内核的各种文件系统,每一种文件系统必须要有自己的file_system_type结构,用于描述具体的文件系统的类型。
static struct file_system_type smk_fs_type = {
.name = "smackfs",
.mount = smk_mount,
.kill_sb = kill_litter_super,
};
每当一个文件系统被安装时,就会有一个vfsmount结构和mount被创建,mount代表该文件系统的一个安装实例。对于每一个mount的文件系统都有一个vfsmount结构来表示,smackfs安装时的vfsmount定义如下所示:
static struct vfsmount *smackfs_mount;
file_system_type,vfsmount,mount是伪文件系统实现中最重要的三个结构体。
Linux内核初始化时,执行init_smk_fs()函数登记smackfs,init_smk_fs()函数在各个伪文件系统的实现(如sockfs等)中大同小异,是非常八股文的写法,可以直接参考。
/**
* init_smk_fs - get the smackfs superblock
*
* register the smackfs
*
* Do not register smackfs if Smack wasn't enabled
* on boot. We can not put this method normally under the
* smack_init() code path since the security subsystem get
* initialized before the vfs caches.
*
* Returns true if we were not chosen on boot or if
* we were chosen and filesystem registration succeeded.
*/
static int __init init_smk_fs(void)
{
int err;
int rc;
if (smack_enabled == 0)
return 0;
err = smk_init_sysfs();
if (err)
printk(KERN_ERR "CBAC: sysfs mountpoint problem.\n");
err = register_filesystem(&smk_fs_type);
if (!err) {
smackfs_mount = kern_mount(&smk_fs_type);
if (IS_ERR(smackfs_mount)) {
printk(KERN_ERR "smackfs: could not mount!\n");
err = PTR_ERR(smackfs_mount);
smackfs_mount = NULL;
}
}
smk_cipso_doi();
smk_unlbl_ambient(NULL);
return err;
}
__initcall(init_smk_fs);
用户空间——smackload分析
该部分对应毕业设计中的上下文管理器部分,因为我是将上下文信息存储在伪文件系统中,而非安全策略。但是策略管理器亦可参考编码风格。
参考
[1] SMACK核心机制浅析https://www.twblogs.net/a/5e4f4104bd9eee101e84dc78/?lang=zh-cn
[2] 浅析Linux内核sockfs网络文件系统 https://zhuanlan.zhihu.com/p/462049620
[3] smackload工具测试实例 https://www.its203.com/article/kkxgx/52088638?2022-04-04