CS361 Assignment 1

Last updated: Mon Sep 8 09:16:24 CDT 2008

Due: Monday, September 15, 2008, by the end of the day. This means that your submission must be dated on or before that date.

Deliverables: You will be submitting your code electronically to the TA. Submission instructions are on the class website. Make sure that you clearly identify the members of your team (one or two people).

Make sure that all members of your team understand the code and contribute equally, as much as possible, to its development. If anyone gets a free ride, they'll be disadvantaged later.

The Assignment

Your assignment is to implement in Java a simple "secure" concurrent system following the Bell and LaPadula (BLP) security rules--simple security, the *-property, and strong tranquility (meaning that no levels change over the course of the assignment). Take care with this assignment, because it will form the basis of at least one subsequent assignment.

SecureSubjects in the system will run as threads (i.e., concurrently), each reading and parsing instructions from a file of instructions specific to that subject, and attempting to execute the instructions in order. The instructions are actually passed to a ReferenceMonitor object that acts like a secure file system; more on this below.

SecureObjects in the system store a single integer value (initially 0). The ObjectManager maintains the objects and has methods to create a new object and set and retrieve an object's value. Think of the ObjectManager as a simple unsecure file system. The ObjectManager does not maintain security attributes for the objects. That is done by the ReferenceMonitor, which mediates all accesses to the ObjectManager.

The ReferenceMonitor maintains the security information for the system and acts as a security guard or firewall for the ObjectManager. It maintains the mappings from subject/object names to SecurityLevel. Whenever it receives an instruction from a subject, it must see if the instruction is allowed according to the BLP rules. If so, invoke the ObjectManager to perform the appropriate operation. If not, do nothing except return a 0 value (see below). Legal instructions and their semantics are defined below.

For this first assignment there are three legal instructions:

   READ object_name 
   WRITE object_name value 
   SLEEP
The invoking subject is an implicit parameter to each instruction. Instructions (including object and subject names in instructions) are NOT case-sensitive. Reject any instructions that are syntactically ill-formed because they have an illegal command or the wrong number of arguments. Extra white space within a line is not relevant. I'd suggest that you define an Instruction class and parse the input line into an Instruction object. An Instruction object is what the subject passes to the ReferenceMonitor for execution. Any ill-formed instruction can be parsed into a BadInstruction constant object of that type.

If allowed, a READ operation returns the current value of the named object to the subject. A WRITE operation stores the indicated value into the named object. I'd suggest that ReferenceMonitor have an executeInstruction method that returns an int value. The value returned is always 0 except in the case of a successful, legal READ where the object's current value is returned. (Of course, that could also be a 0.)

SLEEP has no effect except potentially to hand the processor to the other thread. If you have no SLEEP instructions, unless you have very long instruction lists you very likely will execute all of one subject's instructions and then all of the other's, depending on which was the first one scheduled. That's what happened with my example below. Adding a SLEEP in the instruction stream may make the execution be interleaved in some interesting way. (But it may not because the threads may actually be scheduled on different processors.) You might play with that and try to force a nice interleaving.

Your main program will create and launch two SecureSubject threads: subject "Lyle" at LOW security level and subject "Hal" at HIGH security level. You only need two levels for this assignment. HIGH dominates LOW; there are no need-to-know categories. You will also create two SecureObjects: "lobj" at level LOW and "hobj" at level HIGH. See my example below for how this might look. You can use in your coding the fact that you know the names of all subjects and objects. (For example, when you're printing out the state, you don't have to write a general purpose iterator that finds and prints all existing objecs; you can just print out the values of lobj and hobj.)

Execute Lyle and Hal concurrently, each reading from their respective instruction files, and print a readible and coherent log of updates to the state. See my example below. Note that the result very likely will depend on the scheduler, so may differ from run to run.

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?

Example

Below is my top level class. You don't have to follow this model, but you are welcome to:
class ConcSecureSystem {

    public static void main (String args[]) throws IOException {

	ReferenceMonitor rm = new ReferenceMonitor();
	
	// Create a high level subject Hal getting instructions from the file "HalInstructions"
	SecureSubject hal = new SecureSubject("Hal", SecurityLevel.HIGH, "HalInstructions", rm);
	rm.addSubjectLevel("Hal", SecurityLevel.HIGH);

	// Create a low level subject Lyle getting instructions from the file "LyleInstructions"
	SecureSubject lyle = new SecureSubject("Lyle", SecurityLevel.LOW, "LyleInstructions", rm);
	rm.addSubjectLevel("Lyle", SecurityLevel.LOW);

	// Create a high level object lobj with initial value 0
	rm.createNewObject("hobj", 0);
	rm.addObjectLevel("hobj", SecurityLevel.HIGH);

	// Create a low level object lobj with initial value 0
	rm.createNewObject("lobj", 0);
	rm.addObjectLevel("lobj", SecurityLevel.LOW);

	lyle.start();
	hal.start();

    } // main
}
Sample contents for file HalInstructions:
write hobj 10
read hobj
write lobj 20
read lobj
foo bar 30
write hobj 10 20
Sample contents for file LyleInstructions:
write hobj 100
read hobj
write lobj 200
read lobj
write hobj 300
read hobj
write lobj 400
read lobj
write hobj 500
read hobj
Sample output:

The resulting output my program gave is shown below. You do not have to mimic this exactly, but you should generate intelligible output. Think of this as output for run of the program in debug mode. A production run of the system would not generate output like this; i.e., users would not see this.

Notice also that if Hal had been scheduled before Lyle, the result would be quite different. The two subjects are not synchronized. Also, I did not have any SLEEP instructions in this example, or the result might have been different as well. It is possible that your output may look odd because the scheduler interrupts a process in the middle of printing a line. That's OK. You could send the output to two different output files, but don't do that for this exercise.

Consider the following questions: What should be invariant no matter what the scheduler does or how many SLEEP instructions you include? What is likely to change? Reorder the start statements and see what differs.

Starting execution for subject Lyle

Starting execution for subject Hal

Lyle writes value 100 to hobj
Current state: lobj = 0; hobj: 100

Lyle reads hobj
 Subject Lyle read value: 0
Current state: lobj = 0; hobj: 100

Lyle writes value 200 to lobj
Current state: lobj = 200; hobj: 100

Lyle reads lobj
 Subject Lyle read value: 200
Current state: lobj = 200; hobj: 100

Lyle writes value 300 to hobj
Current state: lobj = 200; hobj: 300

Lyle reads hobj
 Subject Lyle read value: 0
Current state: lobj = 200; hobj: 300

Lyle writes value 400 to lobj
Current state: lobj = 400; hobj: 300

Lyle reads lobj
 Subject Lyle read value: 400
Current state: lobj = 400; hobj: 300

Lyle writes value 500 to hobj
Current state: lobj = 400; hobj: 500

Lyle reads hobj
 Subject Lyle read value: 0
Current state: lobj = 400; hobj: 500

Bad Instruction
Current state: lobj = 400; hobj: 500

Hal writes value 10 to hobj
Current state: lobj = 400; hobj: 10

Hal reads hobj
 Subject Hal read value: 10
Current state: lobj = 400; hobj: 10

Hal writes value 20 to lobj
Current state: lobj = 400; hobj: 10

Hal reads lobj
 Subject Hal read value: 400
Current state: lobj = 400; hobj: 10

Bad Instruction
Current state: lobj = 400; hobj: 10

Bad Instruction
Current state: lobj = 400; hobj: 10