diff --git a/refpolicy/Makefile b/refpolicy/Makefile index 631a2e3..2c7ba7e 100644 --- a/refpolicy/Makefile +++ b/refpolicy/Makefile @@ -50,7 +50,6 @@ LOADPOLICY := $(SBINDIR)/load_policy SETFILES := $(SBINDIR)/setfiles XMLLINT := $(BINDIR)/xmllint -XMLDTD := policy.dtd # enable MLS if requested. ifeq ($(MLS),y) @@ -108,6 +107,15 @@ ALL_FC_FILES := $(foreach dir,$(ALL_LAYERS),$(wildcard $(dir)/*.fc)) POLICY_SECTIONS := tmp/pre_te_files.conf tmp/generated_definitions.conf tmp/all_interfaces.conf tmp/all_attrs_types.conf tmp/only_te_rules.conf tmp/all_post.conf +DOCTOOLS = doctools +XMLDTD = $(DOCTOOLS)/policy.dtd +HTMLHEAD = $(DOCTOOLS)/header.html +HTMLFOOT = $(DOCTOOLS)/footer.html +HTMLCSS = $(DOCTOOLS)/style.css +HTMLOUT = $(DOCTOOLS)/html +JAVASRC = $(wildcard $(DOCTOOLS)/src/*.java) $(wildcard $(DOCTOOLS)/src/policy/*.java) +JAVABYTE = $(patsubst %.java,%.class,$(JAVASRC)) + ######################################## # # default action: build policy locally @@ -280,10 +288,9 @@ relabel: $(FC) $(SETFILES) # Documentation generation # -xml: policy.xml - -policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf +tmp/policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf @echo "Creating $@" + @cp $(XMLDTD) tmp $(QUIET) echo '<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>' > $@ $(QUIET) echo '<!DOCTYPE policy SYSTEM "policy.dtd">' >> $@ $(QUIET) echo "<policy>" >> $@ @@ -295,6 +302,15 @@ policy.xml: $(ALL_INTERFACES) tmp/generated_definitions.conf $(XMLLINT) --noout --dtdvalid $(XMLDTD) $@ ;\ fi +$(JAVABYTE) doctool: $(JAVASRC) + javac $(JAVASRC) + +html: tmp/policy.xml $(JAVABYTE) $(HTMLHEAD) $(HTMLFOOT) + @mkdir -p $(DOCTOOLS)/html + $(QUIET) java -cp $(DOCTOOLS)/src/ Docgen -xf tmp/policy.xml \ + -hf $(HTMLHEAD) -ff $(HTMLFOOT) -od $(HTMLOUT) + $(QUIET) cp $(HTMLCSS) $(HTMLOUT) + ######################################## # # Runtime binary policy patching of users @@ -369,5 +385,7 @@ clean: rm -f policy.conf rm -f policy.$(PV) rm -f $(FC) + rm -fR $(HTMLOUT) + find $(DOCTOOLS)/src -iname "*.class" | xargs rm -f -.PHONY: default policy install reload enableaudit checklabels restorelabels relabel xml clean +.PHONY: default policy install reload enableaudit checklabels restorelabels relabel html clean diff --git a/refpolicy/doc/doctools/footer.html b/refpolicy/doc/doctools/footer.html new file mode 100644 index 0000000..308b1d0 --- /dev/null +++ b/refpolicy/doc/doctools/footer.html @@ -0,0 +1,2 @@ +</body> +</html> diff --git a/refpolicy/doc/doctools/header.html b/refpolicy/doc/doctools/header.html new file mode 100644 index 0000000..daf0904 --- /dev/null +++ b/refpolicy/doc/doctools/header.html @@ -0,0 +1,8 @@ + +<html> +<head> +<title>Security Enhanced Linux Reference Policy</title> +<style type="text/css" media="all">@import "style.css";</style> +</head> +<body> +<div id="Header">Security Enhanced Linux Reference Policy</div> diff --git a/refpolicy/doc/doctools/policy.dtd b/refpolicy/doc/doctools/policy.dtd new file mode 100644 index 0000000..6d21a1b --- /dev/null +++ b/refpolicy/doc/doctools/policy.dtd @@ -0,0 +1,17 @@ +<!ELEMENT policy (module+)> +<!ELEMENT module (summary,interface+)> +<!ATTLIST module + name CDATA #REQUIRED + layer CDATA #REQUIRED> +<!ELEMENT summary (#PCDATA)> +<!ELEMENT interface (description,parameter+,infoflow)> +<!ATTLIST interface name CDATA #REQUIRED> +<!ELEMENT description (#PCDATA)> +<!ELEMENT parameter (#PCDATA)> +<!ATTLIST parameter + name CDATA #REQUIRED + optional (true|false) "false"> +<!ELEMENT infoflow EMPTY> +<!ATTLIST infoflow + type CDATA #REQUIRED + weight CDATA #IMPLIED> diff --git a/refpolicy/doc/doctools/src/Converter.java b/refpolicy/doc/doctools/src/Converter.java new file mode 100644 index 0000000..91378fd --- /dev/null +++ b/refpolicy/doc/doctools/src/Converter.java @@ -0,0 +1,247 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Converter.java: The reference policy documentation converter + * Version: @version@ + */ +import policy.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; + +import java.util.Map; + +/** + * The reference policy documentation generator and xml analyzer class. + * It pulls in XML describing reference policy, transmogrifies it, + * and spits it back out in some other arbitrary format. + */ +public class Converter{ + private Policy policy; + private static final String HTMLEXT = ".html"; + private static final String indexContent = + "<div id=\"Content\"><h2>Welcome to the reference policy API!</h2><p/>" + + "Please choose from the navigation options to the left.</div>"; + + private StringBuffer headerOutput = new StringBuffer(); + private StringBuffer footerOutput = new StringBuffer(); + private File outDir; + + public Converter(Policy pol, File headerFile, File footerFile) throws IOException{ + policy = pol; + + /* + * Setup header and footer. + */ + FileReader headIn = new FileReader(headerFile); + + BufferedReader br = new BufferedReader(headIn); + String line = null; //not declared within while loop + while (( line = br.readLine()) != null){ + headerOutput.append(line); + headerOutput.append(System.getProperty("line.separator")); + } + + FileReader footerIn = new FileReader(footerFile); + + br = new BufferedReader(footerIn); + line = null; //not declared within while loop + while (( line = br.readLine()) != null){ + footerOutput.append(line); + footerOutput.append(System.getProperty("line.separator")); + } + } + + public void Convert(File _outDir) throws IOException{ + outDir = _outDir; + + // write the index document + FileWrite("index" + HTMLEXT, headerOutput.toString() + + Menu().toString() + indexContent + footerOutput.toString()); + + // walk the policy and write pages for each module + for(Map.Entry<String,Layer> lay:policy.Children.entrySet()){ + Layer layer = lay.getValue(); + // the base output contains the menu and layer content header + StringBuffer baseOutput = new StringBuffer(); + // the layer output will be filled with modules and summarys for content + StringBuffer layerOutput = new StringBuffer(); + + // create the navigation menu + baseOutput.append(Menu(layer.Name)); + + baseOutput.append("<div id=\"Content\">\n"); + baseOutput.append("\t<h1>Layer: " + layer.Name + "</h1><p/>\n"); + + layerOutput.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n"); + layerOutput.append("<tr><th class=\"title\">Module Name</th>" + + "<th class=\"title\">Summary</th></tr>"); + + for(Map.Entry<String,Module> mod:layer.Children.entrySet()){ + // module output will be filled with in-depth module info. + StringBuffer moduleOutput = new StringBuffer(); + Module module = mod.getValue(); + + // get the content for the module's document + moduleOutput.append(moduleContent(mod.getValue()).toString() + "</div>"); + + // get the summary and name for the layer's document + + layerOutput.append("<tr><td><a href=\"" + layer.Name + "_" + module.Name + HTMLEXT + + "\">" + module.Name + "</a></td>" + + "\n<td>" + module.PCDATA + "</td></tr>\n"); + + // write module document + FileWrite(layer.Name + "_" + module.Name + HTMLEXT, + headerOutput.toString() + "\n" + baseOutput.toString() + moduleOutput.toString() + footerOutput.toString()); + } + // write layer document + FileWrite(layer.Name + HTMLEXT, + headerOutput.toString() + "\n" + baseOutput.toString() + + layerOutput.toString() + "</div>" + footerOutput.toString()); + + } + } + + private StringBuffer Menu(String key){ + StringBuffer out = new StringBuffer(); + out.append("<div id=\"Menu\">\n"); + for(Map.Entry<String,Layer> layer:policy.Children.entrySet()){ + String layerName = layer.getKey(); + + + // show the modules for the current key + if (layerName.equals(key)){ + out.append("\t<a href=\"" + layerName + + HTMLEXT + "\">" + layerName + "</a><br />\n"); + out.append("\t<div id=\"subitem\">\n"); + for(Map.Entry<String,Module> module:layer.getValue().Children.entrySet()){ + String moduleName = module.getKey(); + out.append("\t\t- <a href=\"" + layerName + "_" + moduleName + HTMLEXT + + "\">" + moduleName + "</a><br />\n"); + } + out.append("\t</div>\n"); + } else { + out.append("\t<a href=\"" + layerName + + HTMLEXT + "\">+ " + layerName + "</a><br />\n"); + } + } + out.append("</div>"); + return out; + } + + private StringBuffer Menu(){ + StringBuffer out = new StringBuffer(); + out.append("<div id=\"Menu\">\n"); + for(Map.Entry<String,Layer> layer:policy.Children.entrySet()){ + String layerName = layer.getKey(); + + + out.append("\t<a href=\"" + layerName + HTMLEXT + "\">" + layerName + "</a><br />\n"); + out.append("\t<div id=\"subitem\">\n"); + for(Map.Entry<String,Module> module:layer.getValue().Children.entrySet()){ + String moduleName = module.getKey(); + out.append("\t\t- <a href=\"" + layerName + "_" + moduleName + HTMLEXT + + "\">" + moduleName + "</a><br />\n"); + } + out.append("\t</div>\n"); + } + out.append("</div>"); + return out; + } + + private StringBuffer moduleContent(Module module){ + StringBuffer out = new StringBuffer(); + + out.append("\t<h2>Module: "+ module.Name + "<br />\n"); + out.append("\tSummary: " + module.PCDATA + "</h2>\n"); + for (Map.Entry<String,Interface> inter:module.Children.entrySet()){ + Interface iface = inter.getValue(); + // main table header + out.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n"); + out.append("<tr><th class=\"title\" colspan=\"3\">Interface</th></tr>\n"); + + // only show weight when type isnt none + if (iface.Type != InterfaceType.None){ + out.append("<tr><td>Name</td><td colspan=\"2\">" + iface.Name + "</td></tr>\n" + + "<tr><td>Flow Type</td><td colspan=\"2\">" + iface.Type.toString() + "</td></tr>\n"); + out.append("\t<td>Flow Weight</td><td colspan=\"2\">" + iface.Weight + "</td></tr>\n"); + } else { + out.append("<tr><td>Name</td><td colspan=\"2\">" + iface.Name + "</td></tr>\n"); + out.append("<tr><td>Flow Type</td><td colspan=\"2\">" + iface.Type.toString() + "</td></tr>\n"); + + } + out.append("<tr><td>Description</td><td colspan=\"2\">" + iface.PCDATA + "</td></tr>\n"); + + out.append("<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" width=\"75%\">\n" + + "<tr><th class=\"title\">Parameter</th><th class=\"title\">Description</th>" + + "<th class=\"title\">Optional</th></tr>"); + for (Map.Entry<String,Parameter> param:iface.Children.entrySet()){ + Parameter parameter = param.getValue(); + out.append("\t<tr><td> " + parameter.Name + "</td>\n"); + out.append("\t<td>" + parameter.PCDATA + "</td>\n"); + String opt = parameter.GetAttribute("optional"); + if (opt != null && opt.equalsIgnoreCase("true")){ + out.append("\t<td><center>Yes</center></td></tr>\n"); + } else { + out.append("\t<td><center>No</center></td></tr>\n"); + } + } + out.append("\n</table></table><br/><br/>\n"); + } + return out; + } + + /** + * Write to sub directory. + * @param path + * @param outFilename + * @param content + * @return + */ + private void FileWrite (String path, String outFilename, String content){ + try { + // create parent if necessary + File outParent = new File(outDir, path ); + File outFile = new File(outParent, outFilename ); + if (outParent.exists() && !outParent.isDirectory()){ + throw new IOException("Output directory not really a directory"); + } else if (!outParent.exists()){ + outParent.mkdirs(); + } + PrintStream stream = new PrintStream(new FileOutputStream(outFile)); + stream.println(content); + stream.flush(); + stream.close(); + } catch (Exception e){ + System.err.println (e.getMessage()); + System.exit(1); + } + } + + /** + * Write to output directory directly. + * + * @param path + * @param outFilename + * @param content + * @return + */ + private void FileWrite (String outFilename, String content){ + try { + File out = new File(outDir, outFilename ); + PrintStream stream = new PrintStream(new FileOutputStream(out)); + stream.println(content); + stream.flush(); + stream.close(); + } catch (Exception e){ + System.err.println (e.getMessage()); + System.exit(1); + } + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/Docgen.java b/refpolicy/doc/doctools/src/Docgen.java new file mode 100644 index 0000000..12e38bf --- /dev/null +++ b/refpolicy/doc/doctools/src/Docgen.java @@ -0,0 +1,565 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Docgen.java: The reference policy xml analyzer and documentation generator + */ +import policy.*; + +import java.io.*; +import java.util.*; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Schema; + +import javax.xml.XMLConstants; + +import org.xml.sax.*; +import org.w3c.dom.*; + +/** + * The reference policy documentation generator and xml analyzer class. + * It pulls in XML describing reference policy, transmogrifies it, + * and spits it back out in some other arbitrary format. + */ +public class Docgen{ + // store the PIs here + private static Vector procInstr = new Vector(); + private static boolean verbose = false; + // the policy structure built after xml is parsed + private Policy policy = null; + // the xml document + private Document dom = null; + + // the files/directories passed in from the command line + private static File xmlFile; + private static File headerFile; + private static File footerFile; + private static File outputDir; + + private static void printUsage(){ + System.out.println("Reference Policy Documentation Compiler usage:"); + System.out.println("\tjava -cp ./src Docgen [-h] [-v] -xf xmlFileIn -hf headerFile -ff footerFile -od outDirectory"); + System.out.println("-h display this message and exit"); + System.out.println("-xf XML file to parse"); + System.out.println("-hf header file for HTML output"); + System.out.println("-ff footer file for HTML output"); + System.out.println("-od output directory"); + System.exit(1); + } + + /** + * Docgen constructor + * + * @param output Filename to setup for output + */ + public Docgen(String output) + throws FileNotFoundException { + } + + /** + * The main() driver for the policy documentation generator. + * @param argv Arguments, takes 1 filename parameter + */ + public static void main(String argv[]) { + if (argv.length == 0){ + printUsage(); + System.exit(1); + } + // hacked up version of getopt() + for (int x=0; x < argv.length; x++){ + if (argv[x].equals("-xf")){ + x++; + if (x<argv.length){ + xmlFile = new File(argv[x]); + if (!xmlFile.isFile()){ + printUsage(); + System.err.println("XML file is not really a file!"); + System.exit(1); + } + } else { + printUsage(); + System.exit(1); + } + } else if (argv[x].equals("-hf")){ + x++; + if (x<argv.length){ + headerFile = new File(argv[x]); + if (!headerFile.isFile()){ + printUsage(); + System.err.println("Header file is not really a file!"); + System.exit(1); + } + } else { + printUsage(); + System.exit(1); + } + } else if (argv[x].equals("-ff")){ + x++; + if (x<argv.length){ + footerFile = new File(argv[x]); + if (!footerFile.isFile()){ + printUsage(); + System.err.println("Footer file is not really a file!"); + System.exit(1); + } + } else { + printUsage(); + System.exit(1); + } + } else if (argv[x].equals("-od")){ + x++; + if (x<argv.length){ + outputDir = new File(argv[x]); + if (!outputDir.isDirectory()){ + printUsage(); + System.err.println("Output directory is not really a directory!"); + System.exit(1); + } + } else { + printUsage(); + System.exit(1); + } + } else if (argv[x].equals("-h")){ + printUsage(); + System.exit(1); + } else if (argv[x].equals("-v")){ + verbose = true; + } else { + printUsage(); + System.out.println("Error unknown argument: " + argv[x]); + System.exit(1); + } + } + + try { + // create document factory + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = schemaFactory.newSchema(); + + factory.setValidating(true); + factory.setNamespaceAware(true); + + // in order for this setting to hold factory must be validating + factory.setIgnoringElementContentWhitespace(true); + + // get builder from factory + DocumentBuilder builder = factory.newDocumentBuilder(); + + // create an anonymous error handler for parsing errors + builder.setErrorHandler( + new org.xml.sax.ErrorHandler() { + // fatal errors + public void fatalError(SAXParseException exception) + throws SAXException { + throw exception; + } + + // parse exceptions will be fatal + public void error(SAXParseException parseErr) + throws SAXParseException + { + // Error generated by the parser + System.err.println("\nPARSE ERROR: line " + parseErr.getLineNumber() + + ", URI " + parseErr.getSystemId()); + System.err.println("PARSE ERROR: " + parseErr.getMessage() ); + + // check the wrapped exception + Exception x = parseErr; + if (parseErr.getException() != null) + x = parseErr.getException(); + x.printStackTrace(); } + + // dump warnings too + public void warning(SAXParseException err) + throws SAXParseException + { + System.err.println("\nPARSE WARNING: line " + err.getLineNumber() + + ", URI " + err.getSystemId()); + System.err.println("PARSE WARNING: " + err.getMessage()); + } + } + ); + + Docgen redoc = new Docgen(argv[1]); + + redoc.dom = builder.parse(xmlFile); + + // do our own transformations + redoc.processDocumentNode(); + + // build our own converter then convert + Converter converter = new Converter(redoc.policy, headerFile, footerFile); + converter.Convert(outputDir); + // TODO: figure out which of these is taken care of by the anonymous error handler above + } catch (SAXException saxErr) { + // sax error + Exception x = saxErr; + if (saxErr.getException() != null) + x = saxErr.getException(); + x.printStackTrace(); + } catch (ParserConfigurationException parseConfigErr) { + // Sometimes we can't build the parser with the specified options + parseConfigErr.printStackTrace(); + } catch (IOException ioe) { + // I/O error + ioe.printStackTrace(); + } catch (Exception err) { + err.printStackTrace(); + } + + + } // main + + public static void Debug(String msg){ + if (verbose) + System.out.println(msg); + } + + void processDocumentNode() throws SAXException{ + Element docNode = dom.getDocumentElement(); + + if (docNode != null && docNode.getNodeName().equals("policy")){ + policy = new Policy("policy"); + + NodeList children = docNode.getChildNodes(); + int len = children.getLength(); + + for (int index = 0; index < len; index++){ + processNode(children.item(index), policy); + } + } else { + System.err.println("Failed to find document/policy node!"); + System.exit(1); + } + } + + /** + * Process children of the policy node (aka modules). + * + * @param node A child node of the policy. + * @param parent The parent PolicyElement. + */ + void processNode(Node node, Policy parent) throws SAXException{ + Layer layer = null; + Module module = null; + + // save us from null pointer de-referencing + if (node == null){ + System.err.println( + "Nothing to do, node is null"); + return; + } + + // snag the name + String nodeName = node.getNodeName(); + + // validity check and pull layer attribute + if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("module")){ + // display the name which might be generic + Docgen.Debug("Encountered node: " + nodeName); + NamedNodeMap attrList = node.getAttributes(); + + // the required attributes + int attrLen = 0; + if(attrList != null){ + attrLen = attrList.getLength(); + } else{ + fatalNodeError("Missing attributes in module. \"" + + "\". \"layer\" and \"name\" are required attributes."); + } + + Node moduleNode = attrList.getNamedItem("name"); + Node layerNode = attrList.getNamedItem("layer"); + + if (moduleNode == null || layerNode == null) + fatalNodeError("Missing attributes in module element. \"layer\" and \"name\" are required attributes."); + + String moduleName = moduleNode.getNodeValue(); + String layerName = layerNode.getNodeValue(); + + // check to see if this is a new layer or a pre-existing layer + layer = parent.Children.get(layerName); + if (layer == null){ + Docgen.Debug("Adding new layer: " + layerName); + layer = new Layer(layerName, parent); + } else { + Docgen.Debug("Lookup succeeded for: " + layerName); + } + + if (layer.Children.containsKey(moduleName)){ + Docgen.Debug("Reusing previously defined module: " + moduleName); + module = layer.Children.get(moduleName); + } else { + Docgen.Debug("Creating module: " + moduleName); + module = new Module(moduleName, layer); + } + + // take care of the attributes + for(int i = 0; i < attrLen; i++){ + Node attrNode = attrList.item(i); + String attrName = attrNode.getNodeName(); + String attrValue = attrNode.getNodeValue(); + if (!attrName.equals("layer") && !attrName.equals("name")){ + Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName() + + "=" + attrValue); + module.AddAttribute(attrName,attrValue); + } + } + } else if (!isEmptyTextNode(node)){ + fatalNodeError("Unexpected child \"" + nodeName + +"\" node of parent \"" + parent.Name + "\"."); + } + + // recurse over children if both module and layer defined + if (module != null && layer != null){ + // the containsKey check verified no duplicate module + layer.Children.put(module.Name, module); + parent.Children.put(layer.Name, layer); + + NodeList children = node.getChildNodes(); + if (children != null){ + int len = children.getLength(); + for (int index = 0; index < len; index++){ + processNode(children.item(index), module); + } + } + } + } + + /** + * Process children of the module node (aka interfaces). + * + * @param node A child node of the policy. + * @param parent The parent PolicyElement. + */ + void processNode(Node node, Module parent) throws SAXException{ + Interface iface = null; + + // save us from null pointer de-referencing + if (node == null){ + System.err.println( + "Nothing to do, node is null"); + return; + } + + // snag the name + String nodeName = node.getNodeName(); + + // if summary node + if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("summary")){ + // unfortunately we still need to snag the PCDATA child node for the actual text + Docgen.Debug("Encountered node: " + nodeName); + NodeList children = node.getChildNodes(); + if (children != null && children.getLength() == 1){ + if (children.item(0).getNodeType() == Node.TEXT_NODE){ + parent.PCDATA = children.item(0).getNodeValue(); + return; + } + } + fatalNodeError("Unexpected child \"" + nodeName + +"\" node of parent \"" + parent.Name + "\"."); + // if interface node + } else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("interface")){ + NamedNodeMap attrList = node.getAttributes(); + // the required attributes + int attrLen = 0; + if(attrList != null){ + attrLen = attrList.getLength(); + } else{ + fatalNodeError("Missing attribute in interface. " + + "\"name\" is a required attribute."); + } + + Node nameNode = attrList.getNamedItem("name"); + + if (nameNode == null ) + fatalNodeError("Missing attribute in interface. " + + "\"name\" is a required attribute."); + + + String iName = nameNode.getNodeValue(); + + Docgen.Debug("Creating interface: " + iName); + iface = new Interface(iName, parent); + + // take care of the attributes + for(int i = 0; i < attrLen; i++){ + Node attrNode = attrList.item(i); + String attrName = attrNode.getNodeName(); + String attrValue = attrNode.getNodeValue(); + if (!attrName.equals("name")){ + Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName() + + "=" + attrValue); + iface.AddAttribute(attrName,attrValue); + } + } + } else if (!isEmptyTextNode(node)){ + fatalNodeError("Unexpected child \"" + nodeName + +"\" node of parent \"" + parent.Name + "\"."); + } + + // recurse over children if both module and layer defined + if (iface != null && parent != null){ + // FIXME: containsKey() check for duplicate + parent.Children.put(iface.Name, iface); + + NodeList children = node.getChildNodes(); + if (children != null){ + int len = children.getLength(); + for (int index = 0; index < len; index++){ + processNode(children.item(index), iface); + } + } + } + } + + /** + * Process children of the interface node (aka parameters, desc., infoflow). + * + * @param node A child node of the policy. + * @param parent The parent PolicyElement. + */ + void processNode(Node node, Interface parent) throws SAXException{ + Parameter param = null; + + // save us from null pointer de-referencing + if (node == null){ + System.err.println( + "Nothing to do, node is null"); + return; + } + + // snag the name + String nodeName = node.getNodeName(); + + // if description node + if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("description")){ + // unfortunately we still need to snag the PCDATA child node for the actual text + NodeList children = node.getChildNodes(); + if (children != null && children.getLength() == 1){ + if (children.item(0).getNodeType() == Node.TEXT_NODE){ + parent.PCDATA = children.item(0).getNodeValue(); + return; + } + } + fatalNodeError("Unexpected child \"" + nodeName + +"\" node of parent \"" + parent.Name + "\"."); + // if infoflow node + } else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("infoflow")){ + NamedNodeMap attrList = node.getAttributes(); + // the required attributes + int attrLen = 0; + if(attrList != null){ + attrLen = attrList.getLength(); + } else{ + fatalNodeError("Missing attribute in infoflow." + + " \"type\" and \"weight\" are required attributes."); + } + + Node typeNode = attrList.getNamedItem("type"); + Node weightNode = attrList.getNamedItem("weight"); + + String type = typeNode.getNodeValue(); + if (typeNode == null || + (!type.equals("none") && weightNode == null)) + fatalNodeError("Missing attribute in infoflow." + + " \"type\" and \"weight\" are required attributes (unless type is none)."); + + if (type.equals("read")){ + parent.Type = InterfaceType.Read; + parent.Weight = Integer.parseInt(weightNode.getNodeValue()); + }else if (type.equals("write")){ + parent.Type = InterfaceType.Write; + parent.Weight = Integer.parseInt(weightNode.getNodeValue()); + }else if (type.equals("both")){ + parent.Type = InterfaceType.Both; + parent.Weight = Integer.parseInt(weightNode.getNodeValue()); + }else if (type.equals("none")){ + parent.Type = InterfaceType.None; + parent.Weight = -1; + } else { + System.err.println("Infoflow type must be read, write, both, or none!"); + } + + } else if (node.getNodeType() == Node.ELEMENT_NODE && nodeName.equals("parameter")){ + NamedNodeMap attrList = node.getAttributes(); + // the required attributes + int attrLen = 0; + if(attrList != null){ + attrLen = attrList.getLength(); + } else{ + fatalNodeError("Missing attribute in parameter \"" + + "\". \"name\" is a required attribute."); + } + + Node nameNode = attrList.getNamedItem("name"); + + if (nameNode == null ) + fatalNodeError("Missing attribute in parameter \"" + + "\". \"name\" is a required attribute."); + + String paramName = nameNode.getNodeValue(); + + Docgen.Debug("Creating parameter: " + paramName); + param = new Parameter(paramName, parent); + + // unfortunately we still need to snag the PCDATA child node for the actual text + NodeList children = node.getChildNodes(); + if (children != null && children.getLength() == 1){ + if (children.item(0).getNodeType() == Node.TEXT_NODE){ + param.PCDATA = children.item(0).getNodeValue(); + } + } else { + fatalNodeError("Unexpected child \"" + +"\" node of parameter."); + } + + // take care of the attributes + for(int i = 0; i < attrLen; i++){ + Node attrNode = attrList.item(i); + String attrName = attrNode.getNodeName(); + String attrValue = attrNode.getNodeValue(); + if (!attrName.equals("name")){ + Docgen.Debug("\tAdding attribute: " + attrNode.getNodeName() + + "=" + attrValue); + param.AddAttribute(attrName,attrValue); + } + } + } else if (!isEmptyTextNode(node)){ + fatalNodeError("Unexpected child \"" + nodeName + +"\" node of parent \"" + parent.Name + "\"."); + } + + // recurse over children if both parent and param defined + if (param != null && parent != null){ + // the containsKey check verified no duplicate module + // FIXME: containsKey() check for duplicate + parent.Children.put(param.Name, param); + } + } + + public boolean isEmptyTextNode(Node node){ + /* + * FIXME: remove once properly validating + * Since we aren't validating yet we needed our + * own pointless whitespace remover. + */ + + if (node.getNodeType() == Node.TEXT_NODE && + node.getNodeValue().trim().length() == 0) + return true; + return false; + } + + public void fatalNodeError(String msg) + throws SAXException { + // FIXME: figure out how to throw SAXParseException w/ location + throw new SAXException(msg); + } +} diff --git a/refpolicy/doc/doctools/src/policy/Interface.java b/refpolicy/doc/doctools/src/policy/Interface.java new file mode 100644 index 0000000..6014adf --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/Interface.java @@ -0,0 +1,37 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Interface.java: The reference policy interfaces + * Version: @version@ + */ +package policy; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Each reference policy interface is represented by this class. + * + * @see Layer + * @see Module + * @see Parameter + */ +public class Interface extends PolicyElement { + /** the children of this element */ + public final Map<String,Parameter> Children; + + public InterfaceType Type; + public int Weight; + + /** + * Default constructor assigns name to module. + * + * @param _name The name of the module. + * @param _Parent The reference to the parent element. + */ + public Interface(String _name, Module _Parent){ + super(_name, _Parent); + Children = new TreeMap<String,Parameter>(); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/InterfaceType.java b/refpolicy/doc/doctools/src/policy/InterfaceType.java new file mode 100644 index 0000000..0a102c5 --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/InterfaceType.java @@ -0,0 +1,12 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Interface.java: The reference policy interface types + * Version: @version@ + */ +package policy; + +public enum InterfaceType { + Read, Write, Both, None; +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/Layer.java b/refpolicy/doc/doctools/src/policy/Layer.java new file mode 100644 index 0000000..b232a38 --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/Layer.java @@ -0,0 +1,34 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Layer.java: The reference policy layers + * Version: @version@ + */ +package policy; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Each reference policy layer is represented by this class. + * + * @see Module + * @see Interface + * @see Parameter + */ +public class Layer extends PolicyElement { + /** the children of this element */ + public final Map<String,Module> Children; + + /** + * Default constructor assigns name to layer. + * + * @param _name The name of the layer. + * @param _Parent The reference to the parent element. + */ + public Layer(String _name, Policy _Parent){ + super(_name, _Parent); + Children = new TreeMap<String, Module>(); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/Module.java b/refpolicy/doc/doctools/src/policy/Module.java new file mode 100644 index 0000000..3bb0c22 --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/Module.java @@ -0,0 +1,34 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Module.java: The reference policy module + * Version: @version@ + */ +package policy; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Each reference policy layer is represented by this class. + * + * @see Layer + * @see Interface + * @see Parameter + */ +public class Module extends PolicyElement { + /** the children of this element */ + public final Map<String,Interface> Children; + + /** + * Default constructor assigns name to module. + * + * @param _name The name of the module. + * @param _Parent The reference to the parent element. + */ + public Module(String _name, Layer _Parent){ + super(_name, _Parent); + Children = new TreeMap<String,Interface>(); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/Parameter.java b/refpolicy/doc/doctools/src/policy/Parameter.java new file mode 100644 index 0000000..0f0ee45 --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/Parameter.java @@ -0,0 +1,28 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Parameter.java: The reference policy interface parameters + * Version: @version@ + */ +package policy; + +/** + * Each reference policy layer is represented by this class. + * + * @see Layer + * @see Module + * @see Interface + */ +public class Parameter extends PolicyElement{ + + /** + * Default constructor assigns name to parameter. + * + * @param _name The name of the parameter. + * @param _Parent The reference to the parent element. + */ + public Parameter(String _name, Interface _Parent){ + super(_name, _Parent); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/Policy.java b/refpolicy/doc/doctools/src/policy/Policy.java new file mode 100644 index 0000000..e17ff1f --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/Policy.java @@ -0,0 +1,35 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * Policy.java: The reference policy api + * Version: @version@ + */ +package policy; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Each reference policy layer is represented by this class. + * + * @see Module + * @see Interface + * @see Parameter + */ +public class Policy extends PolicyElement { + /** the children of this element */ + public final Map<String,Layer> Children; + + /** + * Default constructor assigns name to layer. + * + * @param _name The name of the layer. + * @param _Parent The reference to the parent element. + */ + public Policy(String _name){ + // the policy is the root element so parent==null + super(_name, null); + Children = new TreeMap<String,Layer>(); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/src/policy/PolicyElement.java b/refpolicy/doc/doctools/src/policy/PolicyElement.java new file mode 100644 index 0000000..f5fc913 --- /dev/null +++ b/refpolicy/doc/doctools/src/policy/PolicyElement.java @@ -0,0 +1,114 @@ +/* Copyright (C) 2005 Tresys Technology, LLC + * License: refer to COPYING file for license information. + * Authors: Spencer Shimko <sshimko@tresys.com> + * + * PolicyElement.java: The reference policy base class + * Version: @version@ + */ +package policy; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Reference policy elements have certain similarties which + * should be inherited from a base class. This includes a name + * attribute that will be used for hashing, sorting, and converting + * to a string. + * + * @see Layer + * @see Module + * @see Interface + * @see Parameter + */ +public abstract class PolicyElement { + /** the string identifying the element */ + public final String Name; + /** the parent component */ + public final PolicyElement Parent; + + /** the attributes of this element */ + private final Map<String,String> attributes; + + public String PCDATA; + + /** + * Default constructor assigns name. + * + * @param _name The name of the element. + * @param _Parent The reference to the parent element. + */ + public PolicyElement(String _name, PolicyElement _Parent){ + Name = _name; + Parent = _Parent; + attributes = new TreeMap<String,String>(); + } + + /** + * Add attribute to policy element. + * + * @param aName String to identify attribute + * @param aString Value of attribute identified by string aName + * @return <code>true</code> when attribute added to element, + * <code>false</code> when attribute already defined for element + */ + public boolean AddAttribute(String aName, String aString){ + if (attributes.containsKey(aName)){ + return false; + } + attributes.put(aName,aString); + return true; + } + + /** + * Get attribute from policy element. + * + * @param aName String to identify attribute + * @return String value associated with named attribute + * or <code>null</code> if no attribute defined + * for aName. + */ + public String GetAttribute(String aName){ + return attributes.get(aName); + } + + /** + * Overridden equals method + * + * @param obj Object for comparison + */ + @Override public boolean equals(Object obj){ + return obj.toString().equals(this.Name); + } + + /** + * Return a hashcode for the element. Currently implemented as a + * call to the String hashCode() method. + * + * @return integer hashCode of this instance assuring two objects that + * are == will return same hashcode. + */ + @Override public int hashCode() { + return this.toString().hashCode(); + } + + /** + * Overridden toString() method. + * + * @return String representation of instance (it's name). + */ + @Override public String toString(){ + return Name; + } + + /** + * Since Component might be used as a key in some type of sort Compare + * is implemented. + * + * @return 0 if this==that, <0 if this<that, >0 if this>that + * @see String.compareTo + */ + public int compareTo(Layer that){ + return Name.compareTo(that.Name); + } +} \ No newline at end of file diff --git a/refpolicy/doc/doctools/style.css b/refpolicy/doc/doctools/style.css new file mode 100644 index 0000000..3805bf8 --- /dev/null +++ b/refpolicy/doc/doctools/style.css @@ -0,0 +1,152 @@ +body { + margin:0px; + padding:0px; + font-family:verdana, arial, helvetica, sans-serif; + color:#333; + background-color:white; + } +h1 { + margin:0px 0px 15px 0px; + padding:0px; + font-size:28px; + line-height:28px; + font-weight:900; + color:#ccc; + } +h2 { + font-size:100%; + } +h3 { + font-size:75%; + } +h4 { + font-size:67%; + } +li { + font:11px/20px verdana, arial, helvetica, sans-serif; + margin:0px 0px 0px 0px; + padding:0px; + } +p { + /* normal */ + font:11px/20px verdana, arial, helvetica, sans-serif; + margin:0px 0px 16px 0px; + padding:0px; + } + +tt { + /* inline code */ + font-family: monospace; + } + +table { + background-color:#eee; + border:1px dashed #999; + /*background-color: white;*/ + color: black; + text-align: left; + font:11px/20px verdana, arial, helvetica, sans-serif; + margin-left: 10%; + margin-right: 10%; +} + +th { + background-color: #ccccff; + text-align: center; +} + +td.header { + font-weight: bold; +} + +#Content>p {margin:0px;} +#Content>p+p {text-indent:30px;} +a { + color:#09c; + font-size:11px; + text-decoration:none; + font-weight:600; + font-family:verdana, arial, helvetica, sans-serif; + } +a:link {color:#09c;} +a:visited {color:#07a;} +a:hover {background-color:#eee;} + +#Codeblock { + margin:5px 50px 5px 50px; + padding:5px 0px 5px 15px; + border-style:solid; + border-color:black; + border-width:1px 1px 1px 1px; + background-color:#f8f8f8; + font-size:11px; + font-weight:600; + text-decoration:none; + font-family:courier; +} +pre { + font-size:11px; + font-weight:600; + text-decoration:none; + font-family:courier; +} +pre.codeblock { + /* code block (bordered, slight gray background) */ + border-style:solid; + border-color:black; + border-width:1px 1px 1px 1px; + background-color:#f8f8f8; + margin-left: 10%; + margin-right: 10%; +} +dl { + /* definition text block */ + font:11px/20px verdana, arial, helvetica, sans-serif; + margin:0px 0px 16px 0px; + padding:0px; + } +dt { + /* definition term */ + font-weight: bold; + } + +#Header { + margin:50px 0px 10px 0px; + padding:17px 0px 0px 20px; + /* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */ + height:33px; /* 14px + 17px + 2px = 33px */ + border-style:solid; + border-color:black; + border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */ + line-height:11px; + font-size:110%; + background-color:#eee; + voice-family: "\"}\""; + voice-family:inherit; + height:14px; /* the correct height */ + } +body>#Header {height:14px;} +#Content { + margin:0px 50px 50px 200px; + padding:10px; + } + +#Menu { + position:absolute; + top:100px; + left:20px; + width:162px; + padding:10px; + background-color:#eee; + border:1px dashed #999; + line-height:17px; + text-align:left; + voice-family: "\"}\""; + voice-family:inherit; + width:160px; + } +#Menu subitem { + font-size: 5px; +} + +body>#Menu {width:160px;}