[Git][NTPsec/ntpsec][master] 4 commits: First cut at pyntpdig with authentication.

Eric S. Raymond gitlab at mg.gitlab.com
Tue Nov 8 12:46:34 UTC 2016


Eric S. Raymond pushed to branch master at NTPsec / ntpsec


Commits:
951af89b by Eric S. Raymond at 2016-11-08T06:22:45-05:00
First cut at pyntpdig with authentication.

- - - - -
b06f19f3 by Eric S. Raymond at 2016-11-08T06:43:18-05:00
In pyntpdig, implement packet hex dumping at debug >= 2.

- - - - -
1f3e43af by Eric S. Raymond at 2016-11-08T06:53:58-05:00
Standardize the way we dump the codebase version.

- - - - -
3578cbe1 by Eric S. Raymond at 2016-11-08T07:45:49-05:00
Authenticated send works in pyntdig.

- - - - -


5 changed files:

- ntpdig/pyntpdig
- ntpq/ntpq
- ntpstats/ntpviz
- pylib/packet.py
- pylib/util.py


Changes:

=====================================
ntpdig/pyntpdig
=====================================
--- a/ntpdig/pyntpdig
+++ b/ntpdig/pyntpdig
@@ -35,6 +35,26 @@ from __future__ import print_function, division
 
 import sys, socket, select, struct, time, getopt, datetime 
 
+# This code can run with a large subset of its capabilities
+# even when the ntp library is not available.
+try:
+    import ntp.packet
+    import ntp.util
+    standalone = False
+except:
+    standalone = True
+
+if standalone:
+    version = "ntpsec ntpdig (running standalone)"
+else:
+    version = ntp.util.stdversion()
+
+def standalone_bailout(switch):
+    if standalone:
+        sys.stderr.write("ntpdig: %s is not available running standalone\n." \
+                         % switch)
+        raise SystemExit(1)
+
 class SNTPPacket:
     @staticmethod
     def rescale(t):
@@ -93,10 +113,26 @@ def queryhost(server, concurrent, timeout=5, port=123):
         if debug:
             log("querying %s (%s)" % (sockaddr[0], server))
         s = socket.socket(family, socktype)
-        d = b'\xe3' + b'\0' * 47
-        s.sendto(d, sockaddr)
+        packet = b'\xe3' + b'\0' * 47
+        if keyid and keytype and passwd:
+            if debug:
+                log("authenticating with %s key %d" % (keytype, keyid))
+            mac = ntp.packet.Authenticator.compute_mac(packet,
+                                                       keyid, keytype, passwd)
+            if mac is None:
+                log("MAC generation failed while querying %s" % server)
+                raise SystemExit(1)
+            else:
+                packet += mac
+        s.sendto(packet, sockaddr)
+        if debug >= 2:
+            print("Sent to %s:" % (sockaddr[0],))
+            ntp.packet.dump_hex_printable(packet)
         def read_append(s, packets):
             d, a = s.recvfrom(1024)
+            if debug >= 2:
+                print("Received:")
+                ntp.packet.dump_hex_printable(d)
             pkt = SNTPPacket(d)
             pkt.hostname = server
             pkt.resolved = sockaddr[0]
@@ -204,8 +240,8 @@ USAGE:  sntp [ -<flag> [<val>] | --<name>[{=| }<val>] ]...
 				- prohibits the option 'ipv4'
    -a Num authentication  Enable authentication with the numbered key
    -c yes concurrent      Hosts to be queried concurrently
-   -d no  debug           Normal verbose
-   -D yes set-debug-level Normal verbose
+   -d no  debug           Increase debug verbosity
+   -D yes set-debug-level Set debug verbosity
    -g yes gap             Set gap between requests
    -j no  json            Use JSON output format
    -l Str logfile         Log to specified logfile
@@ -262,7 +298,8 @@ if __name__ == '__main__':
                 af = socket.AF_INET
             elif switch in ("-6", "--ipv6"):
                 af = socket.AF_INET6
-            elif switch in ("-a", "--authentication"):	# Not implemented yet
+            elif switch in ("-a", "--authentication"):
+                standalone_bailout(switch)
                 authkey = int(val)
             elif switch in ("-c", "--concurrent"):
                 concurrent_hosts.append(val)
@@ -272,21 +309,25 @@ if __name__ == '__main__':
                 debug = int(val)
             elif switch in ("-j", "--json"):
                 json = True
-            elif switch in ("-k", "--keyfile"):		# Not implemented yet
+            elif switch in ("-k", "--keyfile"):
+                standalone_bailout(switch)
                 keyfile = val
             elif switch in ("-l", "--logfile"):
                 try:
                     logfp = open(val, "w")
                 except OSError:
-                    self.warn("logfile open of %s failed.\n" % val)
+                    sys.stderr.write("logfile open of %s failed.\n" % val)
                     raise SystemExit(1)
             elif switch in ("-M", "--steplimit"):	# Not implemented yet
