Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1550653
  • 博文数量: 61
  • 博客积分: 472
  • 博客等级: 下士
  • 技术积分: 548
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-26 11:52
文章分类
文章存档

2018年(1)

2017年(2)

2016年(6)

2015年(3)

2014年(19)

2013年(10)

2012年(20)

分类: LINUX

2012-08-15 13:18:35

 1.2.7 与用户空间审计系统的netlink通信机制

  内核审计系统与用户空间的审计后台auditd、规则设置程序auditctl使用netlink机制进行通信。

  应用程序auditctl把用户的设置请求消息发送给内核审计系统,内核审计系统解析消息并进行相应操作,然后将操作的结果回传给应用程序auditctl。

  当netlink机制的接收套接字缓冲区数据准备好时,netlink机制调用函数audit_receive接收来自用户空间应用程序auditctl的消息。

  函数audit_receive接收auditctl的消息,并根据消息设置规则链表、过滤审计信息、设置内核审计系统状态等。在接收第一个消息时,它还启动内核审计系统后台线程kauditd,线程kauditd专门用于将审计信息发送至用户空间后台进程auditd。函数audit_receive的调用层次图如图2-4所示。

  错误!

  

 

  图2-4 函数audit_receive的调用层次图

  函数audit_receive列出如下:

  /*定义互斥锁,序列化从用户空间来的请求*/

  static DEFINE_MUTEX(audit_cmd_mutex);

  static void audit_receive(struct sock *sk, int length)

  {

  struct sk_buff *skb;

  unsigned int qlen;

  mutex_lock(&audit_cmd_mutex);

  //从接收队列中取出skb,逐个进行处理

  for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {

  skb = skb_dequeue(&sk->sk_receive_queue);

  audit_receive_skb(skb);

  kfree_skb(skb);

  }

  mutex_unlock(&audit_cmd_mutex);

  }

  函数audit_receive_skb从接收套接字缓冲skb取出消息,这些消息来自应用程序auditctl。它调用函数audit_receive_msg处理每一条消息,并应答确认信号。该函数列出如下:

  static void audit_receive_skb(struct sk_buff *skb)

  {

  interr;

  struct nlmsghdr*nlh;

  u32rlen;

  while (skb->len >= NLMSG_SPACE(0)) {

  nlh = (struct nlmsghdr *)skb->data;

  if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)

  return;

  rlen = NLMSG_ALIGN(nlh->nlmsg_len); //32位对齐的长度

  if (rlen > skb->len)

  rlen = skb->len;

  if ((err = audit_receive_msg(skb, nlh))) {

  netlink_ack(skb, nlh, err); //应答错误信号

  } else if (nlh->nlmsg_flags & NLM_F_ACK)

  netlink_ack(skb, nlh, 0);//应答无错误的确认信号

  skb_pull(skb, rlen);//从skb开始删除数据

  }

  }

