CS361 Assignment 1

Due: Wednesday, February 12 by the end of the day. This means that your submission must be dated on or before that date, so you have until midnight.

Remember that you can work on this assignment with one other student. Please indicate clearly in your README file who you (both) are.

Each assignment is graded on a 10 point scale. For this assignment, it is possible to get up to 2 extra credit points. That information is at the bottom.

Deliverables: You will be submitting your code electronically using Canvas. Your program must be able to run on the UT Linux machines, so if you develop it on Windows, make sure it runs on Linux.

Please zip your Java implementation files and README.txt file together and submit a single zip file for the main program, and a separate zip file if you also do the extra credit. You should include either or both of the extra credit steps in one file. Don't forget to name your zip files according to the instructions.

Your zip file should contain a README.txt file containing:

  1. A good description of your code. Write a few sentences to explain the high-level idea of your program along with short descriptions of the key/important functions in your code.
  2. Tell us how much you've finished: all or part. If only part is done, say what you didn't get done and why not. e.g., you ran out of time or you encountered bugs.
For this assignment:
  1. The primary Java file name should be SecureSystem.java. Students may organize their assignments into multiple files and should submit those files as well. However, the main method must be in SecureSystem.java and we should be able to compile your program with javac *.java.

  2. The program should be executed via the command: java SecureSystem instructionlist-filename

  3. Include a README.txt file as specified above.
  4. Zip everything together.

The Assignment

Your assignment is to implement in Java a simple "secure" system following the Bell and LaPadula (BLP) security rules--simple security, the *-property, and strong tranquility. Take care with this assignment, because it will form the basis of the next assignment as well. Imagine a system with subjects and objects. Objects in this system are simple integer variables. Each object has a name and a value (initially 0). Each subject has a name and an integer variable TEMP recording the value it most recently read (also initially 0). Subjects can perform READ or WRITE operations on objects. For a READ, the subject reads the current value of the object, and saves that value into its TEMP variable (a subsequent READ will smash it). When a subject does a WRITE, the object's value is updated. Objects are managed by the ObjectManager class. Think of ObjectManager as a very simple file system that READs and WRITEs objects by name.

The input to your system is a file of commands. For the current assignment, the only legal commands are of the form:

   READ subject_name object_name 
   WRITE subject_name object_name value 

All fields of the instruction are strings except the value, which is an integer.

You will read successive lines from the file and parse each into an InstructionObject class object. Commands are not case-sensitive, even object and subject names. Arbitrary whitespace is allowed in commands, but assume that each command is on one line.

Be sure to deal with the possibility of errors in the instructions (neither READ nor WRITE, wrong number/type of arguments, unknown subject/object, etc.). For illegal instructions, generate a BadInstruction constant object.

Suppose hal and lyle are the only Subjects known in your system and lobj and hobj are the only Objects known in the system. Now, suppose your input file contains the following list of instructions. The top three and the last three are bad and the others syntactically OK (though they may not be permitted by the security policy).

write hal hobj 
read hal 
read sal sobj
write lyle lobj 10
read hal lobj 
write lyle hobj 20
write hal lobj 200
read hal hobj
read lyle lobj
read lyle hobj
foo lyle lobj
Hi lyle, This is hal
The missile launch code is 1234567
We might write an interpreter for this system, that would perform the legal instructions and ignore the others. A reasonable implementation might end with a state in which the objects have values: lobj 200 and hobj 20; the subjects' TEMP variables at the end would contain: hal 20 and lyle 20.

But now, let's add BLP security to our system. Start by giving both subjects and objects associated security labels (levels). These labels are maintained by a ReferenceMonitor class object and can't be changed after they are created (strong tranquility). Essentially, the reference monitor manages two mappings from subject/object names to security labels.

