謝 謝 收 藏 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