频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

2.2.5 内核审计系统的接口函数

作者: 倪继利 著 出处:电子工业出版社博文视点  2007-12-22 15:27    砖    好    评论   进入论坛
阅读提示:《Linux安全体系分析与编程》第二章分析了日志系统的记录机制,阐述了审计系统的原理,并说明了文件系统变化监视机制。本文主要介绍的是内核审计系统的接口函数。

2.2.5  内核审计系统的接口函数

在Linux内核需要输出审计信息时,它先调用函数audit_log_start创建缓冲区。接着,调用函数audit_log或audit_log_format写缓冲区写入审计信息,最后调用函数audit_log_end发送审计信息,并释放缓冲区。这三个函数分别说明如下:

1.函数audit_log_start

函数audit_log_start申请审计缓冲区,如果任务当前在系统调用中,系统调用被标识为可审计的,并在系统调用退出时,产生一条审计记录。

函数audit_log_start的参数ctx为审计上下文结构实例;参数gfp_mask为分配内存的类型,如:__GFP_WAIT表示可以等待和重调度;参数type为审计消息类型。如果缓存区申请成功,它返回审计缓冲区的指针,否则返回NULL表示错误。

函数audit_log_start申请审计缓冲区,当审计缓冲区链表的缓冲区个数超过上限时,当前进程需要等待用户空间的后台进程将审计消息写入log文件,直到缓冲区个数小于上限值为止。

函数audit_log_start在申请并初始化审计缓冲区后,给缓冲区加时间戳和审计记录序列号。函数audit_log_start列出如下(在linux26/kernel/audit.c中):

//声明等待队列头,用于等待审计消息被后台进程写入log文件
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
int type)
{
struct audit_buffer*ab= NULL;
struct timespect;
unsigned intserial;
int reserve;
unsigned long timeout_start = jiffies;   //开始的时间

if (!audit_initialized)//如果已初始化,就直接退出
return NULL;

if (unlikely(audit_filter_type(type)))
return NULL;

if (gfp_mask & __GFP_WAIT)
reserve = 0;
else
reserve = 5; /*允许调用者多出5个条目*/

 //当链表中审计缓冲区数超出上限时,进程等待auditd处理链表中缓冲区
while (audit_backlog_limit
&& skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) {
if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time
     && time_before(jiffies, timeout_start + audit_backlog_wait_time)) {

/* 等待后台进程auditd从队列中处理部分缓冲区 */
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_INTERRUPTIBLE);
                         //设置当前进程的状态为可中断等待状态
add_wait_queue(&audit_backlog_wait, &wait);
                //将当前进程加入等待队列

if (audit_backlog_limit &&
    skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
schedule_timeout(timeout_start + 
                         audit_backlog_wait_time - jiffies);//调度

__set_current_state(TASK_RUNNING); //设置当前进程为运行状态
remove_wait_queue(&audit_backlog_wait, &wait);
continue;
}
  //检查每秒发送的记录数不能超过上限,以防止受非法攻击
if (audit_rate_check())
printk(KERN_WARNING
       "audit: audit_backlog=%d > "
       "audit_backlog_limit=%d\n",
       skb_queue_len(&audit_skb_queue),
       audit_backlog_limit);
 
audit_log_lost("backlog limit exceeded");
audit_backlog_wait_time = audit_backlog_wait_overflow;
wake_up(&audit_backlog_wait);
return NULL;
}

ab = audit_buffer_alloc(ctx, gfp_mask, type); //申请审计缓冲区
if (!ab) {
audit_log_lost("out of memory in audit_log_start");
return NULL;
}
//得到当前时间存入t,计算审计记录的序列号,存入serial
audit_get_stamp(ab->ctx, &t, &serial);
     //将时间戳和序列号写入审计记录
audit_log_format(ab, "audit(%lu.%03lu:%u): ",
 t.tv_sec, t.tv_nsec/1000000, serial);
return ab;
}

