Files

475 lines
17 KiB
Python

#
# Copyright 2023 United States Government
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import sys
from xml.dom import minidom
from xml.parsers.expat import ExpatError, ErrorString
class asp(object):
def __init__(self):
self.name = 'none'
self.uuid = ''
self.aspType = ''
self.desc = ''
self.detaileddesc = ''
self.inpdesc = ''
self.outdesc = ''
self.usage = ''
self.aspfile = ''
self.measurers = []
self.measurersdesc = ''
self.targettype = ''
self.targetmagic = ''
self.targetdesc = ''
self.addrtype = ''
self.addrmagic = ''
self.addrdesc = ''
self.meastype = ''
self.measmagic =''
self.measdesc = ''
self.feature = ''
self.filename = ''
self.selinuxtype = ''
self.seealso = ''
self.example = ''
self.user = ''
# Variables
ASPsLocation = sys.argv[1]
allAsps = []
demoAsps = []
networkAsps = []
fileAsps = []
processAsps = []
systemAsps = []
appraisalAsps = []
kernelAsps = []
def printHeader( ):
"This Function Prints SPHINX Header to Standard Output"
print ('Attestation Service Providers (ASPs)')
print ('_____________________________________________________\n')
print ('')
# print ('.. currentmodule:: maat\n')
print ('Attestation Service Providers (ASPs) are the basic func' +
'tional unit of Maat. Each ASP performs a specific, discrete' +
' function in evidence collection tasks. An ASP may be a' +
' measurement agent responsible for gathering a specific piece' +
' of evidence from the system. An ASP may also be a evaluation' +
' unit that ingests some type of evidence and contributes to an' +
' assessment of the target\'s integrity. Other ASPs may provide' +
' post-processing functions such as hashing or compression, or' +
' call out to external components such as another AM or an' +
' existing service.\n')
return
def makeASPLink(name, uuid):
"Helper Function to create url link giving name and magic number"
if (name == ""):
return "";
return """:ref:`""" + name + """ <""" + uuid + """>`"""
def makeUrlLink(name, magic):
"Helper Function to create url link giving name and magic number"
if (name == ""):
return "";
return """:ref:`""" + name + """ <Magic""" + magic + """>`"""
def printASPTableHeader():
nc = 20
dc = 70
ic = 70
oc = 70
nameColumn = "=" * nc;
descColumn = "=" * dc;
inputColumn = "=" * ic;
outputColumn = "=" * oc;
# print('.. tabularcolumns:: |p{6.35cm}|p{6.35cm}|p{6.35cm}|\n');
print('.. table:: ' + "ASPs " + "\n" +
'\t:widths: 20, 70, 70\n' +
'\t:column-alignment: left left left \n' +
'\t:column-wrapping: true true true\n' +
'\t:column-dividers: single single single single\n' +
'\n' +
'\t' + nameColumn + ' ' + descColumn + ' ' + inputColumn + "\n" +
'\tType' + (" " * (nc-4)) + ' ' + 'Name' + (" " * (dc -4)) + ' Description \n' +
'\t' + nameColumn + ' ' + descColumn + ' ' + inputColumn)
#print('\n' +
# ' \t' + nameColumn + ' ' + descColumn + ' ' + inputColumn + "\n" +
# ' \tName' + (" " * (nc-4)) + ' ' + 'Description' + (" " * (dc -11)) + ' Type \n' +
# ' \t' + nameColumn + ' ' + descColumn + ' ' + inputColumn)
return
def printASPTableFooter():
nc = 20
dc = 70
ic = 70
oc = 70
nameColumn = "=" * nc;
descColumn = "=" * dc;
inputColumn = "=" * ic;
outputColumn = "=" * oc;
print(' \t' + nameColumn + ' ' + descColumn + ' ' + inputColumn + "\n");
return
def printASPsTable(title, asps):
"Print Table of list of asps provided with Name, Description, Input and Ouptut as Columns"
nc = 20
dc = 70
ic = 70
oc = 70
nameColumn = "=" * nc;
descColumn = "=" * dc;
inputColumn = "=" * ic;
outputColumn = "=" * oc;
# print('.. table:: ' + title + "\n" +
# '\t:widths: 1 1 1\n' +
# '\t:column-alignment: left left left \n' +
# '\t:column-wrapping: true true true\n' +
# '\t:column-dividers: single single single single\n' +
# '\n' +
# '\t' + nameColumn + ' ' + descColumn + ' ' + inputColumn + "\n" +
# '\tName' + (" " * (nc-4)) + ' ' + 'Description' + (" " * (dc -11)) + ' Type \n' +
# '\t' + nameColumn + ' ' + descColumn + ' ' + inputColumn)
for asp in asps:
# Build Table contents
# ASP has up to 4 rows
# Row 1 -> ASP Name, Desc(0.50), graph{, graph{
# Row 2 -> ,Desc(50,100), address_space, measurement_type
# Row 3 -> ,Desc(100,101), target_type, }
# Row 4 -> ,Desc(101,150), } nodeid,
# Sphynx may combine some of these fields to reduce rows
# build row 1
name = " " * ic # Pad name field, so table lines up
asplink = makeASPLink(asp.name, asp.uuid)
name = asplink + name[len(asplink):] + " "
desc = " " * dc # Pad desc field, so table lines up
adesc = asp.desc[0:dc].lstrip()
adesc = adesc.replace("\n","");
adesc = adesc.replace("\t", "");
desc = adesc + desc[len(adesc):] + " "
inp = " " * ic
addr = makeUrlLink(asp.addrtype, asp.addrmagic)
tmp = "graph {" + addr
inp = tmp + inp[len(tmp):] + " "
out = " " * oc
meas = makeUrlLink(asp.meastype, asp.measmagic)
btype = " " * nc
atype = asp.aspType + btype[len(asp.aspType):] + " "
if (meas != ""):
tmp = "measurement: " + meas
else:
tmp = ''
out = tmp + out[len(tmp):]
row1 = "\t"+ atype + name + desc
# build row 2
name = " " * ic # No Name in Row 2
name = name + " "
desc = " " * dc # Pad desc field, so table lines up
adesc = asp.desc[dc:dc*2].lstrip()
adesc = adesc.replace("\n","");
adesc = adesc.replace("\t", "");
desc = adesc + desc[len(adesc):] + " "
inp = " " * ic
target = makeUrlLink(asp.targettype, asp.targetmagic)
tmp = ", " + target + "}, nodeid"
inp = tmp + inp[len(tmp):]
if (meas != ""):
out = " added to input node "
else:
out = ' '
atype = " " * nc
row2 = "\t" + atype + " " + name + desc
# build row 3
name = " " * ic # No Name in Row 3
name = name + " "
desc = " " * dc # Pad desc field, so table lines up
adesc = asp.desc[2*dc:3*dc].lstrip()
adesc = adesc.replace("\n","");
adesc = adesc.replace("\t","");
desc = adesc + desc[len(adesc):] + " "
inp = " "
out = " "
atype = " " * nc
row3 = "\t" + atype + " " + name + desc
# build row 4
name = " " * ic # No Name in Row 4
name = name + " "
desc = " " * dc # Pad desc field, so table lines up
adesc = asp.desc[3*dc:4*dc].lstrip()
adesc = adesc.replace("\t", "");
adesc = adesc.replace("\n","");
desc = adesc + desc[len(adesc):] + " "
inp = " "
out = " "
atype = " " * nc
row4 = "\t" + atype + " " + name + desc
print(" " + row1);
print(" " + row2);
print(" " + row3);
print(" " + row4);
# print('\t' + nameColumn + ' ' + descColumn + ' ' + inputColumn + "\n");
return
def printASPInfo(asp):
"Print XML Info"
print('.. rst-class:: html-toggle');
print ('.. _' + asp.uuid + ':\n\n' + asp.name)
print ('*********************************************************')
if (asp.targettype != ''):
asp.detaileddesc = asp.detaileddesc.replace(asp.targettype, """:ref:`""" + asp.targettype + """ <""" + "Magic" + asp.targetmagic + """>`""")
if (asp.addrtype != ''):
asp.detaileddesc = asp.detaileddesc.replace(asp.addrtype, """:ref:`""" + asp.addrtype + """ <""" + "Magic" + asp.addrmagic + """>`""")
if (asp.meastype != ''):
asp.detaileddesc = asp.detaileddesc.replace(asp.meastype, """:ref:`""" + asp.meastype + """ <""" + "Magic" + asp.measmagic + """>`""")
print(asp.detaileddesc);
print("Usage: ");
print(asp.usage);
print("Description: \n");
print("\t" + asp.desc + "\n");
print("Input Description: " );
print(asp.inpdesc);
print("Output Description: ");
print(asp.outdesc);
if (asp.example != ''):
print("Example:: ");
print(asp.example);
print(".. seealso::");
print(asp.seealso);
print('| Filename: ' + asp.filename + '.c')
print('| UUID: ' + asp.uuid)
print('| Description: \n| \t' + asp.desc)
print('| Measurers: ')
for name in asp.measurers:
print('| \t' + name)
if (asp.measurersdesc != ''):
print("| \t\tNote: " + asp.measurersdesc);
print('| \tCapabilities:');
if (asp.targetmagic != ''):
print("| \t\tTarget Type: " + makeUrlLink(asp.targettype, asp.targetmagic));
if (asp.targetdesc != ''):
print("| \t\t\t\tDescription: " + asp.targetdesc)
if (asp.addrmagic != '' ):
print("| \t\tAddress Type: " + makeUrlLink(asp.addrtype, asp.addrmagic));
if (asp.addrdesc != ''):
print("| \t\t\t\tDescription: " + asp.addrdesc)
if (asp.measmagic != ''):
print("| \t\tMeasurement Type: " + makeUrlLink(asp.meastype, asp.measmagic));
if (asp.measdesc != ''):
print("| \t\t\t\tDescription: " + asp.measdesc)
if (asp.feature != ''):
print("| \t\t\t\tFeature: " + asp.feature)
print("| Security: ")
print('| \tType: ' + asp.selinuxtype)
print('| \tUser: ' + asp.user)
print('')
return
def parseASPXml( filepath ):
"Parse ASP XML"
doc = parseXml( filepath );
if (doc is not None):
parsedasp = parseASPFields(doc)
return parsedasp
return None
def getElementData(minidoc, fieldname):
"Get first child data of element matching fieldname (if present)"
fs = minidoc.getElementsByTagName(fieldname)
if (fs.length > 0):
return minidoc.getElementsByTagName(fieldname)[0].firstChild.data
return ""
def parseXml( filepath ):
"minidom xml parsing"
try:
doc = minidom.parse(filepath)
return doc
except IOError as e:
print("I/O error ({0}): {1}".format(e.errno, e.strerror))
return None
except ExpatError as expatError:
print("Parse ASP Error for: " + filepath)
error_msg = ErrorString(expatError.code)
error_msg += ' at line ' + str(expatError.lineno) + ' column ' + str(expatError.offset)
print(error_msg);
return None
except AttributeError as ae:
print(ae)
return None
except:
print("Parse ASP: " + filepath + " Unexpected error:" + sys.exc_info()[0])
return None
def parseASPFields(doc):
"Parse ASP Fields from minidom xml object"
parsedasp = asp();
try:
# ASP meta data
parsedasp.name = getElementData(doc, "name")
parsedasp.uuid = getElementData(doc, "uuid")
parsedasp.aspType = getElementData(doc, "type")
parsedasp.filename = getElementData(doc, "aspfile")
# ASP documentation
parsedasp.detaileddesc = getElementData(doc, "detaileddescription")
parsedasp.desc = getElementData(doc, "description")
parsedasp.example = getElementData(doc, "example")
parsedasp.usage = getElementData(doc, "usage")
parsedasp.inpdesc = getElementData(doc, "inputdescription")
parsedasp.outdesc = getElementData(doc, "outputdescription")
parsedasp.seealso = getElementData(doc, "seealso").replace(" ", "\n\n\t\t")
measurers = doc.getElementsByTagName("measurers")[0]
satisfiers = measurers.getElementsByTagName("satisfier")
if (satisfiers.length > 0):
satisfier = measurers.getElementsByTagName("satisfier")[0]
measurersdesc = measurers.getElementsByTagName("description")
if (measurersdesc.length > 0):
d = measurers.getElementsByTagName("description")[0]
parsedasp.measurersdesc = d.firstChild.data
measnames = satisfier.getElementsByTagName("value")
for name in measnames:
parsedasp.measurers.append(name.firstChild.data)
capabilities = satisfier.getElementsByTagName("capability")
for capability in capabilities:
if (capability.hasAttribute("target_type")):
parsedasp.targettype = capability.getAttribute("target_type")
if (capability.hasAttribute("target_desc")):
parsedasp.targetdesc = capability.getAttribute("target_desc")
if (capability.hasAttribute("target_magic")):
parsedasp.targetmagic = capability.getAttribute("target_magic")
if (capability.hasAttribute("address_type")):
parsedasp.addrtype = capability.getAttribute("address_type")
if (capability.hasAttribute("address_desc")):
parsedasp.addrdesc = capability.getAttribute("address_desc")
if (capability.hasAttribute("address_magic")):
parsedasp.addrmagic = capability.getAttribute("address_magic")
if (capability.hasAttribute("measurement_type")):
parsedasp.meastype = capability.getAttribute("measurement_type")
if (capability.hasAttribute("measurement_desc")):
parsedasp.measdesc = capability.getAttribute("measurement_desc")
if (capability.hasAttribute("measurement_magic")):
parsedasp.measmagic = capability.getAttribute("measurement_magic")
if (capability.hasAttribute("attribute")):
parsedasp.feature = capability.getAttribute("attribute")
# update links
if (parsedasp.targettype != ''):
parsedasp.inpdesc = parsedasp.inpdesc.replace(parsedasp.targettype, """:ref:`""" + parsedasp.targettype + """ <""" + "Magic" + parsedasp.targetmagic + """>`""")
if (parsedasp.addrtype != ''):
parsedasp.inpdesc = parsedasp.inpdesc.replace(parsedasp.addrtype, """:ref:`""" + parsedasp.addrtype + """ <""" + "Magic" + parsedasp.addrmagic + """>`""")
if (parsedasp.meastype != ''):
parsedasp.outdesc = parsedasp.outdesc.replace(parsedasp.meastype, """:ref:`""" + parsedasp.meastype + """ <""" + "Magic" + parsedasp.measmagic + """>`""")
securitys = doc.getElementsByTagName("security_context")
for security in securitys:
selinux = security.getElementsByTagName("selinux")
if (selinux.length > 0):
selinux = security.getElementsByTagName("selinux")[0]
types = selinux.getElementsByTagName("type")
if (types.length > 0):
field = selinux.getElementsByTagName("type")[0]
parsedasp.selinuxtype = field.firstChild.data
user = security.getElementsByTagName("user")
if (user.length > 0):
field = security.getElementsByTagName("user")[0]
parsedasp.user = field.firstChild.data
return parsedasp
except AttributeError as ae:
print(ae)
return None
except:
print("Unexpected error: " + sys.exc_info()[0])
return None
return parsedasp
# Print SPHYNX Header
printHeader()
# Parse and Organize ASPs
for root, directories, files in os.walk(ASPsLocation):
for filename in sorted(files):
if filename.endswith(".xml"):
#print("Parsing File :" + filename + "\n");
parsedasp = parseASPXml(ASPsLocation + filename);
if parsedasp is None:
break;
allAsps.append(parsedasp)
if parsedasp.aspType == "Appraisal":
appraisalAsps.append(parsedasp)
if parsedasp.aspType == "Demonstration":
demoAsps.append(parsedasp)
if parsedasp.aspType == "Kernel":
kernelAsps.append(parsedasp)
if parsedasp.aspType == "Network":
networkAsps.append(parsedasp)
if parsedasp.aspType == "File":
fileAsps.append(parsedasp)
if parsedasp.aspType == "Process":
processAsps.append(parsedasp)
if parsedasp.aspType == "System":
systemAsps.append(parsedasp)
break; # only top folder, break before recursively checking test folders
printASPTableHeader();
printASPsTable("Appraisal ASPs", appraisalAsps);
printASPsTable("File ASPs", fileAsps);
printASPsTable("Kernel ASPs", kernelAsps);
printASPsTable("Network ASPs", networkAsps);
printASPsTable("Process ASPs", processAsps);
printASPsTable("System ASPs", systemAsps);
printASPTableFooter();