歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux服務器

LIDS精通與進階(二)

謝 謝 收 藏 http://www.qqread.com/linux/2006/06/y786123061.html
四、保護重要進程
  
  進程是操作系統的動態入口。內核裡有兩個特殊進程,進程ID 0 (swapd) 和進程ID 1(init)。Init進程是在系統啟動的時候所有進程的父進程。
  
  1、不可殺死的進程。
  


  就象你可以看到是否有人要奪得root特權一樣,我們可以很容易的殺死那些該內核發送特別信號的進程。為了殺死一個進程,你必須得到進程的ID,然後用kill命令來殺死它。
  
  系統殺死進程的調用是kill,是在內核裡的sys_kill()命令裡的調用。
  
  讓我們看看LIDS的保護代碼
  
  在/usr/src/linux/kernel/signal.c裡
  
  asmlinkage int
  
  sys_kill(int pid, int sig)
  
  {
  
  struct siginfo info;
  
  
  #ifdef CONFIG_LIDS_INIT_CHILDREN_LOCK pid_t this_pid;
  
  int i;
  
  #ifdef CONFIG_LIDS_ALLOW_KILL_INIT_CHILDREN
  
  if (!(current->flags & PF_KILLINITC))
  
  #endif
  
  if (lids_load && lids_local_load && LIDS_FISSET(lids_flags,LIDS_FLAGS_LOCK_INIT_CHILDREN)) {
  
  this_pid = pid>0?pid:-pid;
  
  for(i=0;i
  if( this_pid == lids_protected_pid[i]) {
  
  lids_security_alert("Try to kill pid=%d,sig=%dn",pid,sig);
  
  return -EPERM;
  
  }
  
  }
  
  }
  
  #endif
  
  ...
  
  }
  
  你可以在內核裡看到兩個標簽,,CONFIG_LIDS_INIT_CHILDREN_LOCK 和CONFIG_LIDS_ALLOW_KILL_INIT_CHILDREN.
  
  在CONFIG_LIDS_INIT_CHILDREN_LOCK的開啟狀態,LIDS能保護初使的運行程序。如,如果你在系統裡運行inetd程序,你可以在隱藏內核前運行它,然後,你還可以殺死它。但是一些人如果telnet到你的機器,inetd就會創造子進程來為用戶服務,這個子進程不會被LIDS保護,因為用戶在任何時候退出和殺死程序。
  
  2、隱藏進程
  
  另外一個保護進程的方法就是隱藏進程。當一個黑客危機你的系統。他會登陸,然後會看看有沒有一些已知的進程在監視它。然後他就殺死它。如果你隱藏了這個功能的進程,黑客就不會知道進程的所有情況並且你可以記錄他在你系統上做的任何事情。
  
  如何隱藏進程
  
  為了隱藏進程,你必須在配置內核的時候提供一個完全的路徑名。
  
  當內核啟動的時候,LIDS會訪問文件結點到一個叫proc_to_hide[]的結構裡。
  
  在include/linux/sched.h裡
  
  #ifdef CONFIG_LIDS_HIDE_PROC
  
  #define PF_HIDDEN 0x04000000 /* Hidden process */
  
  #endif
  
  /* in fs/lids.c */
  
  #ifdef CONFIG_LIDS_HIDE_PROC
  
  struct allowed_ino proc_to_hide[LIDS_MAX_ALLOWED];
  
  int last_hide=0;
  
  #endif
  
  ....
  
  /* in fs/lids.c , init_vfs_security(),
  
  fill up the hidden process in proc_to_hide[]
  
  */
  
  #ifdef CONFIG_LIDS_HIDE_PROC
  
  lids_fill_table(proc_to_hide,&last_hide,LIDS_MAX_ALLOWED,CONFIG_LIDS_HIDDEN_PROC_PATH);
  
  #endif
  
  PF_HIDDEN是否用戶可以用顯示進程的命令(如“ps –a”)來顯示和檢查進程,如果一個進程被LIDS隱藏,當他執行的時候,進程就會得到一個PF_HIDDEN的屬性。然後,當系統輸出系統進程信息到用戶的時候,它就會可以檢查當前輸出進程是否有PF_HIDDEN標志。如果發現了,它就不會輸出這個進程的信息。
  
  在in fs/exec.c
  
  int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
  
  {
  
  ...
  
  if (retval >= 0) {
  
  #ifdef CONFIG_LIDS_HIDE_PROC
  
  if (lids_search_proc_to_hide(dentry->d_inode))
  
  current->flags |= PF_HIDDEN;
  
  ...
  
  因為每一個linux的進程都有一個在/proc文件系統的入口,我們為了隱藏進程也需要修改proc的文件入口。
  
  在fs/proc/root.c
  
  static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
  
  {
  
  ...
  
  inode = NULL;
  
  #ifdef CONFIG_LIDS_HIDE_PROC
  
  if ( pid && p && (! ((p->flags & PF_HIDDEN) && lids_load && lids_local_load)) ) {
  
  #else
  
  if (pid && p) {
  
  #endif
  
  unsigned long ino = (pid >> 16) + PROC_PID_INO;
  
  inode = proc_get_inode(dir->i_sb, ino, &proc_pid);
  
  if (!inode)
  
  return ERR_PTR(-EINVAL);
  
  inode->i_flags|=S_IMMUTABLE;
  
  }
  
  ...
  
  }
  
  然後如果進程被PF_HIDDEN標記,它就不會在proc文件系統裡顯示。
  
  五、密封內核
  
  我們需要在系統啟動的時候做一些必要的操作,但是我們也需要在系統運行的時候保護它們。
  
  例如,我們需要象內核裡插入一些需要的模塊,但是我們不希望在系統運行的時候插入任何模塊,因為那樣會十分危險。如何解決這個問題呢?這裡就有一些密封的方法。我們可以在系統啟動的時候做我們任何想做的事情,然後我們就密封內核。然後,我們就不能做那些以前在沒有密封的時候可以做的事情。用密封的方法,我們可以用模塊來解決問題,我們可以在密封前向內核裡插入我們想要的模塊,在密封後我們就不可以在內核裡插入或是刪除任何模塊。
  
  1、用LIDS密封內核
  
  為了密封內核,我們可以用下面的LIDS命令
  
  #lidsadm –I -- -CAP_xxx….
  
  它們可以放到腳本裡讓系統啟動的時候就執行它。具體你們可以看我以前在linuxbyte和chinabyte發表的文章。LIDS是通過/proc/sys/lids/locks和內核通訊的。
  
  當你密封了內核,lidsadm是調用lidsadm.c的lids_init()的調用。
  
  #define LIDS_LOCKS "/proc/sys/lids/locks"
  
  ......
  
  void lids_init(int optind, int argc, char *argv[])
  
  {
  
  ......
  
  if ((fd=open(LIDS_LOCKS,O_RDWR)) == -1) {
  
  perror("open");
  
  exit_error (2, "can open " LIDS_LOCKS);
  
  }
  
  if (read(fd,&locks,sizeof(lids_locks_t))==-1) {
  
  perror("read");
  
  exit_error (2, "can read " LIDS_LOCKS);
  
  }
  
  lids_set_caps(optind,argc,argv,&locks);
  
  
  locks.magic1=LIDS_MAGIC_1;
  
  .........
  
  if (write(fd,&locks,sizeof(lids_locks_t))==-1) {
  
  perror("write");
  
  exit_error (2, "can write " LIDS_LOCKS);
  
  }
  
  .....
  
  }
  
  這個系統調用在LIDS_LOCKS生成新的變量loks,內核會通過lids_proc_locks_sysctl()命令來讀取它。Lids_proc_locks_sysctl也會從用戶區完全檢查並讀取它,然後改變密封的變量lids_first_time為0。
  
  讓我們看看lids_proc_locks_sysctl().這個函數會在用戶讀寫/proc/sys/lids/locks的時候調用。
  
  int lids_proc_locks_sysctl(ctl_table *table, int write, struct file *filp,
  
  void *buffer, size_t *lenp, int conv, int op)
  
  {
  
  ...........
  
  /* first: check the terminal and the program which access the sysctl */
  
  #ifndef CONFIG_LIDS_REMOTE_SWITCH
  
  if (current->tty && (current->tty->driver.type != 2) ) {
  
  lids_security_alert("Try to %s locks sysctl (unauthorized terminal)",
  
  write ? "write" : "read");
  
  return -EPERM;
  
  }
  
  #endif
  
  ........
  
  /* second: check wether it is not a timeout period after two many failed attempts */
  
  .......
  
  if (write) {
  
  /* Third : check what is submitted (size, magics, passwd) */
  
  if (*lenp != sizeof(lids_locks_t)) {
  
  lids_security_alert("Try to feed locks sysctl with garbage");
  
  return -EINVAL;
  
  }
  
  if (copy_from_user(&locks,buffer,sizeof(lids_locks_t)))
  
  return -EFAULT;
  
  .......
  
  if ((lids_first_time) && (!locks.passwd[0])) {
  
  .........
  
  number_failed=0;
  
  if (lids_process_flags(locks.flags)) {
  
  cap_bset=locks.cap_bset;
  
  lids_security_alert("Changed: cap_bset=0x%x lids_flags=0x%x",cap_t(cap_bset),lids_flags);
  
  }
  
  Change flag here ..--> lids_first_time=0;
  
  .....
  
  }
  
  上面的函數會在密封內核或是改變內核安全級別的時候工作。變量lids_first_time是一個表明當前密封狀態的的一個標志。當改變了需要的使能位,這個標志就會置1表明當前的狀態是“密封後“。
  
  密封內核有兩個任務,首先,改變使能位,然後,改變lids_first_time標志為1。在密封後,系統就不允許改變它們了,除非你用lidsadm和密碼。
  
  2、在密封前保護程序
  
  因為在密封前的狀態是危險的,我們必須知道在密封前那些運行的程序是LIDS來保護的。為什麼呢?因為密封後我們就不能改變它們了。如果文件沒有被保護,一些人就可以改變他們然後重新啟動,這些程序可能對系統非常危險。讓我們來看看在沒有密封前一個運行的非保護程序的代碼。
  
  int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
  
  {
  
  ..........
  
  #ifdef CONFIG_LIDS_SA_EXEC_UP
  
  if (lids_first_time && lids_load) {
  
  if (!lids_check_base(dentry,LIDS_READONLY))
  
  #ifdef CONFIG_LIDS_NO_EXEC_UP
  
  lids_security_alert("Try to exec unprotected program %s before sealing LIDS",filename);
  
  if (dentry)
  
  dput(dentry);
  
  return -EPERM;
  
  #else
  
  lids_security_alert("Execed unprotected program %s before sealing LIDS",filename);
  
  #endif
  
  }
  
  }
  
  #endif
  
  ......
  
  }
  
  你會看到當LIDS保護系統開啟(lids_load==1)和當前系統沒有密封(lids_firest_time 為1)的時候,內核就會檢查當前程序是否在LIDS的lids_check_base()保護下。如果沒有被保護,它就會啟動報警信息。
  
  六、LIDS與Capability
  
  1、Capability是一套來表明一個進程可以做為什麼的位標志。在LIDS,我們可以用capability的限制來限制所有的進程。
  
  在/include/linux/capability.h
  
  typedef struct __user_cap_header_struct {
  
  __u32 version;
  
  int pid;
  
  } *cap_user_header_t;
  
  typedef struct __user_cap_data_struct {
  
  __u32 effective;
  
  __u32 permitted;
  
  __u32 inheritable;
  
  } *cap_user_data_t;
  
  #ifdef __KERNEL__
  
  /* #define STRICT_CAP_T_TYPECHE
  
  #ifdef STRICT_CAP_T_TYPECHECKS
  
  typedef struct kernel_cap_struct {
  
  __u32 cap;
  
  } kernel_cap_t;
  
  #else
  
  typedef __u32 kernel_cap_t;
  
  #endif
  
  kernel_cap_t cap_bset = CAP_FULL_SET;
  
  在kernel_ap_t的每一位都代表一個許可。Cap_bset是capability集的主要部分。它們的值可以通過改變/proc/sys/kernel/cap-bound來改變。
  
  看看上面的文件,你就會發現一些問題。
  
  /* in include/linux/capability.h */
  
  /* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
  
  overrides the restriction of changing file ownership and group
  
  ownership. */
  
  #define CAP_CHOWN 0
  
  /* Override all DAC access, including ACL execute access if
  
  [_POSIX_ACL] is defined. Excluding DAC access covered by
  
  CAP_LINUX_IMMUTABLE. */
  
  #define CAP_DAC_OVERRIDE 1
  
  /* Overrides all DAC restrictions regarding read and search on files
  
  and directories, including ACL restrictions if [_POSIX_ACL] is
  
  defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. */
  
  #define CAP_DAC_READ_SEARCH 2
  
  .........
  
  每一個任務(進程)在結構task_struct定義了三個成員:cap_effective,cap_inheritable,cap_permitted.我們已經有了一個用來表明基本capability的變量cap_bset。它們會檢測這個系統並確定那種capability用來控制系統。
  
  在內核實現的大部分系統調用會調用函數capable() (在kernel/sched.c)。然後會調用cap_raised() (在/include/linux/capability.h)。如下:
  
  #ifdef CONFIG_LIDS_ALLO

W_SWITCH
  
  #define cap_raised(c, flag) ((cap_t(c) & CAP_TO_MASK(flag)) && ((CAP_TO_MASK(flag) & cap_bset) || (!lids_load) || (!lids_local_load)))
  
  #else
  
  #define cap_raised(c, flag) (cap_t(c) & CAP_TO_MASK(flag) & cap_bset)
  
  #endif
  
  你會看到這裡的cap_bset(一般默認都是1)是很重要的。如果有人在那裡把一些位置0,capability就可以會禁止整個系統。如,18 位的CAP_SYS_CHROOT, 如果我們把他置0,表明我們就不能用chroot()了。
  
  如果你看到sys_chroot的源代碼,你就發現很多問題了:
  
  if (!capable(CAP_SYS_CHROOT)) {
  
  goto dput_and_out;
  
  }
  
  capable()會返回0,在位18為0,這樣chroot就會給用戶返回一個錯誤信息。
  
  2、在LIDS裡的capability
  
  LIDS用capability來限制整個動作進程。LIDS用的函數是capable()。在內核代碼中已經存在的許多capable()裡。我們可以禁止一些當前系統默認的capability並且在用戶違反LIDS定義的規則的時候報警。
  
  至於管理員,他們也可以用lidsadm和密碼來改變capability。當內核授權用戶的時候,capability變量cap_bset 就會改變。
  
  作為管理員一個需要理解的重要東西是每一個capability的意思。然後,在密封內核的時候禁止capability,並用密碼來改變它們

Copyright © Linux教程網 All Rights Reserved