Last modified: Wed Sep 17 15:28:31 CDT 2008
Due: Friday, September 26, 2008 by midnight.
Your assignment is to update your secure system from Assignment 1 and add two new operations designed to introduce a covert channel into the system. You will implement the channel and use it to signal information from a high level user to a low level user. Finally, you will measure and report the bandwidth of the channel.
Deliverables: You will be submitting your code electronically to the TA. Make sure that your code is well-annotated (commented) and follows good coding style. Clearly identify the members of your team (one or two people). Include a README file explaining how to run your program and the timing information requested below.
Make sure that all members of your group understand the code and contribute equally, as much as possible, to its development. If anyone gets a free ride, they'll be very disadvantaged later.
CREATE object_name DESTROY object_nameThe intended semantics of CREATE is that a new object is added to the state with SecurityLevel equal to the level of the creating subject. It is given an initial value of 0. If there already exists an object with that name at any level, the operation is a no-op.
DESTROY will eliminate the designated object from the state, assuming that the object exists and the subject has WRITE access to the object. Otherwise, the operation is a no-op.
As with the previous assignment, you will define two subjects Hal and Lyle. However, they will generate the instructions to be executed rather than reading them from an external file. The instuctions will implement a covert channel passing information one bit at a time from Hal to Lyle. Since Lyle and Hal are doing different things, you will need to code separate bodies for Lyle and Hal. Your SecureSubject.run() method will invoke the correct one depending on the subject's name. I have some sample code below to show how this might be done. There is no need to make this very general. You can assume that Hal and Lyle are the only two subjects and have those names.
Here is a possible covert channel; you can design a different one if you like. For this channel, to send a 0 bit Hal executes
CREATE OBJTo send a 1 bit, Hal does nothing. Lyle senses the presence or absence of the object by executing:
CREATE OBJ WRITE OBJ 1 READ OBJ DESTROY OBJIf Lyle sees a value of 1 returned from the READ, then his CREATE succeeded, and Hal has not previously created the object (sending a 1 bit). If Lyle sees a value of 0 returned from the READ, then Hal has previously created the object, Lyle's CREATE has failed, and Hal has sent a 0 bit. (Notice that the value of 1 in the WRITE could have been any non-zero value.)
The goal is to send an arbitrary stream of bits over this channel. I advise getting your program running by sending some small fixed number of bits and making sure they arrive correctly. However, the version you will submit will take a filename parameter on the command line. Your program will read the file contents (probably one byte at a time using a ByteArrayInputStream), convert each byte to 8 bits, send them through the covert channel, reconstruct the byte on the receiving side and write it out to a file. The idea is to transfer the contents of an arbitrary ASCII file over the channel. It may work for other file types, but doesn't have to. (If you only worry about ASCII files, you can assume that there are non-ASCII characters that never appear in the file. That means you can use one of them to signal the end of the input. I.e., if Lyle receives a null byte 00000000, then he knows he can stop receiving.)
For this to work, you have to synchronize the activities of the two threads. To do that, you can use a shared synchronized "token" that the two subjects effectively pass back and forth; only the one currently holding the token is allowed to run. This in itself is a horrible potential covert channel, but we won't worry about that. If you can think of a better (more secure) way to implement the synchronization, please let me know. Below is one implementation of the token class, but feel free to implement your own. An excellent description of Java Synchronization by Ron Rockhold is here: Java Synchronization.
import java.util.*;
import java.io.*;
// This is the class that is used to synchronize the actions of
// Lyle and Hal. They share this class and synchronize on it.
// Only one of them can be executing at a time.
class HasToken {
// the token initially has value "hal"
private static String current = "hal";
public static synchronized void passToken () {
// the value toggles back and forth between "hal" and "lyle"
if ( current.equals("hal") ) {
current = "lyle";
} else {
current = "hal";
}
try {
HasToken.class.notify();
// This should work but some students had problems.
//Class.forName("HasToken").notify();
} catch (Exception e) {}
} // passToken
public static synchronized boolean hasToken (String user) {
try {
if ( ! current.equals(user) )
try {
HasToken.class.wait();
// This should work but some students had problems.
// Class.forName("HasToken").wait();
} catch (ClassNotFoundException e) {}
}
catch (InterruptedException e) { }
finally { return true; }
} // hasToken
} // class hasToken
You might declare the token in your SecureSubject class as follows:
// This is the token used to synchronize the activities of the two
// subjects.
private HasToken token = new HasToken();
Because it is declared static in the HasToken class, there will only
be one instantiation of current.Below is a simplified possible version of what Hal does. This is a method within the SecureSubject class. There would be another method lyleAction to describe Lyle's behavior. This version assumes that you pass a single bit represented as a boolean. Hal takes no action unless he has the token and then only if the bit is not set (is that right?). Note that the point of this example is only to show how the token passing is done.
public void halAction (boolean bit) throws InterruptedException{
int value;
if ( token.hasToken("hal") ) {
if ( ! bit ) {
Instruction inst = new Instruction("Create", "obj");
// the value returned is ignored except for Read operations.
value = refmon.executeInstruction( "hal", inst );
}
// Now pass the token to Lyle.
token.passToken();
}
} // halAction
Then, assuming you were just sending a single bit, the
SecureSubject run function could have some code like this:
try {
if ( subjName.equalsIgnoreCase( "hal" ) ){
halAction(bit); }
else if ( subjName.equalsIgnoreCase( "lyle" ) ){
lyleAction( ...whatever args this needs ); }
}
catch ( InterruptedException e ) { }
Finally, time your program execution on several different input files
and compute the channel's average bandwidth in bits / second. Your
program should compute and output the timing and bandwidth for a given
run. Record that information for several runs, compute the average,
and include that information along with the machine type and clock
speed in the README file for your program. You won't be graded on the
bandwidth, but we'll collect the results and see which group's program
was the most efficient.