In the secure version of our system, whenever a subject requests to perform an action (READ or WRITE), the parsed InstructionObject is submitted to the reference monitor, which decides whether to perform the action or not based on the BLP properties (Simple Security and the *-Property). If the instruction is both syntactically legal and is allowed by the BLP rules, the reference monitor tells the ObjectManager to perform the appropriate action; otherwise, no objects are accessed.

Assuming an instruction is not Bad, the reference monitor always returns an integer value to the subject: the value of the object read, if the command was a legal READ; and 0 otherwise. If the subject is performing a READ, it stores this value into its TEMP variable. Think of the reference monitor as a firewall around the ObjectManager. Note that the ObjectManager itself doesn't know or care about labels or security; it just performs simple accesses.

The top-level class (SecureSystem) manages subjects and the reference monitor, and also serves as the command interpreter. It reads successive instructions from the instruction list, parses them, and submits them to the reference monitor, which asks the ObjectManager to perform them (or not). The value returned by the reference monitor is passed to the subject executing the instruction.

Your task is to implement all this, subject to the following constraints.

  1. The ObjectManager should be local to the ReferenceMonitor. That ensures that you can't access objects except via the ReferenceMonitor interface.
  2. SecurityLevel should be a class with a defined "dominates" relation. You can assume that levels are linearly ordered. That is, you don't need to worry about need-to-know categories. Within that class define two constant levels HIGH and LOW such that HIGH dominates LOW.
  3. When you parse an instruction from the input file, create an InstructionObject (with fields representing the instruction type (READ, WRITE, BAD), the subject's name, the object's name, value if any). It's this InstructionObject that you pass to ReferenceMonitor. For an ill-formed instruction you should pass a constant BadInstruction object. The ReferenceMonitor should know how to deal with these objects.
To see how your system is behaving, you might write a debugging method printState in the SecureSystem class that prints out current values from the state: the value of objects and the TEMP value for subjects. See the example below for what the output of this should look like. Ideally, you would be able to iterate over your data structures to handle an arbitrary number of subjects/objects, but for this assignment you can assume that there are only two of each and you know their names. (Note: printing this kind of information is not something that a typical system user should be able to do.)