函数audit_buffer_alloc申请审计缓冲区,先尝试从空闲链表上取下一个缓冲区,如果空闲链表中没有,就分配一个缓冲区。然后,填充netlink消息头。该函数列出如下:

static DEFINE_SPINLOCK(audit_freelist_lock);//定义自旋锁,用于锁住链表audit_freelist
static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
gfp_t gfp_mask, int type)
{
unsigned long flags;
struct audit_buffer *ab = NULL;
struct nlmsghdr *nlh;

  //从空闲链表中得到一个缓冲区
spin_lock_irqsave(&audit_freelist_lock, flags); //加锁
if (!list_empty(&audit_freelist)) {
ab = list_entry(audit_freelist.next,
struct audit_buffer, list);
list_del(&ab->list);
--audit_freelist_count;
}
spin_unlock_irqrestore(&audit_freelist_lock, flags); //释放锁

  //如果空闲链表中没有空闲缓冲区成员,就分配一个缓冲区
if (!ab) {
ab = kmalloc(sizeof(*ab), gfp_mask);
if (!ab)
goto err;
}

ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask);//分配套接字缓冲区
if (!ab->skb)
goto err;

ab->ctx = ctx;
ab->gfp_mask = gfp_mask;
  //扩展套接字缓冲区skb的已使用数据区,将netlink消息头数据nlmsghdr加到skb
nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); 
nlh->nlmsg_type = type;
nlh->nlmsg_flags = 0;
nlh->nlmsg_pid = 0;
nlh->nlmsg_seq = 0;
return ab;
err:
audit_buffer_free(ab);
return NULL;
}

2.函数audit_log_format

函数audit_log_format将一个审计消息按格式写入审计缓冲区,参数ab为审计缓冲区,参数fmt为格式化的字符串。其列出如下:

void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
{
va_list args;

if (!ab)
return;
va_start(args, fmt);
audit_log_vformat(ab, fmt, args);
va_end(args);
}

函数audit_log_vformat将一个审计消息按格式写入套接字缓冲区中,如果审计缓冲区没有足够的空间,就扩展套接字缓冲区的数据域。由于printk缓冲区为1024,扩展的套接字缓冲区最小应为1024。

函数audit_log_vformat列出如下:

static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, 
 va_list args)
{
int len, avail;
struct sk_buff *skb;
va_list args2;

if (!ab)
return;

BUG_ON(!ab->skb);
skb = ab->skb;
avail = skb_tailroom(skb);//计算套接字缓冲区的空闲数据空间
if (avail == 0) { //如果套接字缓冲区没有空闲数据空间,扩展空间
avail = audit_expand(ab, AUDIT_BUFSIZ); // AUDIT_BUFSIZ为1024,
if (!avail)
goto out;
}
va_copy(args2, args);
len = vsnprintf(skb->tail, avail, fmt, args); //将信息写入到缓冲区
if (len >= avail) { //如果实际信息长度比可用的缓冲区大,扩展空间
/* 由于printk缓冲区是1024,因此,扩展空间最少为1024 */
avail = audit_expand(ab,
max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
if (!avail)
goto out;
len = vsnprintf(skb->tail, avail, fmt, args2); 
                 //将审计信息写入到缓冲区
}
if (len > 0)
skb_put(skb, len); //将写入信息的数据缓冲区附加到skb上
out:
return;
}

函数audit_expand扩展在审计缓冲区中的套接字缓冲区,扩展成功,返回可用的空间大小,扩展失败返回0,表示没有空间。参数ab表示审计缓冲区的指针,参数extra表示加到套接字缓冲区skb尾部的缓冲区空间大小。

函数audit_expand列出如下:

static inline int audit_expand(struct audit_buffer *ab, int extra)
{
struct sk_buff *skb = ab->skb;
int ret = pskb_expand_head(skb, skb_headroom(skb), extra,
   ab->gfp_mask);
if (ret < 0) {
audit_log_lost("out of memory in audit_expand");
return 0;
}
return skb_tailroom(skb);//返回可用的缓冲区空间大小
}

3.函数audit_log_end

当进程完成了将审计记录写入审计缓冲区的操作时,它调用函数audit_log_end将套接字缓冲区中的审计记录数据发送给用户空间后台进程,由后台进程写入到log文件。如果审计后台进程存在,使用netlink机制传输数据,由审计后台将套接字缓冲区中的审计记录数据写入审计文件audit.log中;如果审计后台不存在,使用函数printk记录数据,然后由日志后台进程将数据写入到日志文件中。

当数据发送完成后,函数audit_log_end唤醒等待队列kauditd_wait。有些进程因为审计套接字缓冲区链表上的缓冲区数量超过上限而在队列kauditd_wait等待,当其他进程发送了数据时,应唤醒这些等待进程。

函数audit_log_end列出如下:

void audit_log_end(struct audit_buffer *ab)
{
if (!ab)
return;
if (!audit_rate_check()) { //检查审计系统的传输速度,
如果netlink机制传输速度超过上限,则返回错误
audit_log_lost("rate limit exceeded");
} else {
if (audit_pid) { //如果审计后台的进程ID存在,使用netlink机制传输数据
struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
skb_queue_tail(&audit_skb_queue, ab->skb);
ab->skb = NULL;
wake_up_interruptible(&kauditd_wait); //发送了数据,唤醒等待队列
} else { //使用printk记录数据
printk(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0));
}
}
audit_buffer_free(ab);
}

