--- /dev/null
+'''
+Created on Jan 31, 2010
+
+@author: tsitkova
+'''
+import re
+import copy
+import yaml
+
+class ConfParser(object):
+ def __init__(self, path):
+ self.configuration = self._parse(path)
+
+ def walk(self):
+ for trio in self._walk(self.configuration):
+ yield trio
+
+ def _parse(self, path):
+ comment_pattern = re.compile(r'(\s*[#].*)')
+ section_pattern = re.compile(r'^\s*\[(?P<section>\w+)\]\s+$')
+ empty_pattern = re.compile(r'^\s*$')
+ equalsign_pattern = re.compile(r'=')
+
+ section = None
+ parser_stack = list()
+ result = dict()
+ value = None
+ f = open(path, 'r')
+ for (ln,line) in enumerate(f):
+ line = comment_pattern.sub('', line)
+ line = equalsign_pattern.sub(' = ',line,count=1)
+ if empty_pattern.match(line) is not None:
+ continue
+ m = section_pattern.match(line)
+ if m is not None:
+ section = m.group('section')
+ value = dict()
+ result[section] = value
+ continue
+ if section is None:
+ msg = 'Failed to determine section for line #%i' % ln
+ raise ValueError(msg)
+ try:
+ value = self._parseLine(value, line, parser_stack)
+ except:
+ print 'Error while parsing line %i: %s' % (ln+1, line)
+ raise
+ f.close()
+
+ if len(parser_stack):
+ raise 'Parsing error.'
+
+ return result
+
+ def _parseLine(self, value, content, stack):
+ token_pattern = re.compile(r'(?P<token>\S+)(?=\s+)')
+ attr = None
+ token_stack = list()
+
+ for m in token_pattern.finditer(content):
+ token = m.group('token')
+ if not self._validate(token):
+ raise ValueError('Invalid token %s' % token)
+ if token == '=':
+ if len(token_stack) == 0:
+ raise ValueError('Failed to find attribute.')
+ elif len(token_stack) == 1:
+ attr = token_stack.pop()
+ else:
+ value[attr] = token_stack[:-1]
+ attr = token_stack[-1]
+ token_stack = list()
+ elif token == '{':
+ if attr is None:
+ raise ValueError('Failed to find attribute.')
+ stack.append((attr,value))
+ value = dict()
+ elif token == '}':
+ if len(stack) == 0:
+ raise ValueError('Failed to parse: unbalanced braces')
+ if len(token_stack):
+ if attr is None:
+ raise ValueError('Missing attribute')
+ value[attr] = token_stack
+ attr = None
+ token_stack = list()
+ (attr,parent_value) = stack.pop()
+ parent_value[attr] = value
+ value = parent_value
+ else:
+ token_stack.append(token)
+ if len(token_stack):
+ if attr is None:
+ raise ValueError('Missing attribute')
+ value[attr] = token_stack
+
+ return value
+
+ def _validate(self, token):
+ result = True
+ for s in ['{','}']:
+ if s in token and s != token:
+ result = False
+
+ return result
+
+ def _walk(self, parsedData, path='root'):
+ dirs = list()
+ av = list()
+ for (key, value) in parsedData.iteritems():
+ if type(value) == dict:
+ new_path = path + '.' + key
+ for trio in self._walk(value, new_path):
+ yield trio
+ dirs.append(key)
+ else:
+ av.append((key,value))
+ yield (path, dirs, av)
+
+
+
+class ConfParserTest(ConfParser):
+ def __init__(self):
+ self.conf_path = '../tests/krb5.conf'
+ super(ConfParserTest, self).__init__(self.conf_path)
+
+ def run_tests(self):
+ self._test_walk()
+
+ def _test_parse(self):
+ result = self._parse(self.conf_path)
+ print yaml.dump(result)
+
+ def _test_walk(self):
+ configuration = self._parse(self.conf_path)
+ for (path,dirs,av) in self.walk():
+ print path,dirs,av
+
+
+
+
+if __name__ == '__main__':
+ tester = ConfParserTest()
+ tester.run_tests()
--- /dev/null
+'''
+Created on Jan 25, 2010
+
+@author: tsitkova
+'''
+import os
+import sys
+import re
+import yaml
+from optparse import OptionParser
+from confparser import ConfParser
+
+class Rule(object):
+ def __init__(self):
+ pass
+
+ def validate(self,node):
+ (path,dirs,avs) = node
+
+
+class Validator(object):
+ def __init__(self, kerberosPath, confPath=None, rulesPath=None, hfilePath=None):
+ self.parser = ConfParser(kerberosPath)
+ if confPath is not None:
+ content = self._readConfigFile(confPath)
+ rulesPath = content['RulesPath']
+ hfilePath = content['HfilePath']
+ if rulesPath is not None and hfilePath is not None:
+ self.rules = self._loadRules(rulesPath)
+ self.validKeys = SupportedKeys(hfilePath).validKeys.union(self.rules['Attributes'])
+ else:
+ raise ValueError('Invalid arguments for validator: no path to rules and definition files')
+
+ self._attribute_pattern = re.compile(r'^\w+$')
+ self._lowercase_pattern = re.compile(r'[a-z]')
+
+ def _readConfigFile(self,path):
+ f = open(path)
+ result = dict()
+ for line in f:
+ line = line.rstrip()
+ fields = line.split('=')
+ result[fields[0]] = fields[1]
+
+ return result
+
+ def _loadRules(self, path):
+ f = open(path)
+ rules = yaml.load(f)
+ f.close()
+
+ return rules
+
+ def validate(self):
+ typeInfo = self.rules['Types']
+
+ for node in self.parser.walk():
+ self._validateTypes(node, typeInfo)
+ self._validateAttrubutes(node, self.validKeys)
+ # self._validateRealm(node)
+
+
+ def _validateTypes(self, node, typeInfo):
+ (path, dirs, avs) = node
+ for (key, value) in avs:
+ valid_type_pattern = typeInfo.get(key)
+ if valid_type_pattern is not None:
+ for t in value:
+ if re.match(valid_type_pattern, t) is None:
+ print 'Wrong type %s for attribute %s.%s' % (t,path,key)
+
+ def _validateAttrubutes(self, node, validKeys):
+ (path, dirs, avs) = node
+ attributes = list()
+ for attr in dirs:
+ if self._attribute_pattern.match(attr) is not None:
+ attributes.append(attr)
+ for (attr, value) in avs:
+ if self._attribute_pattern.match(attr) is not None:
+ attributes.append(attr)
+
+ for attr in attributes:
+ if attr not in validKeys:
+ print 'Unrecognized attribute %s at %s' % (attr, path)
+
+# def _validateRealm(self, node):
+# (path, dirs, avs) = node
+# if path == 'root.realms':
+# for attr in dirs:
+# if self._lowercase_pattern.search(attr) is not None:
+# print 'Lower case letter in realm attribute: %s at %s' % (attr, path)
+
+class SupportedKeys(object):
+ def __init__(self, path):
+ self.validKeys = self.getKeysFromHfile(path)
+
+ def getKeysFromHfile(self, path):
+ pattern = re.compile(r'^[#]define KRB5_CONF_\w+\s+["](\w+)["]')
+ f = open(path)
+ result = set()
+ for l in f:
+ l = l.rstrip()
+ m = pattern.match(l)
+ if m is not None:
+ result.add(m.groups()[0])
+ f.close()
+
+ return result
+
+
+class ValidatorTest(Validator):
+ def __init__(self):
+ self.kerberosPath = '../tests/kdc1.conf'
+ self.rulesPath = '../tests/rules.yml'
+ self.hfilePath = '../tests/k5-int.h'
+ self.confPath = '../tests/validator.conf'
+
+ super(ValidatorTest, self).__init__(self.kerberosPath,
+ rulesPath=self.rulesPath,
+ hfilePath=self.hfilePath)
+
+ def run_tests(self):
+ self._test_validate()
+
+ def _test__loadRules(self):
+ result = self._loadRules(self.rulesPath)
+ print result
+
+ def _test_validate(self):
+ self.validate()
+
+ def _test__readConfigFile(self):
+ result = self._readConfigFile(self.confPath)
+ print result
+
+class SupportedKeysTest(SupportedKeys):
+ def __init__(self):
+ self.path = '../tests/k5-int.h'
+
+ def run_tests(self):
+ self._test_getKeysFromHFile()
+
+ def _test_getKeysFromHFile(self):
+ result = set()
+ krb5keys = self.getKeysFromHfile(self.path)
+ for key in krb5keys:
+ print key
+ result.update(key)
+ print len(krb5keys)
+
+ return result
+
+def _test():
+ tester = ValidatorTest()
+ krb5keys = tester.run_tests()
+
+if __name__ == '__main__':
+ TEST = False
+ if TEST:
+ _test()
+ sys.exit()
+
+
+ usage = "\n\t%prog path [-d defPath] [-r rulesPath] [-c validatorConfPath]"
+ description = 'Description: validates kerberos configuration file'
+ parser = OptionParser(usage = usage, description = description)
+ parser.add_option("-c", dest="confPath",
+ help='path to validator config file')
+ parser.add_option("-d", dest="hfilePath",
+ help='path to h-file with attribute definition')
+ parser.add_option("-r", dest="rulesPath",
+ help='path to file with validation rules')
+ (options, args) = parser.parse_args()
+
+ if len(args) != 1 and len(sys.argv) <= 3:
+ print '\n%s' % parser.get_usage()
+ sys.exit()
+
+ validator = None
+ if options.confPath is not None:
+ validator = Validator(args[0], confPath=options.confPath)
+ elif options.hfilePath is not None and options.rulesPath is not None:
+ validator = Validator(args[0], hfilePath=options.hfilePath, rulesPath=options.rulesPath)
+ else:
+ print '\nMust specify either configuration file or paths to rules and definitions files'
+ print '%s' % parser.get_usage()
+ sys.exit()
+
+ validator.validate()
+
+
+
+
+