=== modified file 'INSTALL' --- INSTALL 2014-07-25 22:44:20 +0000 +++ INSTALL 2016-02-28 03:01:43 +0000 @@ -4,7 +4,7 @@ ** Operating System - Debian 6.0 "squeeze" or Ubuntu 10.10 "Maverick Meerkat" (or later). + Debian 8.0 "jessie" or Ubuntu 15.10 "Wily Werewolf" (or later). This is mostly for the support scripts which make sure that the client is installed and started in the initial RAM disk environment @@ -38,11 +38,9 @@ "man -l mandos.8". *** Mandos Server - + GnuTLS 2.4 http://www.gnutls.org/ - Note: GnuTLS 3 will only work with Python-GnuTLS 2 + + GnuTLS 3.3 http://www.gnutls.org/ + Avahi 0.6.16 http://www.avahi.org/ + Python 2.7 https://www.python.org/ - + Python-GnuTLS 1.1.5 https://pypi.python.org/pypi/python-gnutls/ + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + PyGObject 2.14.2 https://developer.gnome.org/pygobject/ + pkg-config http://www.freedesktop.org/wiki/Software/pkg-config/ @@ -54,13 +52,13 @@ + ssh-keyscan from OpenSSH http://www.openssh.com/ Package names: - python-gnutls avahi-daemon python python-avahi python-dbus - python-gobject python-urwid pkg-config fping ssh-client + avahi-daemon python python-avahi python-dbus python-gobject + python-urwid pkg-config fping ssh-client *** Mandos Client + initramfs-tools 0.85i https://tracker.debian.org/pkg/initramfs-tools - + GnuTLS 2.4 http://www.gnutls.org/ + + GnuTLS 3.3 http://www.gnutls.org/ + Avahi 0.6.16 http://www.avahi.org/ + GnuPG 1.4.9 https://www.gnupg.org/ + GPGME 1.1.6 https://www.gnupg.org/related_software/gpgme/ === modified file 'debian/control' --- debian/control 2015-12-03 21:04:24 +0000 +++ debian/control 2016-02-28 03:01:43 +0000 @@ -5,10 +5,11 @@ Uploaders: Teddy Hogeborn , Björn Påhlsson Build-Depends: debhelper (>= 9), docbook-xml, docbook-xsl, - libavahi-core-dev, libgpgme11-dev, libgnutls28-dev - | gnutls-dev, xsltproc, pkg-config, libnl-route-3-dev -Build-Depends-Indep: systemd, python2.7, python2.7-gnutls, - python2.7-dbus, python2.7-avahi, python2.7-gobject + libavahi-core-dev, libgpgme11-dev, libgnutls28-dev (>= 3.3.0) + | gnutls-dev (>= 3.3.0), xsltproc, pkg-config, + libnl-route-3-dev +Build-Depends-Indep: systemd, python2.7, python2.7-dbus, + python2.7-avahi, python2.7-gobject Standards-Version: 3.9.6 Vcs-Bzr: http://ftp.recompile.se/pub/mandos/trunk Vcs-Browser: http://bzr.recompile.se/loggerhead/mandos/trunk/files @@ -16,8 +17,9 @@ Package: mandos Architecture: all -Depends: ${misc:Depends}, python (>= 2.7), python2.7, python-gnutls, - python2.7-gnutls, python-dbus, python2.7-dbus, python-avahi, +Depends: ${misc:Depends}, python (>= 2.7), python2.7, + libgnutls28-dev (>= 3.3.0) | libgnutls30 (>= 3.3.0), + python-dbus, python2.7-dbus, python-avahi, python2.7-avahi, python-gobject, python2.7-gobject, avahi-daemon, adduser, python-urwid, python2.7-urwid, gnupg (<< 2) === modified file 'mandos' --- mandos 2016-02-21 13:00:15 +0000 +++ mandos 2016-02-28 03:01:43 +0000 @@ -44,11 +44,6 @@ import argparse import datetime import errno -import gnutls.connection -import gnutls.errors -import gnutls.library.functions -import gnutls.library.constants -import gnutls.library.types try: import ConfigParser as configparser except ImportError: @@ -434,6 +429,244 @@ .format(self.name))) return ret +# Pretend that we have a GnuTLS module +class GnuTLS(object): + """This isn't so much a class as it is a module-like namespace. + It is instantiated once, and simulates having a GnuTLS module.""" + + _library = ctypes.cdll.LoadLibrary( + ctypes.util.find_library("gnutls")) + _need_version = "3.3.0" + def __init__(self): + # Need to use class name "GnuTLS" here, since this method is + # called before the assignment to the "gnutls" global variable + # happens. + if GnuTLS.check_version(self._need_version) is None: + raise GnuTLS.Error("Needs GnuTLS {} or later" + .format(self._need_version)) + + # Unless otherwise indicated, the constants and types below are + # all from the gnutls/gnutls.h C header file. + + # Constants + E_SUCCESS = 0 + CRT_OPENPGP = 2 + CLIENT = 2 + SHUT_RDWR = 0 + CRD_CERTIFICATE = 1 + E_NO_CERTIFICATE_FOUND = -49 + OPENPGP_FMT_RAW = 0 # gnutls/openpgp.h + + # Types + class session_int(ctypes.Structure): + _fields_ = [] + session_t = ctypes.POINTER(session_int) + class certificate_credentials_st(ctypes.Structure): + _fields_ = [] + certificate_credentials_t = ctypes.POINTER( + certificate_credentials_st) + certificate_type_t = ctypes.c_int + class datum_t(ctypes.Structure): + _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)), + ('size', ctypes.c_uint)] + class openpgp_crt_int(ctypes.Structure): + _fields_ = [] + openpgp_crt_t = ctypes.POINTER(openpgp_crt_int) + openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h + log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) + credentials_type_t = ctypes.c_int # + transport_ptr_t = ctypes.c_void_p + close_request_t = ctypes.c_int + + # Exceptions + class Error(Exception): + # We need to use the class name "GnuTLS" here, since this + # exception might be raised from within GnuTLS.__init__, + # which is called before the assignment to the "gnutls" + # global variable happens. + def __init__(self, message = None, code = None, args=()): + # Default usage is by a message string, but if a return + # code is passed, convert it to a string with + # gnutls.strerror() + if message is None and code is not None: + message = GnuTLS.strerror(code) + return super(GnuTLS.Error, self).__init__( + message, *args) + + class CertificateSecurityError(Error): + pass + + # Classes + class Credentials(object): + def __init__(self): + self._c_object = gnutls.certificate_credentials_t() + gnutls.certificate_allocate_credentials( + ctypes.byref(self._c_object)) + self.type = gnutls.CRD_CERTIFICATE + + def __del__(self): + gnutls.certificate_free_credentials(self._c_object) + + class ClientSession(object): + def __init__(self, socket, credentials = None): + self._c_object = gnutls.session_t() + gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT) + gnutls.set_default_priority(self._c_object) + gnutls.transport_set_ptr(self._c_object, socket.fileno()) + gnutls.handshake_set_private_extensions(self._c_object, + True) + self.socket = socket + if credentials is None: + credentials = gnutls.Credentials() + gnutls.credentials_set(self._c_object, credentials.type, + ctypes.cast(credentials._c_object, + ctypes.c_void_p)) + self.credentials = credentials + + def __del__(self): + gnutls.deinit(self._c_object) + + def handshake(self): + return gnutls.handshake(self._c_object) + + def send(self, data): + data = bytes(data) + if not data: + return 0 + return gnutls.record_send(self._c_object, data, len(data)) + + def bye(self): + return gnutls.bye(self._c_object, gnutls.SHUT_RDWR) + + # Error handling function + def _error_code(result): + """A function to raise exceptions on errors, suitable + for the 'restype' attribute on ctypes functions""" + if result >= 0: + return result + if result == gnutls.E_NO_CERTIFICATE_FOUND: + raise gnutls.CertificateSecurityError(code = result) + raise gnutls.Error(code = result) + + # Unless otherwise indicated, the function declarations below are + # all from the gnutls/gnutls.h C header file. + + # Functions + priority_set_direct = _library.gnutls_priority_set_direct + priority_set_direct.argtypes = [session_t, ctypes.c_char_p, + ctypes.POINTER(ctypes.c_char_p)] + priority_set_direct.restype = _error_code + + init = _library.gnutls_init + init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int] + init.restype = _error_code + + set_default_priority = _library.gnutls_set_default_priority + set_default_priority.argtypes = [session_t] + set_default_priority.restype = _error_code + + record_send = _library.gnutls_record_send + record_send.argtypes = [session_t, ctypes.c_void_p, + ctypes.c_size_t] + record_send.restype = ctypes.c_ssize_t + + certificate_allocate_credentials = ( + _library.gnutls_certificate_allocate_credentials) + certificate_allocate_credentials.argtypes = [ + ctypes.POINTER(certificate_credentials_t)] + certificate_allocate_credentials.restype = _error_code + + certificate_free_credentials = ( + _library.gnutls_certificate_free_credentials) + certificate_free_credentials.argtypes = [certificate_credentials_t] + certificate_free_credentials.restype = None + + handshake_set_private_extensions = ( + _library.gnutls_handshake_set_private_extensions) + handshake_set_private_extensions.argtypes = [session_t, + ctypes.c_int] + handshake_set_private_extensions.restype = None + + credentials_set = _library.gnutls_credentials_set + credentials_set.argtypes = [session_t, credentials_type_t, + ctypes.c_void_p] + credentials_set.restype = _error_code + + strerror = _library.gnutls_strerror + strerror.argtypes = [ctypes.c_int] + strerror.restype = ctypes.c_char_p + + certificate_type_get = _library.gnutls_certificate_type_get + certificate_type_get.argtypes = [session_t] + certificate_type_get.restype = _error_code + + certificate_get_peers = _library.gnutls_certificate_get_peers + certificate_get_peers.argtypes = [session_t, + ctypes.POINTER(ctypes.c_uint)] + certificate_get_peers.restype = ctypes.POINTER(datum_t) + + global_set_log_level = _library.gnutls_global_set_log_level + global_set_log_level.argtypes = [ctypes.c_int] + global_set_log_level.restype = None + + global_set_log_function = _library.gnutls_global_set_log_function + global_set_log_function.argtypes = [log_func] + global_set_log_function.restype = None + + deinit = _library.gnutls_deinit + deinit.argtypes = [session_t] + deinit.restype = None + + handshake = _library.gnutls_handshake + handshake.argtypes = [session_t] + handshake.restype = _error_code + + transport_set_ptr = _library.gnutls_transport_set_ptr + transport_set_ptr.argtypes = [session_t, transport_ptr_t] + transport_set_ptr.restype = None + + bye = _library.gnutls_bye + bye.argtypes = [session_t, close_request_t] + bye.restype = _error_code + + check_version = _library.gnutls_check_version + check_version.argtypes = [ctypes.c_char_p] + check_version.restype = ctypes.c_char_p + + # All the function declarations below are from gnutls/openpgp.h + + openpgp_crt_init = _library.gnutls_openpgp_crt_init + openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)] + openpgp_crt_init.restype = _error_code + + openpgp_crt_import = _library.gnutls_openpgp_crt_import + openpgp_crt_import.argtypes = [openpgp_crt_t, + ctypes.POINTER(datum_t), + openpgp_crt_fmt_t] + openpgp_crt_import.restype = _error_code + + openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self + openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint, + ctypes.POINTER(ctypes.c_uint)] + openpgp_crt_verify_self.restype = _error_code + + openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit + openpgp_crt_deinit.argtypes = [openpgp_crt_t] + openpgp_crt_deinit.restype = None + + openpgp_crt_get_fingerprint = ( + _library.gnutls_openpgp_crt_get_fingerprint) + openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t, + ctypes.c_void_p, + ctypes.POINTER( + ctypes.c_size_t)] + openpgp_crt_get_fingerprint.restype = _error_code + + # Remove non-public function + del _error_code +# Create the global "gnutls" object, simulating a module +gnutls = GnuTLS() + def call_pipe(connection, # : multiprocessing.Connection func, *args, **kwargs): """This function is meant to be called by multiprocessing.Process @@ -1879,13 +2112,7 @@ logger.debug("Pipe FD: %d", self.server.child_pipe.fileno()) - session = gnutls.connection.ClientSession( - self.request, gnutls.connection.X509Credentials()) - - # Note: gnutls.connection.X509Credentials is really a - # generic GnuTLS certificate credentials object so long as - # no X.509 keys are added to it. Therefore, we can use it - # here despite using OpenPGP certificates. + session = gnutls.ClientSession(self.request) #priority = ':'.join(("NONE", "+VERS-TLS1.1", # "+AES-256-CBC", "+SHA1", @@ -1895,8 +2122,8 @@ priority = self.server.gnutls_priority if priority is None: priority = "NORMAL" - gnutls.library.functions.gnutls_priority_set_direct( - session._c_object, priority, None) + gnutls.priority_set_direct(session._c_object, priority, + None) # Start communication using the Mandos protocol # Get protocol number @@ -1912,7 +2139,7 @@ # Start GnuTLS connection try: session.handshake() - except gnutls.errors.GNUTLSError as error: + except gnutls.Error as error: logger.warning("Handshake failed: %s", error) # Do not run session.bye() here: the session is not # established. Just abandon the request. @@ -1924,8 +2151,7 @@ try: fpr = self.fingerprint( self.peer_certificate(session)) - except (TypeError, - gnutls.errors.GNUTLSError) as error: + except (TypeError, gnutls.Error) as error: logger.warning("Bad certificate: %s", error) return logger.debug("Fingerprint: %s", fpr) @@ -1993,7 +2219,7 @@ while sent_size < len(client.secret): try: sent = session.send(client.secret[sent_size:]) - except gnutls.errors.GNUTLSError as error: + except gnutls.Error as error: logger.warning("gnutls send failed", exc_info=error) return @@ -2014,7 +2240,7 @@ client.approvals_pending -= 1 try: session.bye() - except gnutls.errors.GNUTLSError as error: + except gnutls.Error as error: logger.warning("GnuTLS bye failed", exc_info=error) @@ -2022,18 +2248,15 @@ def peer_certificate(session): "Return the peer's OpenPGP certificate as a bytestring" # If not an OpenPGP certificate... - if (gnutls.library.functions.gnutls_certificate_type_get( - session._c_object) - != gnutls.library.constants.GNUTLS_CRT_OPENPGP): - # ...do the normal thing - return session.peer_certificate + if (gnutls.certificate_type_get(session._c_object) + != gnutls.CRT_OPENPGP): + # ...return invalid data + return b"" list_size = ctypes.c_uint(1) - cert_list = (gnutls.library.functions - .gnutls_certificate_get_peers + cert_list = (gnutls.certificate_get_peers (session._c_object, ctypes.byref(list_size))) if not bool(cert_list) and list_size.value != 0: - raise gnutls.errors.GNUTLSError("error getting peer" - " certificate") + raise gnutls.Error("error getting peer certificate") if list_size.value == 0: return None cert = cert_list[0] @@ -2043,34 +2266,31 @@ def fingerprint(openpgp): "Convert an OpenPGP bytestring to a hexdigit fingerprint" # New GnuTLS "datum" with the OpenPGP public key - datum = gnutls.library.types.gnutls_datum_t( + datum = gnutls.datum_t( ctypes.cast(ctypes.c_char_p(openpgp), ctypes.POINTER(ctypes.c_ubyte)), ctypes.c_uint(len(openpgp))) # New empty GnuTLS certificate - crt = gnutls.library.types.gnutls_openpgp_crt_t() - gnutls.library.functions.gnutls_openpgp_crt_init( - ctypes.byref(crt)) + crt = gnutls.openpgp_crt_t() + gnutls.openpgp_crt_init(ctypes.byref(crt)) # Import the OpenPGP public key into the certificate - gnutls.library.functions.gnutls_openpgp_crt_import( - crt, ctypes.byref(datum), - gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW) + gnutls.openpgp_crt_import(crt, ctypes.byref(datum), + gnutls.OPENPGP_FMT_RAW) # Verify the self signature in the key crtverify = ctypes.c_uint() - gnutls.library.functions.gnutls_openpgp_crt_verify_self( - crt, 0, ctypes.byref(crtverify)) + gnutls.openpgp_crt_verify_self(crt, 0, + ctypes.byref(crtverify)) if crtverify.value != 0: - gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) - raise gnutls.errors.CertificateSecurityError( - "Verify failed") + gnutls.openpgp_crt_deinit(crt) + raise gnutls.CertificateSecurityError("Verify failed") # New buffer for the fingerprint buf = ctypes.create_string_buffer(20) buf_len = ctypes.c_size_t() # Get the fingerprint from the certificate into the buffer - gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint( - crt, ctypes.byref(buf), ctypes.byref(buf_len)) + gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), + ctypes.byref(buf_len)) # Deinit the certificate - gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) + gnutls.openpgp_crt_deinit(crt) # Convert the buffer to a Python bytestring fpr = ctypes.string_at(buf, buf_len.value) # Convert the bytestring to hexadecimal notation @@ -2702,14 +2922,13 @@ # "Use a log level over 10 to enable all debugging options." # - GnuTLS manual - gnutls.library.functions.gnutls_global_set_log_level(11) + gnutls.global_set_log_level(11) - @gnutls.library.types.gnutls_log_func + @gnutls.log_func def debug_gnutls(level, string): logger.debug("GnuTLS: %s", string[:-1]) - gnutls.library.functions.gnutls_global_set_log_function( - debug_gnutls) + gnutls.global_set_log_function(debug_gnutls) # Redirect stdin so all checkers get /dev/null null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)