+                standalone_bailout(switch)
                 steplimit = int(val)
             elif switch in ("-p", "--samples"):
                 samples = int(val)
             elif switch in ("-S", "--step"):		# Not implemented yet
+                standalone_bailout(switch)
                 step = True
             elif switch in ("-s", "--slew"):		# Not implemented yet
+                standalone_bailout(switch)
                 slew = True
             elif switch in ("-t", "--timeout"):
                 timeout = int(val)
@@ -294,18 +335,31 @@ if __name__ == '__main__':
                 print(usage)
                 raise SystemExit(0)
             elif switch in ("-V", "--version"):
-                print("ntpdig 0.1\n")	# FIXME: Use the version module
+                print(version)
                 raise SystemExit(0)
             else:
-                self.warn("Unknown command line switch or missing argument.\n")
-                self.warn(usage)
+                sys.stderr.write("Unknown command line switch or missing argument.\n")
+                sys.stderr.write(usage)
                 raise SystemExit(1)
     except ValueError:
-        self.warn("Invalid argument.\n")
-        self.warn(usage)
+        sys.stderr.write("Invalid argument.\n")
+        sys.stderr.write(usage)
         raise SystemExit(1)
 
-    if authkey and keyfile is None:
+    credentials = keyid = keytype = passwd = None
+    try:
+        credentials = ntp.packet.Authenticator(keyfile)
+    except (OSError, IOError):
+        pass
+    if credentials:
+        try:
+            (keyid, keytype, passwd) = credentials.control(authkey)
+        except ValueError:
+            # There are no trusted keys.  Barf.
+            log("cannot get authentication key")
+            raise SystemExit(1)
+
+    if not credentials and authkey and keyfile is None:
         self.warn("-a option requires -k.\n")
         raise SystemExit(1)
 


=====================================
ntpq/ntpq
=====================================
--- a/ntpq/ntpq
+++ b/ntpq/ntpq
@@ -30,7 +30,7 @@ try:
 except ImportError:
     pass
 
-version = ntp.version.BASENAME + " " + ntp.version.VERSION
+version = stdversion()
 
 # General notes on Python 2/3 compatibility:
 #


=====================================
ntpstats/ntpviz
=====================================
--- a/ntpstats/ntpviz
+++ b/ntpstats/ntpviz
@@ -42,7 +42,7 @@ import tempfile
 
 try:
     from ntp.statfiles import *
-    import ntp.version
+    import ntp.util
 except ImportError:
     sys.stderr.write("ntpviz: can't find Python NTP library -- check PYTHONPATH.\n")
     sys.exit(1)
@@ -1283,8 +1283,7 @@ Python by ESR, concept and GNUPLOT code by Dan Drown.
 
     args = parser.parse_args()
 
-    version = "%s %s+%s" % ( ntp.version.BASENAME, ntp.version.VERSION, \
-                            ntp.version.TICK)
+    version = ntp.util.stdversion()
 
     if args.version:
         print(version)


=====================================
pylib/packet.py
=====================================
--- a/pylib/packet.py
+++ b/pylib/packet.py
@@ -1040,9 +1040,11 @@ DEFAULT_KEYFILE = "/usr/local/etc/ntp.keys"
 
 class Authenticator:
     "MAC authentication manager for NTP packets."
-    def __init__(self, keyfile=DEFAULT_KEYFILE):
+    def __init__(self, keyfile=None):
         # We allow I/O and permission errors upward deliberately
         self.passwords = {}
+        if keyfile is None:
+            keyfile = DEFAULT_KEYFILE
         for line in open(keyfile):
             if '#' in line:
                 line = line[:line.index("#")]
@@ -1055,8 +1057,13 @@ class Authenticator:
         return len(self.passwords)
     def __getitem__(self, keyid):
         return self.passwords.get(keyid)
-    def control(self):
+    def control(self, keyid=None):
         "Get a keyid/passwd pair that is trusted on localhost"
+        if keyid is not None:
+            if keyid in self.passwords:
+                return (keyid) + self.passwords[keyid]
+            else:
+                return (keyid, None, None)
         for line in open("/etc/ntp.conf"):
             if line.startswith("control"):
                 keyid = int(line.split()[1])


=====================================
pylib/util.py
=====================================
--- a/pylib/util.py
+++ b/pylib/util.py
@@ -10,6 +10,10 @@ import ntp.ntpc
 import re
 
 from ntp.packet import *
+from ntp.version import *
+
+def stdversion():
+    return "%s %s+%s" % (BASENAME, VERSION, ntp.version.TICK)
 
 def portsplit(hostname):
     portsuffix = ""



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/5877d463b16fe3a852029a1edf22df1638abbc29...3578cbe19b9da920cc6d76bba0cf3a08816f602d
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ntpsec.org/pipermail/vc/attachments/20161108/f80b62cf/attachment.html>


More information about the vc mailing list