=== modified file 'TODO' --- TODO 2012-06-17 14:55:31 +0000 +++ TODO 2012-06-23 00:58:49 +0000 @@ -47,7 +47,6 @@ ** TODO [#C] DBusServiceObjectUsingSuper ** TODO [#B] Global enable/disable flag ** TODO [#B] By-client countdown on number of secrets given -** TODO [#B] Support RFC 3339 time duration syntax ** D-Bus Client method NeedsPassword(50) - Timeout, default disapprove + SetPass(u"gazonk", True) -> Approval, persistent + Approve(False) -> Close client connection immediately @@ -80,7 +79,6 @@ * mandos-ctl *** Handle "no D-Bus server" and/or "no Mandos server found" better *** [#B] --dump option -** TODO Support RFC 3339 time duration syntax * TODO mandos-dispatch Listens for specified D-Bus signals and spawns shell commands with === modified file 'clients.conf' --- clients.conf 2011-11-26 23:08:17 +0000 +++ clients.conf 2012-06-23 00:58:49 +0000 @@ -4,19 +4,19 @@ # How long until a client is disabled and not be allowed to get the # data this server holds. -;timeout = 5m +;timeout = PT5M # How often to run the checker to confirm that a client is still up. # Note: a new checker will not be started if an old one is still # running. The server will wait for a checker to complete until the # above "timeout" occurs, at which time the client will be disabled, # and any running checker killed. -;interval = 2m +;interval = PT2M # Extended timeout is an added timeout that is given once after a # password has been sent sucessfully to a client. This allows for # additional delays caused by file system checks and quota checks. -;extended_timeout = 15m +;extended_timeout = PT15M # What command to run as "the checker". ;checker = fping -q -- %%(host)s @@ -25,10 +25,10 @@ ;approved_by_default = True # How long to wait for approval. -;approval_delay = 0s +;approval_delay = PT0S # How long one approval will last. -;approval_duration = 1s +;approval_duration = PT1S # Whether this client is enabled by default ;enabled = True @@ -78,10 +78,10 @@ ;host = 192.0.2.3 ; ;# Parameters from the [DEFAULT] section can be overridden per client. -;interval = 1m +;interval = PT1M ; ;# This client requires manual approval before it receives its secret. ;approved_by_default = False ;# Require approval within 30 seconds. -;approval_delay = 30s +;approval_delay = PT30S ;#### === modified file 'mandos' --- mandos 2012-06-17 22:26:40 +0000 +++ mandos 2012-06-23 00:58:49 +0000 @@ -68,6 +68,7 @@ import binascii import tempfile import itertools +import collections import dbus import dbus.service @@ -446,13 +447,13 @@ "fingerprint", "host", "interval", "last_approval_request", "last_checked_ok", "last_enabled", "name", "timeout") - client_defaults = { "timeout": "5m", - "extended_timeout": "15m", - "interval": "2m", + client_defaults = { "timeout": "PT5M", + "extended_timeout": "PT15M", + "interval": "PT2M", "checker": "fping -q -- %%(host)s", "host": "", - "approval_delay": "0s", - "approval_duration": "1s", + "approval_delay": "PT0S", + "approval_duration": "PT1S", "approved_by_default": "True", "enabled": "True", } @@ -2090,6 +2091,105 @@ return True +def rfc3339_duration_to_delta(duration): + """Parse an RFC 3339 "duration" and return a datetime.timedelta + + >>> rfc3339_duration_to_delta("P7D") + datetime.timedelta(7) + >>> rfc3339_duration_to_delta("PT60S") + datetime.timedelta(0, 60) + >>> rfc3339_duration_to_delta("PT60M") + datetime.timedelta(0, 3600) + >>> rfc3339_duration_to_delta("PT24H") + datetime.timedelta(1) + >>> rfc3339_duration_to_delta("P1W") + datetime.timedelta(7) + >>> rfc3339_duration_to_delta("PT5M30S") + datetime.timedelta(0, 330) + >>> rfc3339_duration_to_delta("P1DT3M20S") + datetime.timedelta(1, 200) + """ + + # Parsing an RFC 3339 duration with regular expressions is not + # possible - there would have to be multiple places for the same + # values, like seconds. The current code, while more esoteric, is + # cleaner without depending on a parsing library. If Python had a + # built-in library for parsing we would use it, but we'd like to + # avoid excessive use of external libraries. + + # New type for defining tokens, syntax, and semantics all-in-one + Token = collections.namedtuple("Token", + ("regexp", # To match token; if + # "value" is not None, + # must have a "group" + # containing digits + "value", # datetime.timedelta or + # None + "followers")) # Tokens valid after + # this token + # RFC 3339 "duration" tokens, syntax, and semantics; taken from + # the "duration" ABNF definition in RFC 3339, Appendix A. + token_end = Token(re.compile(r"$"), None, frozenset()) + token_second = Token(re.compile(r"(\d+)S"), + datetime.timedelta(seconds=1), + frozenset((token_end,))) + token_minute = Token(re.compile(r"(\d+)M"), + datetime.timedelta(minutes=1), + frozenset((token_second, token_end))) + token_hour = Token(re.compile(r"(\d+)H"), + datetime.timedelta(hours=1), + frozenset((token_minute, token_end))) + token_time = Token(re.compile(r"T"), + None, + frozenset((token_hour, token_minute, + token_second))) + token_day = Token(re.compile(r"(\d+)D"), + datetime.timedelta(days=1), + frozenset((token_time, token_end))) + token_month = Token(re.compile(r"(\d+)M"), + datetime.timedelta(weeks=4), + frozenset((token_day, token_end))) + token_year = Token(re.compile(r"(\d+)Y"), + datetime.timedelta(weeks=52), + frozenset((token_month, token_end))) + token_week = Token(re.compile(r"(\d+)W"), + datetime.timedelta(weeks=1), + frozenset((token_end,))) + token_duration = Token(re.compile(r"P"), None, + frozenset((token_year, token_month, + token_day, token_time, + token_week))), + # Define starting values + value = datetime.timedelta() # Value so far + found_token = None + followers = frozenset(token_duration,) # Following valid tokens + s = duration # String left to parse + # Loop until end token is found + while found_token is not token_end: + # Search for any currently valid tokens + for token in followers: + match = token.regexp.match(s) + if match is not None: + # Token found + if token.value is not None: + # Value found, parse digits + factor = int(match.group(1), 10) + # Add to value so far + value += factor * token.value + # Strip token from string + s = token.regexp.sub("", s, 1) + # Go to found token + found_token = token + # Set valid next tokens + followers = found_token.followers + break + else: + # No currently valid tokens were found + raise ValueError("Invalid RFC 3339 duration") + # End token found + return value + + def string_to_delta(interval): """Parse a string and return a datetime.timedelta @@ -2106,6 +2206,12 @@ >>> string_to_delta('5m 30s') datetime.timedelta(0, 330) """ + + try: + return rfc3339_duration_to_delta(interval) + except ValueError: + pass + timevalue = datetime.timedelta(0) for s in interval.split(): try: === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2012-05-26 22:48:45 +0000 +++ mandos-clients.conf.xml 2012-06-23 00:58:49 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/clients.conf"> - + %common; ]> @@ -335,17 +335,12 @@ option. - The TIME is specified as a - space-separated number of values, each of which is a - number and a one-character suffix. The suffix must be one - of d, s, m, - h, and w for days, seconds, - minutes, hours, and weeks, respectively. The values are - added together to give the total time value, so all of - 330s, - 110s 110s 110s, and - 5m 30s will give a value - of five minutes and thirty seconds. + The TIME is specified as an RFC + 3339 duration; for example + P1Y2M3DT4H5M6S meaning + one year, two months, three days, four hours, five + minutes, and six seconds. Some values can be omitted, see + RFC 3339 Appendix A for details. @@ -465,8 +460,8 @@ [DEFAULT] -timeout = 5m -interval = 2m +timeout = PT5M +interval = PT2M checker = fping -q -- %%(host)s # Client "foo" @@ -489,15 +484,15 @@ 4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O QlnHIvPzEArRQLo= host = foo.example.org -interval = 1m +interval = PT1M # Client "bar" [bar] fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 secfile = /etc/mandos/bar-secret -timeout = 15m +timeout = PT15M approved_by_default = False -approval_delay = 30s +approval_delay = PT30S @@ -516,6 +511,20 @@ fping 8 + + + + RFC 3339: Date and Time on the Internet: + Timestamps + + + + The time intervals are in the "duration" format, as + specified in ABNF in Appendix A of RFC 3339. + + + + === modified file 'mandos-ctl' --- mandos-ctl 2012-06-22 23:33:56 +0000 +++ mandos-ctl 2012-06-23 00:58:49 +0000 @@ -85,7 +85,7 @@ def rfc3339_duration_to_delta(duration): - """Parse a RFC 3339 "duration" and return a datetime.timedelta + """Parse an RFC 3339 "duration" and return a datetime.timedelta >>> rfc3339_duration_to_delta("P7D") datetime.timedelta(7) @@ -103,10 +103,10 @@ datetime.timedelta(1, 200) """ - # Parsing a RFC 3339 duration with regular expressions is not + # Parsing an RFC 3339 duration with regular expressions is not # possible - there would have to be multiple places for the same - # values, like seconds. This, while more esoteric, is cleaner - # without depending on a parsing library. If Python had a + # values, like seconds. The current code, while more esoteric, is + # cleaner without depending on a parsing library. If Python had a # built-in library for parsing we would use it, but we'd like to # avoid excessive use of external libraries.