5. we need to perform some necessary operations when the system starts to seal the kernel, but we also need to protect them when the system is running. For example, we need to insert some required modules in the kernel, but we do not want to insert any modules when the system is running, because it is very dangerous. Solution
5. sealed the kernel
We need to do some necessary operations when the system is started, but we also need to protect them when the system is running.
For example, we need to insert some required modules in the kernel, but we do not want to insert any modules when the system is running, because it is very dangerous. How can this problem be solved? Here are some sealing methods. We can do anything we want to do when the system starts, and then we will seal the kernel. Then, we can't do things that we can do before when there is no seal. With the method of sealing, we can use modules to solve the problem. we can insert the modules we want into the kernel before sealing, after being sealed, we cannot insert or delete any modules in the kernel.
5.1 use LIDS to seal the kernel
To seal the kernel, we can use the following LIDS command
# Lidsadm? I ---CAP_xxx ....
They can be put in the script so that the system can execute it at startup. For more information, see my previous articles in linuxbyte and chinabyte. LIDS communicates with the kernel through/proc/sys/lids/locks.
When you seal the kernel, lidsadm calls lids_init () of lidsadm. c.
# 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, "cant open" LIDS_LOCKS );
}
If (read (fd, & locks, sizeof (lids_locks_t) =-1 ){
Perror ("read ");
Exit_error (2, "cant 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, "cant write" LIDS_LOCKS );
}
.....
}
This system calls the loks variable generated in LIDS_LOCKS, and the kernel will read it through the lids_proc_locks_sysctl () command. Lids_proc_locks_sysctl will also fully check and read it from the user zone, and then change the sealed variable lids_first_time to 0.
Let's take a look at lids_proc_locks_sysctl (). This function will be called when users read/write/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 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;
.....
}
The above functions work when the kernel is sealed or the kernel security level is changed. The variable lids_first_time indicates the current sealing status. When the required enable bit is changed, this flag sets 1 to indicate that the current status is "sealed".
There are two tasks to seal the kernel. First, change the enable bit, and then change lids_first_time to 1. After they are sealed, the system will not allow them to be changed unless you use lidsadm and password.
Protection program before sealing
Because the status before sealing is dangerous, we must know that the programs running before sealing are protected by LIDS. Why? Since they are sealed, we cannot change them. If files are not protected, some people can change them and restart them, which may be very dangerous to the system. Let's take a look at the code that does not seal the previous unprotected program.
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
......
}
You will see that when the LIDS protection system is enabled (lids_load = 1) and the current system is not sealed (lids_firest_time is 1), the kernel will check whether the current program is in LIDS's lids_check_base () under protection. If it is not protected, an alarm is triggered.
6. LIDS and Capability
Capability is a set of symbols that indicate what a process can do. In LIDS, we can use capability to restrict all processes.
In/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 valid tive;
_ 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;
Each digit in kernel_ap_t represents a license. Cap_bset is the main part of the capability set. Their values can be changed by changing/proc/sys/kernel/cap-bound.
Look at the above files and you will find some problems.
/* 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
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
.........
Each task (process) defines three members in the structure task_struct: cap_effective, cap_inheritable, cap_permitted. we already have a variable cap_bset to indicate basic capability. They will detect the system and determine the capability to control the system.
Most system calls implemented in the kernel call the function capable () (in kernel/sched. c ). Then, cap_raised () (in/include/linux/capability. h) is called ). As follows:
# Ifdef CONFIG_LIDS_ALLOW_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
You will see that cap_bset (usually 1 by default) is very important. If someone places 0 in it, capability can disable the entire system. For example, if we set the 18-bit CAP_SYS_CHROOT value to 0, it means we cannot use chroot.
If you see the source code of sys_chroot, you will find many problems:
If (! Capable (CAP_SYS_CHROOT )){
Goto dput_and_out;
}
Capable () returns 0, and 18 is 0, so that chroot returns an error message to the user.
6. 2. capability in LIDS
LIDS uses capability to limit the overall motion