Project 2: Intrusion Detection

Due by: Friday, March 7, 2025 at 11:59 p.m.

Introduction

In this project, you will build a client to connect to an intrusion detection system (IDS) server. An IDS is a security tool that detects if files have been corrupted, permissions have been changed, and so on, typically as the result of an attack.

Your client will use a message queue to request information from the server, and then it will compare that data with information you gather locally about the file. Specifically, you are comparing the file's size (in bytes), the permissions (represented as a mode_t value), and the checksum value (computed as in Assignment 4).

Note that Section 3.6 of the textbook has valuable information about using message queues. In addition to the textbook, you might want to consult the POSIX specification or man pages for fstat() and access() functions.

Lastly, note that you may need to monitor your processes and message queues, as crashes might leave background processes or existing queues. Use ps -ef | grep dbowie (assuming your username is dbowie) to find your processes. Use kill -USR1 on the server processes to get them to shut down cleanly. For message queues, look in the /dev/mqueue directory and use the rm command to delete them as needed.

Project Structure

Download the archive given below and unzip it into an appropriate directory.

This project uses a message queue to communicate with a pre-compiled server application. Your task is to construct request messages to send to the server and to process the responses you get back. You will be working primarily in the following files:

client.c
This file contains two required functions: one for opening message queues and retrieving information about a file, and one for determining if this information is correct. All files used for testing purposes are in the data directory, but the server is configured to return intentionally bad results for certain files (thus simulating a corruption of those files).
support.c
This file contains two required functions for starting and stopping the server programmatically. This functionality is only required for the Stage 3 requirements. For Stages 1 and 2, you can run the server by starting it manually in one window and sending SIGUSR1 on the command line to stop it.
main.c
This file is the main driver of the program. For Stages 1 and 2, you will need to start the server manually and implement enough code here to call the functions in client.c. For Stage 2, you will need to modify get_args() to accept a -k command-line argument; when this flag is passed, you will need to send a signal to the server to kill it after processing the current requests. For Stage 3, you will start by checking to see if there is a server process based on the provided pidfile argument. If not, start it. Once the server is running, issue the file requests to the server. Finally, you will modify get_args() to add the -o outfile option (writing the results into a binary file).

In addition to these files, you should consult the provided header files, which contain the required type definitions. Do not modify these header files, as your messages will no longer be compatible with the server or the testing infrastructure.

Stage 1: Getting and Checking Records

As with all the assignments and projects in this course, this project is designed to be tackled incrementally. Unless you are passing all of the Stage 1 requirements, it's not recommended that you move on to Stage 2 or 3 requirements.

Work through the following steps:

  • Implement the get_record() function. Open a request message queue (whose name is given by the mqreq parameter) to send the request, and receive the response using the mqresp message queue. Return the server's response through the call-by-reference response parameter.
  • Implement the check_record() function to print the specified messages and return true. You do not need to validate the information is correct for Stage 1.
  • Implement enough code in main() to support requesting the information about a single file. Run ./ids -h (or see the usage() function) for the order of arguments. Note that you will need two message queues: one for requests and one for responses.

For Stage 1 and Stage 2 implementation, you should run the server manually on the command line. Note that $ in the following examples is used to mark the command prompt and should not be entered. You would run the server as follows:

$ ./server /comp3400.req /comp3400.resp
Server starting...
SUCCESS: Server is now waiting on messages
SUCCESS: Server started

You can actually call your message queues whatever you want, but we name them to distinguish them from other message queues on the system. Note: The names that you pick must begin with a / as shown here, because of POSIX requirements. In a different window, you can run your program and shutdown the server as follows. The $(cat PID) part of that last line will open the PID file (which contains the server's PID) and dump its contents into the command.

$ ./ids /comp3400.req /comp3400.resp data/int.txt
data/int.txt:
  permissions: 100640
  size: 4
  cksum: 2330645186
$ kill -USR1 $(cat PID)

Note that the server contains some diagnostic information that may be helpful at times. This information is generally written to stderr, which means it won't affect the output required for the test cases. If the USR1 signal doesn't work, you can also kill the server process at any time using killall -9 server, but that will leave the message queues open. In that situation, you would need to go into /dev/mqueue and manually delete your message queues.

Stage 2: Validating Responses and Stopping the Server

Work through the following steps:

  • Update the check_record() function to validate the server's response with local values about the file. Doing so will require you to fork() and exec() the cksum program and read its output, as you did in Assignment 4. Print the specified messages and return whether or not the data was correct.
  • Implement stop_server() to shut the server down cleanly. This function will open the specified pidfile to get the server process's PID, then send it SIGUSR1.
  • Modify main.c to accept the -k flag, killing the server by calling stop_server(). Be sure that your command-line processing supports multiple file requests by looping through all of the arguments after the flags.

Stage 3: Starting the Server and Writing Output

Work through the following steps:

  • Implement start_server(). Consult the comment describing this function, because the timing is important and requires redirecting the server's stdout through a pipe.
  • Modify main.c to start the server if the specified pidfile does not exist.
  • Modify main.c to support the -o outfile command-line option. When this option is passed, you'll create a binary file of the specified name, and you'll write out an array of ids_entry_t structs. These entries contain the file name, the values retrieved from the server, and a boolean value indicating whether or not the data is correct.

For the -o outfile option, you must create the file of the exact size needed (by using ftruncate() after open()). You can then use mmap() to map the file into memory and treat the file as an array of structs. Note that mmap() will require you to use MAP_SHARED to save these values to disk. Doing so will ensure that your file is the exact size and format required without having to worry about things like endianness, padding, or a trailing newline character.

Turn In

First, run make clean to remove all of the executable and object files from your directory structure. Then, zip up the contents of your project directory in either a .zip or a .tar.xz file. Upload this archive to Brightspace. Your project must be submitted by Friday, March 7, 2025 at 11:59 p.m. unless you're going to use a grace day.

All work must be done within assigned teams, though you may discuss general concepts with your classmates. Please refer to the course policies if you have any questions about academic integrity. If you have trouble with the assignment, I am always available for assistance.

Under no circumstances should any member of one team look at the code written by another team. Tools will be used to detect code similarity automatically.

Code that does not compile will automatically score zero points.

Grading

Your grade will be determined by the following categories.

Category Description Weight
Unit Tests Your solution passes the six unit tests. 25%
Integration Tests Your solution passes the 11 integration tests. 55%
Memory Leaks Your code has no memory leaks. 10%
Style Your solution conforms to GNU style, passes the style checks, and most importantly, is readable. 10%

Credits

This assignment is based on material from CS 361 by Michael S. Kirkpatrick. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.