函数audit_receive_msg处理每条接收的消息,当第一次接收到用户空间auditctl的消息时,它创建专门用来发送审计消息的线程kauditd,用来发送内核的审计消息。该函数列出如下:

  static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)

  {

  u32uid, pid, seq, sid;

  void*data;

  struct audit_status*status_get, status_set;

  interr;

  struct audit_buffer*ab;

  u16msg_type = nlh->nlmsg_type; //得到消息类型

  uid_tloginuid; /* 发送者注册uid */

  struct audit_sig_info *sig_data;

  char*ctx;

  u32len;

  //检查接收的消息类型是否正确

  err = audit_netlink_ok(skb, msg_type);

  if (err)

  return err;

  /* 创建名为kauditd的内核线程,用来将审计消息发送给审计后台auditd*/

  if (!kauditd_task)

  //创建线程并唤醒线程

  kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");

  if (IS_ERR(kauditd_task)) {

  err = PTR_ERR(kauditd_task);

  kauditd_task = NULL;

  return err;

  }

  pid = NETLINK_CREDS(skb)->pid;

  uid = NETLINK_CREDS(skb)->uid;

  loginuid = NETLINK_CB(skb).loginuid;

  sid = NETLINK_CB(skb).sid;

  seq = nlh->nlmsg_seq; //消息序列号

  data = NLMSG_DATA(nlh);

  switch (msg_type) { //根据用户空间的应用程序auditctl发来的消息类型进行处理

  case AUDIT_GET: //得到内核审计系统状态

  status_set.enabled = audit_enabled;

  status_set.failure = audit_failure;

  status_set.pid = audit_pid;

  status_set.rate_limit = audit_rate_limit;

  status_set.backlog_limit = audit_backlog_limit;

  status_set.lost = atomic_read(&audit_lost);

  status_set.backlog = skb_queue_len(&audit_skb_queue);

  //分配并填充套接字缓冲区,然后调用netlink机制函数netlink_unicast将应答发给auditctl

  audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0,

  &status_set, sizeof(status_set));

  break;

  case AUDIT_SET://开/关审计系统、设置状态

  ......

  break;

  case AUDIT_USER:

  case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG://过滤auditctl转发的消息

  case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:

  if (!audit_enabled && msg_type != AUDIT_USER_AVC)

  return 0;

  err = audit_filter_user(&NETLINK_CB(skb), msg_type);

  if (err == 1) { //如果auditctl转发的消息满足user规则链表上的规则,则返回转发的消息

  err = 0;

  ab = audit_log_start(NULL, GFP_KERNEL, msg_type);

  if (ab) {

  audit_log_format(ab,

  "user pid=%d uid=%u auid=%u",

  pid, uid, loginuid);//填充pid等

  if (sid) {

  if (selinux_sid_to_string(sid, &ctx, &len)) {

  audit_log_format(ab, " ssid=%u", sid);

  //填充安全ID

  } else

  audit_log_format(ab, " subj=%s", ctx);

  //填充安全上下文

  kfree(ctx);

  }

  audit_log_format(ab, " msg='%.1024s'", (char *)data);

  //data是auditctl送来的消息

  audit_set_pid(ab, pid);

  audit_log_end(ab);

  }

  }

  break;

  case AUDIT_ADD:

  case AUDIT_DEL:

  if (nlmsg_len(nlh) < sizeof(struct audit_rule))

  return -EINVAL;

  case AUDIT_LIST: //列出系统调用规则

  err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,

  uid, seq, data, nlmsg_len(nlh),

  loginuid, sid);

  break;

  case AUDIT_ADD_RULE://增加系统调用过滤规则

  case AUDIT_DEL_RULE:

  if (nlmsg_len(nlh) < sizeof(struct audit_rule_data))

  return -EINVAL;

  case AUDIT_LIST_RULES: //列出系统调用过滤规则

  err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,

  uid, seq, data, nlmsg_len(nlh),

  loginuid, sid);

  break;

  case AUDIT_SIGNAL_INFO: //得到发送者的信号信息

  ......

  break;

  default:

  err = -EINVAL;

  break;

  }

  return err < 0 ? err : 0;

  }

  线程函数kauditd_thread是一个独立线程的运行函数。它是内核后台线程,是一直在运行的工作线程。它从审计套接字缓冲区链表上取下缓冲区,通过netlink机制函数netlink_unicast将缓冲区发送给用户空间的审计后台。如果链表中没有数据,这个工作线程就进入睡眠等待状态。

  函数kauditd_thread列出如下:

  static int kauditd_thread(void *dummy)

  {

  struct sk_buff *skb;

  while (!kthread_should_stop()) {

  skb = skb_dequeue(&audit_skb_queue); //从审计套接字缓冲区链表上取下缓冲区skb

  wake_up(&audit_backlog_wait); //唤醒等待的进程

  if (skb) {

  if (audit_pid) { //如果用户空间审计后台存在,发送消息

  int err = netlink_unicast(audit_sock, skb, audit_pid, 0);

  if (err < 0) {

  BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */

  printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n",

  audit_pid);

  audit_pid = 0;

  }

  } else {

  printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));

  kfree_skb(skb);//释放skb

  }

  } else { //如果没有需要发送的套接字缓冲区,则将当前进程放入等待队列进行等待

  DECLARE_WAITQUEUE(wait, current);//初始化等待队列成员wait

  set_current_state(TASK_INTERRUPTIBLE); //将当前进程设置为可中断等待状态

  add_wait_queue(&kauditd_wait, &wait); //加入等待队列kauditd_wait

  if (!skb_queue_len(&audit_skb_queue)) { //如果队列长度为0,即没有成员,

  则进入等待状态

  try_to_freeze();

  schedule();//调度

  }

  __set_current_state(TASK_RUNNING);//将当前进程设置为正在运行状态

  remove_wait_queue(&kauditd_wait, &wait);

  //从等待队列kauditd_wait删除成员wait

  }

  }

  return 0;

  }

阅读(2840) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~