[Cryptech-Commits] [sw/libhal] branch pymux updated: Whack multiplexer to handle console too.

git at cryptech.is git at cryptech.is
Fri Jan 6 04:48:05 UTC 2017


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch pymux
in repository sw/libhal.

The following commit(s) were added to refs/heads/pymux by this push:
     new 3c20fd1  Whack multiplexer to handle console too.
3c20fd1 is described below

commit 3c20fd189648b8182edbafed572898d1af744aa6
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Thu Jan 5 23:43:22 2017 -0500

    Whack multiplexer to handle console too.
    
    Renamed multiplexer to cryptech_muxd, since it now handles both RPC and CTY.
    
    Added new program cryptech_console to act as client for CTY multiplexer.
    
    Might want to add console logging capability eventually, not today.
    
    Probably want to incorporate UART probing (what cryptech_probe does
    now) eventually, also not today.
---
 cryptech_console                  | 119 +++++++++++++++++++++++++++++
 cryptech_rpcmuxd => cryptech_muxd | 154 ++++++++++++++++++++++++++++++--------
 hal_internal.h                    |   2 +-
 libhal.py                         |   2 +-
 4 files changed, 244 insertions(+), 33 deletions(-)

diff --git a/cryptech_console b/cryptech_console
new file mode 100755
index 0000000..80ec15d
--- /dev/null
+++ b/cryptech_console
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2017, NORDUnet A/S All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# - Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+#
+# - Neither the name of the NORDUnet nor the names of its contributors may
+#   be used to endorse or promote products derived from this software
+#   without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+Console client shim to work with Cryptech Python multiplexer.
+"""
+
+import os
+import sys
+import socket
+import atexit
+import termios
+import argparse
+
+import tornado.iostream
+import tornado.ioloop
+import tornado.gen
+
+
+class FemtoTerm(object):
+
+    def __init__(self, s):
+        self.termios_setup()
+        self.stdin_stream  = tornado.iostream.PipeIOStream(sys.stdin.fileno())
+        self.stdout_stream = tornado.iostream.PipeIOStream(sys.stdout.fileno())
+        self.socket_stream = tornado.iostream.IOStream(s)
+        self.closed = False
+
+    def termios_setup(self):
+        self.fd = sys.stdin.fileno()
+        self.old_tcattr = termios.tcgetattr(self.fd)
+        self.new_tcattr = termios.tcgetattr(self.fd)
+        atexit.register(self.termios_teardown)
+        self.new_tcattr[3] &= ~(termios.ICANON | termios.ECHO) #  | termios.ISIG
+        self.new_tcattr[6][termios.VMIN] = 1
+        self.new_tcattr[6][termios.VTIME] = 0
+        termios.tcsetattr(self.fd, termios.TCSANOW, self.new_tcattr)
+
+    def termios_teardown(self):
+        termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_tcattr)
+
+    @tornado.gen.coroutine
+    def stdin_loop(self):
+        try:
+            while not self.closed:
+                buffer = yield self.stdin_stream.read_bytes(1024, partial = True)
+                yield self.socket_stream.write(buffer.replace("\n", "\r"))
+        except tornado.iostream.StreamClosedError:
+            self.closed = True
+
+    @tornado.gen.coroutine
+    def stdout_loop(self):
+        try:
+            while not self.closed:
+                buffer = yield self.socket_stream.read_bytes(1024, partial = True)
+                yield self.stdout_stream.write(buffer.replace("\r\n", "\n"))
+        except tornado.iostream.StreamClosedError:
+            self.closed = True
+
+
+ at tornado.gen.coroutine
+def main():
+    parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument("-v", "--verbose",  action = "store_true",  help = "produce human-readable output")
+    parser.add_argument("-d", "--debug",    action = "store_true",  help = "blather about what we're doing")
+
+    parser.add_argument("--cty-socket",            help = "CTY PF_UNIX socket name",
+                        default = os.getenv("CRYPTECH_CTY_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.cty"))
+
+    args = parser.parse_args()
+
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.connect(args.cty_socket)
+
+    term = FemtoTerm(s)
+
+    if False:
+        yield [term.stdin_loop(), term.stdout_loop()]
+
+    else:
+        stdout_future = term.stdout_loop()
+        stdin_future  = term.stdin_loop()
+        yield stdout_future
+        sys.stdin.close()
+        yield stdin_future
+
+
+if __name__ == "__main__":
+    try:
+        tornado.ioloop.IOLoop.current().run_sync(main)
+    except KeyboardInterrupt:
+        pass
diff --git a/cryptech_rpcmuxd b/cryptech_muxd
similarity index 56%
rename from cryptech_rpcmuxd
rename to cryptech_muxd
index d08d6df..80be443 100755
--- a/cryptech_rpcmuxd
+++ b/cryptech_muxd
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright (c) 2016, NORDUnet A/S All rights reserved.
+# Copyright (c) 2016-2017, NORDUnet A/S All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -87,14 +87,9 @@ class SerialIOStream(tornado.iostream.BaseIOStream):
 
     def __init__(self, device, baudrate = 921600, debug = False, *pargs, **kwargs):
         self.serial = serial.Serial(device, baudrate, timeout = 0, write_timeout = 0)
-        self.queues = weakref.WeakValueDictionary()
-        self.debug = debug
-        self.hsm_write_lock = tornado.locks.Lock()
+        self.debug  = debug
         super(SerialIOStream, self).__init__(*pargs, **kwargs)
 
-    # The next four methods are required: BaseIOStream is an abstract
-    # class, we provide a driver by overriding them.
-
     def fileno(self):
         return self.serial.fileno()
 
@@ -107,17 +102,46 @@ class SerialIOStream(tornado.iostream.BaseIOStream):
     def read_from_fd(self):
         return self.serial.read(self.read_chunk_size) or None
 
+
+class PFUnixServer(tornado.tcpserver.TCPServer):
+    """
+    Variant on tornado.tcpserver.TCPServer, listening on a PF_UNIX
+    (aka PF_LOCAL) socket instead of a TCP socket.
+    """
+
+    def listen(self, filename, mode = 0600):
+        self.socket_filename = filename
+        self.add_socket(tornado.netutil.bind_unix_socket(filename, mode))
+        atexit.register(self.atexit_unlink)
+
+    def atexit_unlink(self):
+        try:
+            os.unlink(self.socket_filename)
+        except:
+            pass
+
+
+class RPCIOStream(SerialIOStream):
+    """
+    Tornado IOStream for a serial RPC channel.
+    """
+
+    def __init__(self, *pargs, **kwargs):
+        super(RPCIOStream, self).__init__(*pargs, **kwargs)
+        self.queues = weakref.WeakValueDictionary()
+        self.rpc_input_lock = tornado.locks.Lock()
+
     @tornado.gen.coroutine
-    def hsm_write(self, query, handle, queue):
+    def rpc_input(self, query, handle, queue):
         "Send a query to the HSM."
         if self.debug:
             sys.stdout.write("+send: {}\n".format(":".join("{:02x}".format(ord(c)) for c in query)))
         self.queues[handle] = queue
-        with (yield self.hsm_write_lock.acquire()):
+        with (yield self.rpc_input_lock.acquire()):
             yield self.write(query)
 
     @tornado.gen.coroutine
-    def hsm_read_loop(self):
+    def rpc_output_loop(self):
         "Handle reply stream HSM -> network."
         while True:
             reply = yield self.read_until(SLIP_END)
@@ -129,15 +153,11 @@ class SerialIOStream(tornado.iostream.BaseIOStream):
             self.queues[handle].put_nowait(reply)
 
 
-class UnixServer(tornado.tcpserver.TCPServer):
+class RPCServer(PFUnixServer):
     """
