policy.py revision f6096b958c8b58c4709860d7c4dcdde5deeacb7a
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
45c5f403619029a363cf089e0a4b1bb44425dd84Tinderbox User# Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews# Permission to use, copy, modify, and/or distribute this software for any
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews# purpose with or without fee is hereby granted, provided that the above
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews# copyright notice and this permission notice appear in all copies.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
2f4561bc9cd5e5cdc58e29e600303c812f6902eeAutomatic Updater# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# PERFORMANCE OF THIS SOFTWARE.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# policy.py
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# This module implements the parser for the dnssec.policy file.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
99f6179191e583d23f3c5567d3c00b57b64eb52dEvan Huntimport re
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntimport ply.lex as lex
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntimport ply.yacc as yacc
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntfrom string import *
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntfrom copy import copy
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
39f2d1a96a7c7494b1db0ea0f45e063a6a5ef9bbEvan Hunt# PolicyLex: a lexer for the policy file syntax.
99f6179191e583d23f3c5567d3c00b57b64eb52dEvan Hunt############################################################################
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntclass PolicyLex:
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt reserved = ('POLICY',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'ALGORITHM_POLICY',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'ZONE',
4eb998928b9aef0ceda42d7529980d658138698aEvan Hunt 'ALGORITHM',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'DIRECTORY',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'KEYTTL',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'KEY_SIZE',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'ROLL_PERIOD',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'PRE_PUBLISH',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'POST_PUBLISH',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'COVERAGE',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'STANDBY',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'NONE')
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt tokens = reserved + ('DATESUFFIX',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'KEYTYPE',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'ALGNAME',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'STR',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'QSTRING',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'NUMBER',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'LBRACE',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'RBRACE',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'SEMI')
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt reserved_map = {}
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt t_ignore = ' \t'
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt t_ignore_olcomment = r'(//|\#).*'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t_LBRACE = r'\{'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t_RBRACE = r'\}'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t_SEMI = r';';
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_newline(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'\n+'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.lexer.lineno += t.value.count("\n")
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_comment(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'/\*(.|\n)*?\*/'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.lexer.lineno += t.value.count('\n')
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_DATESUFFIX(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'(?i)(?<=[0-9 \t])(y(?:ears|ear|ea|e)?|mo(?:nths|nth|nt|n)?|w(?:eeks|eek|ee|e)?|d(?:ays|ay|a)?|h(?:ours|our|ou|o)?|mi(?:nutes|nute|nut|nu|n)?|s(?:econds|econd|econ|eco|ec|e)?)\b'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.value = re.match(r'(?i)(y|mo|w|d|h|mi|s)([a-z]*)', t.value).group(1).lower()
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt return t
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_KEYTYPE(self, t):
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt r'(?i)\b(KSK|ZSK)\b'
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt t.value = t.value.upper()
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt return t
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt def t_ALGNAME(self, t):
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt r'(?i)\b(RSAMD5|DH|DSA|NSEC3DSA|ECC|RSASHA1|NSEC3RSASHA1|RSASHA256|RSASHA512|ECCGOST|ECDSAP256SHA245|ECDSAP384SHA384)\b'
79ce3a9e82384cc31fd6b86be8f3d1474fcfd9f4Evan Hunt t.value = t.value.upper()
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt return t
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_STR(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'[A-Za-z._-][\w._-]*'
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt t.type = self.reserved_map.get(t.value, "STR")
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt return t
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_QSTRING(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'"([^"\n]|(\\"))*"'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.type = self.reserved_map.get(t.value, "QSTRING")
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.value = t.value[1:-1]
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt return t
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def t_NUMBER(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt r'\d+'
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.value = int(t.value)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt return t
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt def t_error(self, t):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt print("Illegal character '%s'" % t.value[0])
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t.lexer.skip(1)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def __init__(self, **kwargs):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt for r in self.reserved:
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt self.reserved_map[r.lower().translate(maketrans('_', '-'))] = r
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.lexer = lex.lex(object=self, **kwargs)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt def test(self, text):
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt self.lexer.input(text)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt while True:
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt t = self.lexer.token()
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt if not t:
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt break
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt print(t)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt# Policy: this object holds a set of DNSSEC policy settings.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt############################################################################
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntclass Policy:
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt is_zone = False
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt is_alg = False
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt is_constructed = False
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt ksk_rollperiod = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt zsk_rollperiod = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt ksk_prepublish = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt zsk_prepublish = None
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt ksk_postpublish = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt zsk_postpublish = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt ksk_keysize = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt zsk_keysize = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt ksk_standby = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt zsk_standby = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt keyttl = None
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt coverage = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt directory = None
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt valid_key_sz_per_algo = {'DSA': [512, 1024],
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'NSEC3DSA': [512, 1024],
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'RSAMD5': [512, 4096],
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt 'RSASHA1': [512, 4096],
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'NSEC3RSASHA1': [512, 4096],
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'RSASHA256': [512, 4096],
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'RSASHA512': [512, 4096],
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'ECCGOST': None,
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'ECDSAP256SHA245': None,
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt 'ECDSAP384SHA384': None}
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt def __init__(self, name=None, algorithm=None, parent=None):
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.name = name
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.algorithm = algorithm
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.parent = parent
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt pass
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt def __repr__(self):
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt return ("%spolicy %s:\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tinherits %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tdirectory %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\talgorithm %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tcoverage %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tksk_keysize %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tzsk_keysize %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tksk_rollperiod %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tzsk_rollperiod %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tksk_prepublish %s\n"
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt "\tksk_postpublish %s\n"
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews "\tzsk_prepublish %s\n"
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews "\tzsk_postpublish %s\n"
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews "\tksk_standby %s\n"
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews "\tzsk_standby %s\n"
2c089bf6d24936de631a57b4958ba6b8b5e3b23dMark Andrews "\tkeyttl %s\n"
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews %
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews ((self.is_constructed and 'constructed ' or \
5ae2eac4c16bdbbef032544bd9fc86f47e7bdc2cMark Andrews self.is_zone and 'zone ' or \
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.is_alg and 'algorithm ' or ''),
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.name or 'UNKNOWN',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.parent and self.parent.name or 'None',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.directory and ('"' + str(self.directory) + '"') or 'None',
e11a0c114cdaf8f7e7832e9f1a011138248093a6Evan Hunt self.algorithm or 'None',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt self.coverage and str(self.coverage) or 'None',
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt self.ksk_keysize and str(self.ksk_keysize) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.zsk_keysize and str(self.zsk_keysize) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.ksk_rollperiod and str(self.ksk_rollperiod) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.zsk_rollperiod and str(self.zsk_rollperiod) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.ksk_prepublish and str(self.ksk_prepublish) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.ksk_postpublish and str(self.ksk_postpublish) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.zsk_prepublish and str(self.zsk_prepublish) or 'None',
bbedadf76ab670b01887fb9b41097120ea4fdf14Evan Hunt self.zsk_postpublish and str(self.zsk_postpublish) or 'None',
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.ksk_standby and str(self.ksk_standby) or 'None',
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.zsk_standby and str(self.zsk_standby) or 'None',
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.keyttl and str(self.keyttl) or 'None'))
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt def __verify_size(self, key_size, size_range):
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt return (size_range[0] <= key_size <= size_range[1])
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt def get_name(self):
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt return self.name
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt def constructed(self):
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt return self.is_constructed
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt def validate(self):
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt """ Check if the values in the policy make sense
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt :return: True/False if the policy passes validation
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt """
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt if self.ksk_rollperiod and \
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.ksk_prepublish is not None and \
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.ksk_prepublish > self.ksk_rollperiod:
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt print(self.ksk_rollperiod)
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt return (False,
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt ('KSK pre-publish period (%d) exceeds rollover period %d'
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt % (self.ksk_prepublish, self.ksk_rollperiod)))
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt if self.ksk_rollperiod and \
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.ksk_postpublish is not None and \
61bcc232038f0a2cb77ed6269675fdc288f5ec98Evan Hunt self.ksk_postpublish > self.ksk_rollperiod:
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt return (False,
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt ('KSK post-publish period (%d) exceeds rollover period %d'
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt % (self.ksk_postpublish, self.ksk_rollperiod)))
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt if self.zsk_rollperiod and \
319b8a14881a95996af3a9ba4a20f144eb766b31Evan Hunt self.zsk_prepublish is not None and \
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt self.zsk_prepublish >= self.zsk_rollperiod:
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt return (False,
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt ('ZSK pre-publish period (%d) exceeds rollover period %d'
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt % (self.zsk_prepublish, self.zsk_rollperiod)))
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt if self.zsk_rollperiod and \
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt self.zsk_postpublish is not None and \
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt self.zsk_postpublish >= self.zsk_rollperiod:
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt return (False,
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt ('ZSK post-publish period (%d) exceeds rollover period %d'
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt % (self.zsk_postpublish, self.zsk_rollperiod)))
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt if self.ksk_rollperiod and \
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt self.ksk_prepublish and self.ksk_postpublish and \
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews self.ksk_prepublish + self.ksk_postpublish >= self.ksk_rollperiod:
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews return (False,
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews (('KSK pre/post-publish periods (%d/%d) ' +
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews 'combined exceed rollover period %d') %
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews (self.ksk_prepublish,
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews self.ksk_postpublish,
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews self.ksk_rollperiod)))
e939674d53a127ddeeaf4b41fd72933f0b493308Mark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews if self.zsk_rollperiod and \
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews self.zsk_prepublish and self.zsk_postpublish and \
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews self.zsk_prepublish + self.zsk_postpublish >= self.zsk_rollperiod:
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews return (False,
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews (('ZSK pre/post-publish periods (%d/%d) ' +
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews 'combined exceed rollover period %d') %
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews (self.zsk_prepublish,
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews self.zsk_postpublish,
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews self.zsk_rollperiod)))
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews if self.algorithm is not None:
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews # Validate the key size
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews key_sz_range = self.valid_key_sz_per_algo.get(self.algorithm)
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews if key_sz_range is not None:
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews # Verify KSK
0d6328ce5f6b799f8e7c6cbbb3b965cf29bfb7baMark Andrews if not self.__verify_size(self.ksk_keysize, key_sz_range):
677f507de7c546c187c1505c48bc7b440545485cMark Andrews return False, 'KSK key size %d outside valid range %s' \
677f507de7c546c187c1505c48bc7b440545485cMark Andrews % (self.ksk_keysize, key_sz_range)
677f507de7c546c187c1505c48bc7b440545485cMark Andrews
677f507de7c546c187c1505c48bc7b440545485cMark Andrews # Verify ZSK
677f507de7c546c187c1505c48bc7b440545485cMark Andrews if not self.__verify_size(self.zsk_keysize, key_sz_range):
677f507de7c546c187c1505c48bc7b440545485cMark Andrews return False, 'ZSK key size %d outside valid range %s' \
677f507de7c546c187c1505c48bc7b440545485cMark Andrews % (self.zsk_keysize, key_sz_range)
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews # Specific check for DSA keys
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews if self.algorithm in ['DSA', 'NSEC3DSA'] and \
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews self.ksk_keysize % 64 != 0:
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews return False, \
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews ('KSK key size %d not divisible by 64 ' +
e01ef6f01c7e8f80122cd80a2e011425a0135489Mark Andrews 'as required for DSA') % self.ksk_keysize
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
if self.algorithm in ['DSA', 'NSEC3DSA'] and \
self.zsk_keysize % 64 != 0:
return False, \
('ZSK key size %d not divisible by 64 ' +
'as required for DSA') % self.zsk_keysize
return True, ''
############################################################################
# dnssec_policy:
# This class reads a dnssec.policy file and creates a dictionary of
# DNSSEC policy rules from which a policy for a specific zone can
# be generated.
############################################################################
class PolicyException(Exception):
pass
class dnssec_policy:
alg_policy = {}
named_policy = {}
zone_policy = {}
current = None
filename = None
initial = True
def __init__(self, filename=None, **kwargs):
self.plex = PolicyLex()
self.tokens = self.plex.tokens
if 'debug' not in kwargs:
kwargs['debug'] = False
if 'write_tables' not in kwargs:
kwargs['write_tables'] = False
self.parser = yacc.yacc(module=self, **kwargs)
# set defaults
self.setup('''policy global { algorithm rsasha256;
key-size ksk 2048;
key-size zsk 2048;
roll-period ksk 0;
roll-period zsk 1y;
pre-publish ksk 1mo;
pre-publish zsk 1mo;
post-publish ksk 1mo;
post-publish zsk 1mo;
standby ksk 0;
standby zsk 0;
keyttl 1h;
coverage 6mo; };
policy default { policy global; };''')
p = Policy()
p.algorithm = None
p.is_alg = True
p.ksk_keysize = 2048;
p.zsk_keysize = 2048;
# set default algorithm policies
# these need a lower default key size:
self.alg_policy['DSA'] = copy(p)
self.alg_policy['DSA'].algorithm = "DSA"
self.alg_policy['DSA'].name = "DSA"
self.alg_policy['DSA'].ksk_keysize = 1024;
self.alg_policy['NSEC3DSA'] = copy(p)
self.alg_policy['NSEC3DSA'].algorithm = "NSEC3DSA"
self.alg_policy['NSEC3DSA'].name = "NSEC3DSA"
self.alg_policy['NSEC3DSA'].ksk_keysize = 1024;
# these can use default settings
self.alg_policy['RSAMD5'] = copy(p)
self.alg_policy['RSAMD5'].algorithm = "RSAMD5"
self.alg_policy['RSAMD5'].name = "RSAMD5"
self.alg_policy['RSASHA1'] = copy(p)
self.alg_policy['RSASHA1'].algorithm = "RSASHA1"
self.alg_policy['RSASHA1'].name = "RSASHA1"
self.alg_policy['NSEC3RSASHA1'] = copy(p)
self.alg_policy['NSEC3RSASHA1'].algorithm = "NSEC3RSASHA1"
self.alg_policy['NSEC3RSASHA1'].name = "NSEC3RSASHA1"
self.alg_policy['RSASHA256'] = copy(p)
self.alg_policy['RSASHA256'].algorithm = "RSASHA256"
self.alg_policy['RSASHA256'].name = "RSASHA256"
self.alg_policy['RSASHA512'] = copy(p)
self.alg_policy['RSASHA512'].algorithm = "RSASHA512"
self.alg_policy['RSASHA512'].name = "RSASHA512"
self.alg_policy['ECCGOST'] = copy(p)
self.alg_policy['ECCGOST'].algorithm = "ECCGOST"
self.alg_policy['ECCGOST'].name = "ECCGOST"
self.alg_policy['ECDSAP256SHA245'] = copy(p)
self.alg_policy['ECDSAP256SHA245'].algorithm = "ECDSAP256SHA256"
self.alg_policy['ECDSAP256SHA245'].name = "ECDSAP256SHA256"
self.alg_policy['ECDSAP384SHA384'] = copy(p)
self.alg_policy['ECDSAP384SHA384'].algorithm = "ECDSAP384SHA384"
self.alg_policy['ECDSAP384SHA384'].name = "ECDSAP384SHA384"
if filename:
self.load(filename)
def load(self, filename):
self.filename = filename
self.initial = True
with open(filename) as f:
text = f.read()
self.plex.lexer.lineno = 0
self.parser.parse(text)
self.filename = None
def setup(self, text):
self.initial = True
self.plex.lexer.lineno = 0
self.parser.parse(text)
def policy(self, zone, **kwargs):
z = zone.lower()
p = None
if z in self.zone_policy:
p = self.zone_policy[z]
if p is None:
p = copy(self.named_policy['default'])
p.name = zone
p.is_constructed = True
if p.algorithm is None:
parent = p.parent or self.named_policy['default']
while parent and not parent.algorithm:
parent = parent.parent
p.algorithm = parent and parent.algorithm or None
if p.algorithm in self.alg_policy:
ap = self.alg_policy[p.algorithm]
else:
raise PolicyException('algorithm not found')
if p.directory is None:
parent = p.parent or self.named_policy['default']
while parent is not None and not parent.directory:
parent = parent.parent
p.directory = parent and parent.directory
if p.coverage is None:
parent = p.parent or self.named_policy['default']
while parent and not parent.coverage:
parent = parent.parent
p.coverage = parent and parent.coverage or ap.coverage
if p.ksk_keysize is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.ksk_keysize:
parent = parent.parent
p.ksk_keysize = parent and parent.ksk_keysize or ap.ksk_keysize
if p.zsk_keysize is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.zsk_keysize:
parent = parent.parent
p.zsk_keysize = parent and parent.zsk_keysize or ap.zsk_keysize
if p.ksk_rollperiod is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.ksk_rollperiod:
parent = parent.parent
p.ksk_rollperiod = parent and \
parent.ksk_rollperiod or ap.ksk_rollperiod
if p.zsk_rollperiod is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.zsk_rollperiod:
parent = parent.parent
p.zsk_rollperiod = parent and \
parent.zsk_rollperiod or ap.zsk_rollperiod
if p.ksk_prepublish is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.ksk_prepublish:
parent = parent.parent
p.ksk_prepublish = parent and \
parent.ksk_prepublish or ap.ksk_prepublish
if p.zsk_prepublish is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.zsk_prepublish:
parent = parent.parent
p.zsk_prepublish = parent and \
parent.zsk_prepublish or ap.zsk_prepublish
if p.ksk_postpublish is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.ksk_postpublish:
parent = parent.parent
p.ksk_postpublish = parent and \
parent.ksk_postpublish or ap.ksk_postpublish
if p.zsk_postpublish is None:
parent = p.parent or self.named_policy['default']
while parent.parent and not parent.zsk_postpublish:
parent = parent.parent
p.zsk_postpublish = parent and \
parent.zsk_postpublish or ap.zsk_postpublish
if 'novalidate' not in kwargs or not kwargs['novalidate']:
(valid, msg) = p.validate()
if not valid:
raise PolicyException(msg)
return None
return p
def p_policylist(self, p):
'''policylist : init policy
| policylist policy'''
pass
def p_init(self, p):
"init :"
self.initial = False
def p_policy(self, p):
'''policy : alg_policy
| zone_policy
| named_policy'''
pass
def p_name(self, p):
'''name : STR
| KEYTYPE
| DATESUFFIX'''
p[0] = p[1]
pass
def p_new_policy(self, p):
"new_policy :"
self.current = Policy()
def p_alg_policy(self, p):
"alg_policy : ALGORITHM_POLICY ALGNAME new_policy alg_option_group SEMI"
self.current.name = p[2]
self.current.is_alg = True
self.alg_policy[p[2]] = self.current
pass
def p_zone_policy(self, p):
"zone_policy : ZONE name new_policy policy_option_group SEMI"
self.current.name = p[2]
self.current.is_zone = True
self.zone_policy[p[2].lower()] = self.current
pass
def p_named_policy(self, p):
"named_policy : POLICY name new_policy policy_option_group SEMI"
self.current.name = p[2]
self.named_policy[p[2].lower()] = self.current
pass
def p_duration_1(self, p):
"duration : NUMBER"
p[0] = p[1]
pass
def p_duration_2(self, p):
"duration : NONE"
p[0] = None
pass
def p_duration_3(self, p):
"duration : NUMBER DATESUFFIX"
if p[2] == "y":
p[0] = p[1] * 31536000 # year
elif p[2] == "mo":
p[0] = p[1] * 2592000 # month
elif p[2] == "w":
p[0] = p[1] * 604800 # week
elif p[2] == "d":
p[0] = p[1] * 86400 # day
elif p[2] == "h":
p[0] = p[1] * 3600 # hour
elif p[2] == "mi":
p[0] = p[1] * 60 # minute
elif p[2] == "s":
p[0] = p[1] # second
else:
raise PolicyException('invalid duration')
def p_policy_option_group(self, p):
"policy_option_group : LBRACE policy_option_list RBRACE"
pass
def p_policy_option_list(self, p):
'''policy_option_list : policy_option SEMI
| policy_option_list policy_option SEMI'''
pass
def p_policy_option(self, p):
'''policy_option : parent_option
| directory_option
| coverage_option
| rollperiod_option
| prepublish_option
| postpublish_option
| keysize_option
| algorithm_option
| keyttl_option
| standby_option'''
pass
def p_alg_option_group(self, p):
"alg_option_group : LBRACE alg_option_list RBRACE"
pass
def p_alg_option_list(self, p):
'''alg_option_list : alg_option SEMI
| alg_option_list alg_option SEMI'''
pass
def p_alg_option(self, p):
'''alg_option : coverage_option
| rollperiod_option
| prepublish_option
| postpublish_option
| keyttl_option
| keysize_option
| standby_option'''
pass
def p_parent_option(self, p):
"parent_option : POLICY name"
self.current.parent = self.named_policy[p[2].lower()]
def p_directory_option(self, p):
"directory_option : DIRECTORY QSTRING"
self.current.directory = p[2]
def p_coverage_option(self, p):
"coverage_option : COVERAGE duration"
self.current.coverage = p[2]
def p_rollperiod_option(self, p):
"rollperiod_option : ROLL_PERIOD KEYTYPE duration"
if p[2] == "KSK":
self.current.ksk_rollperiod = p[3]
else:
self.current.zsk_rollperiod = p[3]
def p_prepublish_option(self, p):
"prepublish_option : PRE_PUBLISH KEYTYPE duration"
if p[2] == "KSK":
self.current.ksk_prepublish = p[3]
else:
self.current.zsk_prepublish = p[3]
def p_postpublish_option(self, p):
"postpublish_option : POST_PUBLISH KEYTYPE duration"
if p[2] == "KSK":
self.current.ksk_postpublish = p[3]
else:
self.current.zsk_postpublish = p[3]
def p_keysize_option(self, p):
"keysize_option : KEY_SIZE KEYTYPE NUMBER"
if p[2] == "KSK":
self.current.ksk_keysize = p[3]
else:
self.current.zsk_keysize = p[3]
def p_standby_option(self, p):
"standby_option : STANDBY KEYTYPE NUMBER"
if p[2] == "KSK":
self.current.ksk_standby = p[3]
else:
self.current.zsk_standby = p[3]
def p_keyttl_option(self, p):
"keyttl_option : KEYTTL duration"
self.current.keyttl = p[2]
def p_algorithm_option(self, p):
"algorithm_option : ALGORITHM ALGNAME"
self.current.algorithm = p[2]
def p_error(self, p):
if p:
print("%s%s%d:syntax error near '%s'" %
(self.filename or "", ":" if self.filename else "",
p.lineno, p.value))
else:
if not self.initial:
raise PolicyException("%s%s%d:unexpected end of input" %
(self.filename or "", ":" if self.filename else "",
p and p.lineno or 0))
if __name__ == "__main__":
import sys
if sys.argv[1] == "lex":
file = open(sys.argv[2])
text = file.read()
file.close()
plex = PolicyLex(debug=1)
plex.test(text)
elif sys.argv[1] == "parse":
try:
pp = dnssec_policy(sys.argv[2], write_tables=True, debug=True)
print(pp.named_policy['default'])
print(pp.policy("nonexistent.zone"))
except Exception as e:
print(e.args[0])