This project is due on Sunday, May 21, at 11:59:59 PM (Eastern daylight time). You must use the submit to turn in your assignment like so: submit cs421_jtang proj2 pwkeeper.c pwkeeper-test.c
Your driver code must be named pwkeeper.c, and it will be compiled against a 4.9 Linux kernel source tree, via the Kbuild supplied below. It must not have any compilation warnings; warnings will result in grading penalties. This module code must be properly indented and have a file header comment, as described on the coding conventions page. Prior to submission, use the kernel's indentation script to reformat your code, like so:
~/linux/scripts/Lindent pwkeeper.c
In addition, you will write a unit test program, pwkeeper-test.c, and it will be compiled on Ubuntu 16.04 as follow:
gcc ‐‐std=c99 ‐Wall ‐O2 ‐pthread ‐o pwkeeper-test pwkeeper-test.c cs421net.c ‐lmThere must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, this code must also have a file header comment and be properly indented. You will submit this test code along with your driver code.
Within the past few years, numerous Internet providers have had their user accounts breached. The best recourse, other than to live as an anti-Internet hermit, is to use different passwords for every website. Doing so is impractical; a 2016 survey found that the average person has 27 different online accounts. One solution is a password manager, a tool that stores and generates passwords. In this project, you will create your own password manager as a Linux driver. If implemented correctly, your password system may be more secure than the professionals!
All instructions henceforth assume you successfully completed the first project. If you have not done so, go back and finish that assignment before proceeding. You have been warned.
To begin, create a directory for your project and download the following files into that directory via wget:
Now run make to compile everything. Upon success, you should now have the kernel driver pwkeeper.ko and user space program pwkeeper-test. Run proj2_start.sh; it may ask for your password, as that it runs some commands under sudo. This will set up your VM for this project. Rerun this script if you ever need to reboot your VM.
The first task is to allow users to register their own master
passwords within the system. In pwkeeper.c
, read the
comments for pwkeeper_master_write()
and pwkeeper_remove()
. At the top of the file, declare
a kernel linked
list. Each element of the list holds two entries: a user ID and
that user's master password. Because this master password list can
be accessed by multiple threads, guard accesses to the list using a
spin lock, specifically using the irqsave
form.
Next, modify pwkeeper_master_write()
. If the current
user has already set a master password, replace it with the incoming
value. Otherwise, allocate a new list entry for that user and set
his or her password. Add that entry to the master password list with
the list_add_tail()
macro. Then
in pwkeeper_remove()
, free all memory that was
allocated for this master password list.
Master passwords are limited to 32 bytes. They are not limited to printable ASCII characters, nor are they necessarily C-style strings. If the user does not specify all 32 bytes, or replaces the password with one that is shorter, pad the remaining bytes with the null character.
Your kernel driver will need to obtain the user ID (UID) of the
requesting process. Be aware
of various blog
posts describing the current_uid()
call. Kernel
UIDs have changed; many older blog posts are inaccurate. In modern
kernels, UIDs are no longer scalars but rather
are struct
types. See
the
kernel code itself.
Test that your code correctly allows different users to set their own master passwords. Use the adduser command to create new user accounts. Then use su to log in as another user.
The next task is to allow users to add accounts and auto-generate
passwords for those accounts. In pwkeeper.c
, read the
comments
for pwkeeper_account_read()
, pwkeeper_account_write()
and pwkeeper_remove()
. At the top of the file, declare
another kernel linked list. Each element of this second list holds
three entries: a user ID, an account name, and then the password for
that account. Because this accounts list can be accessed by multiple
threads, guard accesses to this list using a spin lock, specifically
using the irqsave
form.
Next, modify pwkeeper_account_write()
. If the current
user has not created the account, allocate a new list entry
containing the user ID, account name, and soon-to-be generated
password. Add that entry to the accounts linked list. If the given
account was already added, do nothing. Then
in pwkeeper_remove()
, free all memory that was
allocated for this accounts list.
Account names are limited to 16 bytes. They are not limited to printable ASCII characters, nor are they necessarily C-style strings. If the user does not specify all 16 bytes, pad the remaining bytes with the null character.
When an account is added, your driver will automatically generate a password for that account. Implement the following key derivation function to generate the password:
Now that your driver can create account passwords, implement the
code for pwkeeper_account_read()
. Return to the user
the password associated with the most recently written account name.
If all of the above works, compile and insert your module. Test it like so:
$ echo -n '01234567890123456789012345678901' > /dev/pwkeeper_master $ echo -n 'abcdEFGHijklMNOP' > /dev/pwkeeper_account $ cat /dev/pwkeeper_account && echo UMWd8^TH\]RkTXLR
Now that you have created a system that stores account names and passwords, you will simulate a network daemon in your driver. Read the code in the file xt_cs421net.c. When this Netfilter module is installed, via the proj2_start.sh script, it will raise an interrupt every time your computer receives network packets on TCP port 4210. It is your driver's responsibility to handle those interrupts and treat the packets as if a user were attempting to log in to the system.
In pwkeeper_probe()
, install a threaded interrupt handler
for interrupt number CS421NET_IRQ
. Remove that handler
in
pwkeeper_remove()
. Then
implement cs421net_top()
and cs421net_bottom()
. The network payload for a login
attempt must be at least 32 bytes, where the first 16 bytes contain
the account name and next 16 bytes the password. Ignore everything
else in the incoming data buffer. If there exists an entry in the
accounts list that matches the account name and password (regardless
of the UID associated with that account name), then increment a
global successful login counter. If the account name does not exist
within the accounts list, or if the password associated with the
account name does not match, then increment a global failed login
counter. If the incoming payload was less than 32 bytes (including
payloads consisting solely of blank lines), then increment a global
invalid attempt counter.
As that cs421net_bottom()
will be accessing your
account linked list, be sure to guard the code with a spin
lock. (Because the bottom half is not running in interrupt context,
you do not need to use spin_lock_irqsave()
).
Once you are confident your ISR works,
call cs421net_enable()
in pwkeeper_probe()
, and likewise disable network
integration via babblenet_disable()
in pwkeeper_remove()
. Install your module, and
check /proc/interrupts to ensure your ISR was registered.
The final change to your kernel driver is to
implement pwkeeper_logins_show()
. This callback is
invoked when a process reads
from /sys/devices/platform/pwkeeper/logins. This function
writes
to the output buffer the global counter values, in a message
similar to this:
CS421Net Daemon Successful logins: 3 Failed logins: 10 Invalid logins: 4
To test this part, ensure you earlier ran proj2_start.sh. In a second terminal, run this command: telnet localhost 4210. Use this second terminal to send login attempts to your driver. Read from /sys/devices/platform/pwkeeper/logins after each login attempt. Close the telnet session by pressing control-] (right bracket) and then entering close.
Now that you have (in theory) a working driver, you must then write
your own unit tests. Read the networking code
in cs421net.h. You can use cs421net_send()
to
send arbitrary messages to the CS421Net server. Modify
the pwkeeper-test.c to exercise all functionality of this
assignment. This includes a mix of inputs when writing to the device
nodes and network socket, confirming that users can set only their
own master password, that users can add account names, and verifying
network login attempts. Two functions that may be of use
are getuid()
and getresuid()
. You should
avoid using system()
, and instead learn how to perform
I/O yourself.
The unit tests must have comments that explain what things are being tested. As before, your goal is to test boundary conditions of your driver's interfaces. You will be graded based upon the thoroughness of the tests.
Assume that grader will run your unit tests from a "fresh" installation. That is, the grader will not have written anything to /dev/pwkeeper_master nor /dev/pwkeeper_account prior to running your unit tests. If your unit test program requires to be run as the root user (i.e., under sudo), indicate as such within a comment at the top of the file.
You may earn an additional 15% credit for this assignment by improving your KDF. The astute observer will note that if by chance two different users happen to select the same master passwords, then the generated passwords will be the same if they also chose the same account names. The remedy for this situation is to salt the KDF. There are many ways to salt passwords; the following is relatively cryptographically secure.
In your accounts linked list declaration, add a salt field to each
accounts entry. This field is a 16 byte
array. In pwkeeper_account_write()
, fill the salt
array's contents randomly, by using get_random_bytes()
.
Then as part of the KDF, increase the temporary buffer size from 48
to 64 bytes. Append the salt after the account name.
Afterwards, update pwkeeper-test.c to test this new functionality. Explicitly add a unit test that demonstrates that the same combination of master password and account name will result in different passwords [with extremely high probability]. Then add another unit test that shows if two different users have the same master password and account name, their generated passwords will be different [with extremely high probability].
If you choose to perform this extra credit, put a comment at the top of your file, alerting the grader.