【责任编辑:董书 TEL:(010)68476606】

回书目   上一节   下一节
专题
系统应用日志分析管理
Linux——从菜鸟到高手
网络管理系统如何支撑ITSM
企业Web安全威胁在线评估系统
赛门铁克诺顿误杀Windows系统文件 导致百万系统崩溃
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:SQL Server数据库管理精品黄皮书
简介:书中文章经过精挑细选,便于用户能根据自己的实际工作和学习,快速在本书寻找到相关资料。内容涵盖了SQL Server的安装与升级、语句查询、数据备份和恢复、自动化任务、数据同步、数据字典、安全和预防、性能和优化、集群等各方面应用信息,以及DBA管理人员在数据库管理工作中
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
浏览器的战国时代
浏览器的战国时代
ARP攻击防范与解决方案
ARP攻击防范与解决方案
NAC安全访问控制
NAC安全访问控制
· NAC安全访问控制
· 网络布线测试仪器
· Windows Server 2008专..
· Windows远程桌面应用
· 网络故障排除宝典
· 运营商封堵ADSL共享 中..
· 解析35岁技术人的价值..
· 世纪枭雄比尔盖茨的王..
· 主流品牌防火墙配置
· ASP.NET开发教程
· 超级计算机TOP500专题
· Vista SP1对决XP SP3
· SQL Server 2008/2005..
· 程序员如何成长?
· C#技术开发指南
· 虚拟化技术还有点“虚”
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SOA 面向服务架构
SOA 面向服务架构
· SOA 面向服务架构
· SQL Server 2008/2005..
· Apache技术专题
· 三层交换技术专题
· SQL Server入门到精通
· Windows远程桌面应用
· C#技术开发指南
· Apache技术专题
· Windows集群服务应用
· C#技术开发指南
· 国际文档格式标准开战
· 路由器设置与口令恢复
· Linux 集群技术专题
· PHP开发应用手册
· SOA 面向服务架构
· 企业数据恢复指南
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SQL Server入门到精通
SQL Server入门到精通
· SQL Server入门到精通
· SQL Server 2008/2005..
· SOA 面向服务架构
· Apache技术专题
· C#技术开发指南
· 三层交换技术专题
· Apache技术专题
· C#技术开发指南
· Windows远程桌面应用
· 企业数据恢复指南
· Windows集群服务应用
· 路由器设置与口令恢复
· Linux 集群技术专题
· SOA 面向服务架构
· 了解统一威胁管理(UTM)..
· 反垃圾邮件技术应用