However, for the production run (the one we'll grade) your output must be more constrained. We'll say more below.

The main function in your SecureSystem class should perform the following tasks:

  1. Create two new subjects: lyle of security level LOW, and hal of security level HIGH. Store these subjects into the state and inform the reference monitor about them.

  2. Create two new objects: lobj of security level LOW, and hobj of security level HIGH. Store these in the ObjectManager, telling the ReferenceMonitor about their levels. The initial value of each should be 0.

  3. Read successive instructions from the input file and execute them, following the Bell and LaPadula constraints on reading and writing. You should have methods executeRead and executeWrite within your ReferenceMonitor class that check access requests and perform the appropriate update (if any) on the state, following the instruction semantics outlined above.

  4. After each instruction, print a line that reflects the instruction executed and the values of the two objects (in order lyle and hal) and of the two objects (in order lobj and hobj). There is an example below.

You should following good programming practices, making your code modular and using good abstractions. For example, SecurityLevel should be a class that defines levels and Dominates in such a way that you might later add need-to-know categories without changing much outside the class. Don't take a bunch of shortcuts that result in lousy code.

Below is a snippet of code from my main function. You don't have to follow this model, but you're welcome to.

 

        // LOW and HIGH are constants defined in the SecurityLevel 
        // class, such that HIGH dominates LOW.

	SecurityLevel low  = SecurityLevel.LOW;
	SecurityLevel high = SecurityLevel.HIGH;

	// We add two subjects, one high and one low.

	sys.createSubject("lyle", low);
	sys.createSubject("hal", high);

	// We add two objects, one high and one low.

	sys.getReferenceMonitor().createNewObject("Lobj", low);
	sys.getReferenceMonitor().createNewObject("Hobj", high);

	...

While designing your system consider the following questions: Are there security flaws in this design? What are they? How might you go about fixing them?

Consider the list of instructions given above. Below is a description that shows what is happening as each line is executed. This is not what your output should look like, but you should make sure you understand how each instruction updates the state. The actual output is lower on this page.

The initial state is:
   lobj: 0
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "write hal hobj"
Bad Instruction
The current state is: 
   lobj: 0
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "read hal"
Bad Instruction
The current state is: 
   lobj: 0
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "read sal sobj"
Bad Instruction
The current state is: 
   lobj: 0
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "read hal"
Bad Instruction
The current state is: 
   lobj: 0
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "write lyle lobj 10"
OK to execute
The current state is: 
   lobj: 10
   hobj: 0
   lyle: 0
   hal: 0

Instruction: "read hal lobj"
OK to execute
The current state is: 
   lobj: 10
   hobj: 0
   lyle: 0
   hal: 10

Instruction: "write lyle hobj 20"
OK to execute
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 10

Instruction: "write hal lobj 200"
Fails BLP tests
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 10

Instruction: "read hal hobj"
OK to execute
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 20

Instruction: "read lyle lobj"
OK to execute
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 10
   hal: 20

Instruction: "read lyle hobj"
Fails BLP tests
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 20

Instruction: "foo lyle lobj"
Bad Instruction
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 20

Instruction: "Hi lyle, This is hal"
Bad Instruction
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 20

Instruction: "The missile launch code is 1234567"
Bad Instruction
The current state is: 
   lobj: 10
   hobj: 20
   lyle: 0
   hal: 20

But what your program will generate in production mode is the following output:
> java SecureSystem instructionList

bad 0 0 0 0
bad 0 0 0 0
bad 0 0 0 0
write 10 0 0 0
read 10 0 0 10
write 10 20 0 10
write 10 20 0 10
read 10 20 0 20
read 10 20 10 20
read 10 20 0 20
bad 10 20 0 20
bad 10 20 0 20
bad 10 20 0 20
For each line, there should be five fields separated by a single space. The first field should contain one of the lower case keywords: "bad", "read", "write." Use "bad" only if the instruction is syntactically illegal; not if it's not permitted by BLP. The other four fields contain the values in order of lobj, hobj, value most recently read by lyle, value most recently read by hal. If the instruction is not permitted by BLP, you should still indicate the attempted operation. Even for a failed instruction, the values may still change; for example, a failed READ returns a 0 to the reading subject. The TA should be able to pipe your output to a file and compare the file to his test. They should be identical!

Extra Credit

If you decide to do the extra credit, you should do the regular assignment and submit it. Then do this part separately.

You can get up to 2 points extra credit on this assignment: 1 point for the threaded implementation and 1 point for adding a SLEEP instruction. You can only do the SLEEP part, if you are also doing the threaded implementation.

Since it's extra credit, I'm not going to give you detailed instructions on this part. If you want to do this one, you will have to use some ingenuity, though I'm happy to help you individually.

The assignment above is unrealistic in that the actions of subjects lyle and hal are totally synchronized according to the ordering of instructions in the input. Reimplement this assignment so that lyle and hal run as separate processes (threads), each reads from its own instruction file (remove the subject name argument to each instruction), and the reference monitor is a shared resource.

If you do this in the most straightforward way, they will run in an arbitrarily interleaved fashion depending on the OS scheduling. If the instruction lists are fairly short, whichever one gets the processor first likely will just blow through his instruction list, finishing before the other thread ever runs. Add an additional SLEEP instruction that relinguishes the processor. I.e., if hal is running and executes a SLEEP instruction, then the processor should be handed to lyle, etc. That way you can force interesting interleavings. Note that this still may not work as expected on multi-processor systems where the threads are scheduled on separate processors. You may be able to deal with this issue by SLEEPING for some microseconds. Printing of the output may be oddly interleaved. Don't worry about that.

If you're having problems with the threads programming, these may help. Here is an excellent description of Java Synchronization by Ron Rockhold: Java Synchronization, and a link to a helpful short paper by Prof. Mike Dahlin: Threads Programming.