Exploring Linux, the Kernel basics...!
Tags: cybersecurity, security, linux, kernel
Published: 2023-03-26 19:23:26

The purpose of writing is to analyze the weak spots we often overlook whilst developing programs. Therefore, the secure coding practices are used to proactively strengthen our defenses.

### Random Numbers
These generators re often found in cryptographic libraries as the encryption/decryption algos emphasize alot on randomness and often times the security of key immensely depends on this. These are very useful for debugging, as the series of random numbers repeats each time. Although for production application one must use psudo random number generators (PRNG), with some seed.

What we can do is to secure the seed to PRNG algo, we can use real values as input to rand() and feed the results to srand() to give us a unique value each time, hence ensuring the security in the random seed value returned form the 1st rand() function.

Linux kernel includes the tool **"/dev/random"**. Various factors like threads execution time, calibration data from hardware etc serve to make this RNG work remarkably. Whereas, in case of Windows we can use **BCryptGetRandom()** method.

### User privileges in applications
**SETUID bit - setting the User ID bit.......!**
In general, when the user is running an application the OS manages the permissions to that application according to the role of that user. If the application is a server, then responsibility lies with the application to manage connections and security to the current system.

Where possible try and avoid **setuid** and use **capabilities** instead. Some capabilities are critical, although even they are preferred to use rather than *setuid*. On contrary in windows, such commands don't exist. Microsoft recommends creating a role with custom policies and allow the role to interact with COM proxy and in-turn pipe it to COM DLL.

### Linux Kernel - Safe driver development (Windows practices are similar)
A kernel module is an object file loaded and run in kernel mode (and not simply as a **root** user). Driver can be for physical hardware or even a simulated device say like *memory* or to implement a *network protocol* . Our applications can be designed to interact directly with a drives or to interact through an existing subsystem.

1. application connects through a node in **/dev**
2. **permissions** on **node** are the last line of defense
3. **/udev** in linux, manages the permissions to the nodes mounted at **/dev**

The **kernel** manages one stack for each user mode **thread**. In making a *system call* or an *interrupt*, the kernel replaces the *user mode* stack with this *kernel mode* stack. This stack's size is limited unlike the OS stack. **ulimit** is a comand to check max limit of kernel stack in linux.

##### Best practices in Kernel Mode
* less no of local variables
* large local arrays/structures must not be used
* must not use recursion
As, 8-24KB sized stack is made for such practices. That too when this needs to be shared with interrupt handler or other parts in kernel. If stack pointer reaches end of stack chances are that the threads crash or the whole system becomes unstable.

##### Read, write and IoCtl
These are the different entry points for the OS to interact with the kernel program. Few are some operations associated with the lifecycle of device (driver program).
* Device_Create -> mem_allocation, initialization, etc.
* Device_Delete -> When the driver is unloaded form memory, this program is then terminated.
* IoCtl -> application from OS interacts through this function.
* Open -> device connects to the driver using this function.
* Read -> determine the size of read (buffer). And copy from kernel space to user space through, `copy_to_user`.
* Write -> determine the size of write (buffer). It then copies from user space to kernel space through, `copy_from_user`.
* Release -> called for cleanup in some cases before application is terminated.

###### Note: The size and address of user provided buffers must always be checked. Moreover, a driver must not access the user mode directly, rather through the `copy_to_user` and `copy_from_user`. And always share memory using the page size.

##### Managing Errors
if driver encounters error, the whole OS crashes therefore its correct functionality is crucial. Hence, validating all user level inputs are a must. And it must not display any log, address or information for invalid user input, as it can be targeted for buffer overflow attack.
On contrary in windows, the permissions can be set in the driver code as well as INF file. The two modes of buffer exchange are:-
* buffered mode -> where the buffers are automatically shared amongst kernel and user.
* direct mode -> where kernel mappings have to be explicitly defined. Not preffered because there is no copy_from and copy_to functions. Here, the driver must explicitly avoid reading beyond buffer space (overflow protection).