-    Variant on tornado.tcpserver.TCPServer, listening on a PF_UNIX
-    (aka PF_LOCAL) socket instead of a TCP socket.
+    Serve multiplexed Cryptech RPC over a PF_UNIX socket.
     """
 
-    def listen(self, filename, mode = 0600):
-        self.add_socket(tornado.netutil.bind_unix_socket(filename, mode))
-
     def set_serial(self, serial_stream):
         self.serial = serial_stream
 
@@ -153,28 +173,100 @@ class UnixServer(tornado.tcpserver.TCPServer):
                 if len(query) < 9:
                     continue
                 query = slip_encode(client_handle_set(slip_decode(query), handle))
-                yield self.serial.hsm_write(query, handle, queue)
+                yield self.serial.rpc_input(query, handle, queue)
                 reply = yield queue.get()
                 yield stream.write(SLIP_END + reply)
             except tornado.iostream.StreamClosedError:
                 closed = True
 
+class CTYIOStream(SerialIOStream):
+    """
+    Tornado IOStream for a serial console channel.
+    """
 
-parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
-parser.add_argument("-v", "--verbose",  action = "store_true",  help = "produce human-readable output")
-parser.add_argument("-d", "--debug",    action = "store_true",  help = "blather about what we're doing")
-parser.add_argument("device",           nargs = "?",            help = "serial device name",
-                    default = os.getenv("CRYPTECH_RPC_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"))
-parser.add_argument("socket",           nargs = "?",            help = "PF_UNIX socket name",
-                    default = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", "/tmp/.cryptech_rpcmuxd"))
-args = parser.parse_args()
+    def __init__(self, *pargs, **kwargs):
+        super(CTYIOStream, self).__init__(*pargs, **kwargs)
+        self.attached_cty = None
 
-serial_stream = SerialIOStream(device = args.device, debug = args.debug)
+    @tornado.gen.coroutine
+    def cty_output_loop(self):
+        while True:
+            buffer = yield self.read_bytes(1024, partial = True)
+            try:
+                if self.attached_cty is not None:
+                    yield self.attached_cty.write(buffer)
+            except tornado.iostream.StreamClosedError:
+                pass
 
-unix_server = UnixServer()
-unix_server.set_serial(serial_stream)
-unix_server.listen(args.socket)
 
-atexit.register(os.unlink, args.socket)
+class CTYServer(PFUnixServer):
+    """
+    Serve Cryptech console over a PF_UNIX socket.
+    """
+
+    def set_serial(self, serial_stream):
+        self.serial = serial_stream
+
+    @tornado.gen.coroutine
+    def handle_stream(self, stream, address):
+        "Handle one network connection."
 
-tornado.ioloop.IOLoop.current().run_sync(serial_stream.hsm_read_loop)
+        if self.serial.attached_cty is not None:
+            yield stream.write("[Console already in use, sorry]\n")
+            stream.close()
+            return
+
+        try:
+            self.serial.attached_cty = stream
+            while self.serial.attached_cty is stream:
+                yield self.serial.write((yield stream.read_bytes(1024, partial = True)))
+        except tornado.iostream.StreamClosedError:
+            pass
+        finally:
+            if self.serial.attached_cty is stream:
+                self.serial.attached_cty = None
+
+
+ at tornado.gen.coroutine
+def main():
+    parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument("-v", "--verbose",  action = "store_true",  help = "produce human-readable output")
+    parser.add_argument("-d", "--debug",    action = "store_true",  help = "blather about what we're doing")
+
+    parser.add_argument("--rpc-device",            help = "RPC serial device name",
+                        default = os.getenv("CRYPTECH_RPC_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"))
+    parser.add_argument("--rpc-socket",            help = "RPC PF_UNIX socket name",
+                        default = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.rpc"))
+
+    parser.add_argument("--cty-device",            help = "CTY serial device name",
+                        default = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"))
+    parser.add_argument("--cty-socket",            help = "CTY PF_UNIX socket name",
+                        default = os.getenv("CRYPTECH_CTY_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.cty"))
+
+    args = parser.parse_args()
+
+    futures = []
+
+    rpc_stream = RPCIOStream(device = args.rpc_device, debug = args.debug)
+    rpc_server = RPCServer()
+    rpc_server.set_serial(rpc_stream)
+    rpc_server.listen(args.rpc_socket)
+    futures.append(rpc_stream.rpc_output_loop())
+
+    cty_stream = CTYIOStream(device = args.cty_device, debug = args.debug)
+    cty_server = CTYServer()
+    cty_server.set_serial(cty_stream)
+    cty_server.listen(args.cty_socket)
+    futures.append(cty_stream.cty_output_loop())
+
+    # Might want to use WaitIterator(dict(...)) here so we can
+    # diagnose and restart output loops if they fail.  Worry about
+    # that when we get to automatic device probing.
+
+    yield futures
+
+if __name__ == "__main__":
+    try:
+        tornado.ioloop.IOLoop.current().run_sync(main)
+    except KeyboardInterrupt:
+        pass
diff --git a/hal_internal.h b/hal_internal.h
index ef3dd49..69d9e67 100644
--- a/hal_internal.h
+++ b/hal_internal.h
@@ -880,7 +880,7 @@ typedef enum {
  */
 
 #ifndef HAL_CLIENT_DAEMON_DEFAULT_SOCKET_NAME
-#define HAL_CLIENT_DAEMON_DEFAULT_SOCKET_NAME   "/tmp/.cryptech_rpcmuxd"
+#define HAL_CLIENT_DAEMON_DEFAULT_SOCKET_NAME   "/tmp/.cryptech_muxd.rpc"
 #endif
 
 /*
diff --git a/libhal.py b/libhal.py
index 1e4ff02..e899d7b 100644
--- a/libhal.py
+++ b/libhal.py
@@ -407,7 +407,7 @@ class HSM(object):
         if status != 0:
             raise HALError.table[status]()
 
-    def __init__(self, sockname = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", "/tmp/.cryptech_rpcmuxd")):
+    def __init__(self, sockname = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.rpc")):
         self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
         self.socket.connect(sockname)
         self.sockfile = self.socket.makefile("rb")

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Commits mailing list