/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2016-02-21 14:24:01 UTC
  • Revision ID: teddy@recompile.se-20160221142401-j7glu6a2hg604d1e
Use AddressSanitizer and UndefinedBehaviorSanitizer.

* Makefile (SANITIZE): New; set to all sanitizing options depending on
                       GCC version.
  (CFLAGS): Added "$(SANITIZE)".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
 
1
#!/usr/bin/python2.7
2
2
# -*- mode: python; coding: utf-8 -*-
3
3
4
4
# Mandos server - give out binary blobs to connecting clients.
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2016 Teddy Hogeborn
15
 
# Copyright © 2008-2016 Björn Påhlsson
 
14
# Copyright © 2008-2015 Teddy Hogeborn
 
15
# Copyright © 2008-2015 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
44
44
import argparse
45
45
import datetime
46
46
import errno
 
47
import gnutls.connection
 
48
import gnutls.errors
 
49
import gnutls.library.functions
 
50
import gnutls.library.constants
 
51
import gnutls.library.types
47
52
try:
48
53
    import ConfigParser as configparser
49
54
except ImportError:
151
156
    
152
157
    def __init__(self):
153
158
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
154
 
        self.gpg = "gpg"
155
 
        try:
156
 
            output = subprocess.check_output(["gpgconf"])
157
 
            for line in output.splitlines():
158
 
                name, text, path = line.split(":")
159
 
                if name == "gpg":
160
 
                    self.gpg = path
161
 
                    break
162
 
        except OSError as e:
163
 
            if e.errno != errno.ENOENT:
164
 
                raise
165
159
        self.gnupgargs = ['--batch',
166
 
                          '--homedir', self.tempdir,
 
160
                          '--home', self.tempdir,
167
161
                          '--force-mdc',
168
162
                          '--quiet',
169
163
                          '--no-use-agent']
208
202
                dir=self.tempdir) as passfile:
209
203
            passfile.write(passphrase)
210
204
            passfile.flush()
211
 
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
205
            proc = subprocess.Popen(['gpg', '--symmetric',
212
206
                                     '--passphrase-file',
213
207
                                     passfile.name]
214
208
                                    + self.gnupgargs,
226
220
                dir = self.tempdir) as passfile:
227
221
            passfile.write(passphrase)
228
222
            passfile.flush()
229
 
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
223
            proc = subprocess.Popen(['gpg', '--decrypt',
230
224
                                     '--passphrase-file',
231
225
                                     passfile.name]
232
226
                                    + self.gnupgargs,
440
434
            .format(self.name)))
441
435
        return ret
442
436
 
443
 
# Pretend that we have a GnuTLS module
444
 
class GnuTLS(object):
445
 
    """This isn't so much a class as it is a module-like namespace.
446
 
    It is instantiated once, and simulates having a GnuTLS module."""
447
 
    
448
 
    _library = ctypes.cdll.LoadLibrary(
449
 
        ctypes.util.find_library("gnutls"))
450
 
    _need_version = "3.3.0"
451
 
    def __init__(self):
452
 
        # Need to use class name "GnuTLS" here, since this method is
453
 
        # called before the assignment to the "gnutls" global variable
454
 
        # happens.
455
 
        if GnuTLS.check_version(self._need_version) is None:
456
 
            raise GnuTLS.Error("Needs GnuTLS {} or later"
457
 
                               .format(self._need_version))
458
 
    
459
 
    # Unless otherwise indicated, the constants and types below are
460
 
    # all from the gnutls/gnutls.h C header file.
461
 
    
462
 
    # Constants
463
 
    E_SUCCESS = 0
464
 
    E_INTERRUPTED = -52
465
 
    E_AGAIN = -28
466
 
    CRT_OPENPGP = 2
467
 
    CLIENT = 2
468
 
    SHUT_RDWR = 0
469
 
    CRD_CERTIFICATE = 1
470
 
    E_NO_CERTIFICATE_FOUND = -49
471
 
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
472
 
    
473
 
    # Types
474
 
    class session_int(ctypes.Structure):
475
 
        _fields_ = []
476
 
    session_t = ctypes.POINTER(session_int)
477
 
    class certificate_credentials_st(ctypes.Structure):
478
 
        _fields_ = []
479
 
    certificate_credentials_t = ctypes.POINTER(
480
 
        certificate_credentials_st)
481
 
    certificate_type_t = ctypes.c_int
482
 
    class datum_t(ctypes.Structure):
483
 
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
484
 
                    ('size', ctypes.c_uint)]
485
 
    class openpgp_crt_int(ctypes.Structure):
486
 
        _fields_ = []
487
 
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
488
 
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
489
 
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
490
 
    credentials_type_t = ctypes.c_int # 
491
 
    transport_ptr_t = ctypes.c_void_p
492
 
    close_request_t = ctypes.c_int
493
 
    
494
 
    # Exceptions
495
 
    class Error(Exception):
496
 
        # We need to use the class name "GnuTLS" here, since this
497
 
        # exception might be raised from within GnuTLS.__init__,
498
 
        # which is called before the assignment to the "gnutls"
499
 
        # global variable has happened.
500
 
        def __init__(self, message = None, code = None, args=()):
501
 
            # Default usage is by a message string, but if a return
502
 
            # code is passed, convert it to a string with
503
 
            # gnutls.strerror()
504
 
            self.code = code
505
 
            if message is None and code is not None:
506
 
                message = GnuTLS.strerror(code)
507
 
            return super(GnuTLS.Error, self).__init__(
508
 
                message, *args)
509
 
    
510
 
    class CertificateSecurityError(Error):
511
 
        pass
512
 
    
513
 
    # Classes
514
 
    class Credentials(object):
515
 
        def __init__(self):
516
 
            self._c_object = gnutls.certificate_credentials_t()
517
 
            gnutls.certificate_allocate_credentials(
518
 
                ctypes.byref(self._c_object))
519
 
            self.type = gnutls.CRD_CERTIFICATE
520
 
        
521
 
        def __del__(self):
522
 
            gnutls.certificate_free_credentials(self._c_object)
523
 
    
524
 
    class ClientSession(object):
525
 
        def __init__(self, socket, credentials = None):
526
 
            self._c_object = gnutls.session_t()
527
 
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
528
 
            gnutls.set_default_priority(self._c_object)
529
 
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
530
 
            gnutls.handshake_set_private_extensions(self._c_object,
531
 
                                                    True)
532
 
            self.socket = socket
533
 
            if credentials is None:
534
 
                credentials = gnutls.Credentials()
535
 
            gnutls.credentials_set(self._c_object, credentials.type,
536
 
                                   ctypes.cast(credentials._c_object,
537
 
                                               ctypes.c_void_p))
538
 
            self.credentials = credentials
539
 
        
540
 
        def __del__(self):
541
 
            gnutls.deinit(self._c_object)
542
 
        
543
 
        def handshake(self):
544
 
            return gnutls.handshake(self._c_object)
545
 
        
546
 
        def send(self, data):
547
 
            data = bytes(data)
548
 
            data_len = len(data)
549
 
            while data_len > 0:
550
 
                data_len -= gnutls.record_send(self._c_object,
551
 
                                               data[-data_len:],
552
 
                                               data_len)
553
 
        
554
 
        def bye(self):
555
 
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
556
 
    
557
 
    # Error handling functions
558
 
    def _error_code(result):
559
 
        """A function to raise exceptions on errors, suitable
560
 
        for the 'restype' attribute on ctypes functions"""
561
 
        if result >= 0:
562
 
            return result
563
 
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
564
 
            raise gnutls.CertificateSecurityError(code = result)
565
 
        raise gnutls.Error(code = result)
566
 
    
567
 
    def _retry_on_error(result, func, arguments):
568
 
        """A function to retry on some errors, suitable
569
 
        for the 'errcheck' attribute on ctypes functions"""
570
 
        while result < 0:
571
 
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
572
 
                return _error_code(result)
573
 
            result = func(*arguments)
574
 
        return result
575
 
    
576
 
    # Unless otherwise indicated, the function declarations below are
577
 
    # all from the gnutls/gnutls.h C header file.
578
 
    
579
 
    # Functions
580
 
    priority_set_direct = _library.gnutls_priority_set_direct
581
 
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
582
 
                                    ctypes.POINTER(ctypes.c_char_p)]
583
 
    priority_set_direct.restype = _error_code
584
 
    
585
 
    init = _library.gnutls_init
586
 
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
587
 
    init.restype = _error_code
588
 
    
589
 
    set_default_priority = _library.gnutls_set_default_priority
590
 
    set_default_priority.argtypes = [session_t]
591
 
    set_default_priority.restype = _error_code
592
 
    
593
 
    record_send = _library.gnutls_record_send
594
 
    record_send.argtypes = [session_t, ctypes.c_void_p,
595
 
                            ctypes.c_size_t]
596
 
    record_send.restype = ctypes.c_ssize_t
597
 
    record_send.errcheck = _retry_on_error
598
 
    
599
 
    certificate_allocate_credentials = (
600
 
        _library.gnutls_certificate_allocate_credentials)
601
 
    certificate_allocate_credentials.argtypes = [
602
 
        ctypes.POINTER(certificate_credentials_t)]
603
 
    certificate_allocate_credentials.restype = _error_code
604
 
    
605
 
    certificate_free_credentials = (
606
 
        _library.gnutls_certificate_free_credentials)
607
 
    certificate_free_credentials.argtypes = [certificate_credentials_t]
608
 
    certificate_free_credentials.restype = None
609
 
    
610
 
    handshake_set_private_extensions = (
611
 
        _library.gnutls_handshake_set_private_extensions)
612
 
    handshake_set_private_extensions.argtypes = [session_t,
613
 
                                                 ctypes.c_int]
614
 
    handshake_set_private_extensions.restype = None
615
 
    
616
 
    credentials_set = _library.gnutls_credentials_set
617
 
    credentials_set.argtypes = [session_t, credentials_type_t,
618
 
                                ctypes.c_void_p]
619
 
    credentials_set.restype = _error_code
620
 
    
621
 
    strerror = _library.gnutls_strerror
622
 
    strerror.argtypes = [ctypes.c_int]
623
 
    strerror.restype = ctypes.c_char_p
624
 
    
625
 
    certificate_type_get = _library.gnutls_certificate_type_get
626
 
    certificate_type_get.argtypes = [session_t]
627
 
    certificate_type_get.restype = _error_code
628
 
    
629
 
    certificate_get_peers = _library.gnutls_certificate_get_peers
630
 
    certificate_get_peers.argtypes = [session_t,
631
 
                                      ctypes.POINTER(ctypes.c_uint)]
632
 
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
633
 
    
634
 
    global_set_log_level = _library.gnutls_global_set_log_level
635
 
    global_set_log_level.argtypes = [ctypes.c_int]
636
 
    global_set_log_level.restype = None
637
 
    
638
 
    global_set_log_function = _library.gnutls_global_set_log_function
639
 
    global_set_log_function.argtypes = [log_func]
640
 
    global_set_log_function.restype = None
641
 
    
642
 
    deinit = _library.gnutls_deinit
643
 
    deinit.argtypes = [session_t]
644
 
    deinit.restype = None
645
 
    
646
 
    handshake = _library.gnutls_handshake
647
 
    handshake.argtypes = [session_t]
648
 
    handshake.restype = _error_code
649
 
    handshake.errcheck = _retry_on_error
650
 
    
651
 
    transport_set_ptr = _library.gnutls_transport_set_ptr
652
 
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
653
 
    transport_set_ptr.restype = None
654
 
    
655
 
    bye = _library.gnutls_bye
656
 
    bye.argtypes = [session_t, close_request_t]
657
 
    bye.restype = _error_code
658
 
    bye.errcheck = _retry_on_error
659
 
    
660
 
    check_version = _library.gnutls_check_version
661
 
    check_version.argtypes = [ctypes.c_char_p]
662
 
    check_version.restype = ctypes.c_char_p
663
 
    
664
 
    # All the function declarations below are from gnutls/openpgp.h
665
 
    
666
 
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
667
 
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
668
 
    openpgp_crt_init.restype = _error_code
669
 
    
670
 
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
671
 
    openpgp_crt_import.argtypes = [openpgp_crt_t,
672
 
                                   ctypes.POINTER(datum_t),
673
 
                                   openpgp_crt_fmt_t]
674
 
    openpgp_crt_import.restype = _error_code
675
 
    
676
 
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
677
 
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
678
 
                                        ctypes.POINTER(ctypes.c_uint)]
679
 
    openpgp_crt_verify_self.restype = _error_code
680
 
    
681
 
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
682
 
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
683
 
    openpgp_crt_deinit.restype = None
684
 
    
685
 
    openpgp_crt_get_fingerprint = (
686
 
        _library.gnutls_openpgp_crt_get_fingerprint)
687
 
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
688
 
                                            ctypes.c_void_p,
689
 
                                            ctypes.POINTER(
690
 
                                                ctypes.c_size_t)]
691
 
    openpgp_crt_get_fingerprint.restype = _error_code
692
 
    
693
 
    # Remove non-public functions
694
 
    del _error_code, _retry_on_error
695
 
# Create the global "gnutls" object, simulating a module
696
 
gnutls = GnuTLS()
697
 
 
698
437
def call_pipe(connection,       # : multiprocessing.Connection
699
438
              func, *args, **kwargs):
700
439
    """This function is meant to be called by multiprocessing.Process
2140
1879
            logger.debug("Pipe FD: %d",
2141
1880
                         self.server.child_pipe.fileno())
2142
1881
            
2143
 
            session = gnutls.ClientSession(self.request)
 
1882
            session = gnutls.connection.ClientSession(
 
1883
                self.request, gnutls.connection.X509Credentials())
 
1884
            
 
1885
            # Note: gnutls.connection.X509Credentials is really a
 
1886
            # generic GnuTLS certificate credentials object so long as
 
1887
            # no X.509 keys are added to it.  Therefore, we can use it
 
1888
            # here despite using OpenPGP certificates.
2144
1889
            
2145
1890
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
2146
1891
            #                      "+AES-256-CBC", "+SHA1",
2150
1895
            priority = self.server.gnutls_priority
2151
1896
            if priority is None:
2152
1897
                priority = "NORMAL"
2153
 
            gnutls.priority_set_direct(session._c_object, priority,
2154
 
                                       None)
 
1898
            gnutls.library.functions.gnutls_priority_set_direct(
 
1899
                session._c_object, priority, None)
2155
1900
            
2156
1901
            # Start communication using the Mandos protocol
2157
1902
            # Get protocol number
2167
1912
            # Start GnuTLS connection
2168
1913
            try:
2169
1914
                session.handshake()
2170
 
            except gnutls.Error as error:
 
1915
            except gnutls.errors.GNUTLSError as error:
2171
1916
                logger.warning("Handshake failed: %s", error)
2172
1917
                # Do not run session.bye() here: the session is not
2173
1918
                # established.  Just abandon the request.
2179
1924
                try:
2180
1925
                    fpr = self.fingerprint(
2181
1926
                        self.peer_certificate(session))
2182
 
                except (TypeError, gnutls.Error) as error:
 
1927
                except (TypeError,
 
1928
                        gnutls.errors.GNUTLSError) as error:
2183
1929
                    logger.warning("Bad certificate: %s", error)
2184
1930
                    return
2185
1931
                logger.debug("Fingerprint: %s", fpr)
2243
1989
                    else:
2244
1990
                        delay -= time2 - time
2245
1991
                
2246
 
                try:
2247
 
                    session.send(client.secret)
2248
 
                except gnutls.Error as error:
2249
 
                    logger.warning("gnutls send failed",
2250
 
                                   exc_info = error)
2251
 
                    return
 
1992
                sent_size = 0
 
1993
                while sent_size < len(client.secret):
 
1994
                    try:
 
1995
                        sent = session.send(client.secret[sent_size:])
 
1996
                    except gnutls.errors.GNUTLSError as error:
 
1997
                        logger.warning("gnutls send failed",
 
1998
                                       exc_info=error)
 
1999
                        return
 
2000
                    logger.debug("Sent: %d, remaining: %d", sent,
 
2001
                                 len(client.secret) - (sent_size
 
2002
                                                       + sent))
 
2003
                    sent_size += sent
2252
2004
                
2253
2005
                logger.info("Sending secret to %s", client.name)
2254
2006
                # bump the timeout using extended_timeout
2262
2014
                    client.approvals_pending -= 1
2263
2015
                try:
2264
2016
                    session.bye()
2265
 
                except gnutls.Error as error:
 
2017
                except gnutls.errors.GNUTLSError as error:
2266
2018
                    logger.warning("GnuTLS bye failed",
2267
2019
                                   exc_info=error)
2268
2020
    
2270
2022
    def peer_certificate(session):
2271
2023
        "Return the peer's OpenPGP certificate as a bytestring"
2272
2024
        # If not an OpenPGP certificate...
2273
 
        if (gnutls.certificate_type_get(session._c_object)
2274
 
            != gnutls.CRT_OPENPGP):
2275
 
            # ...return invalid data
2276
 
            return b""
 
2025
        if (gnutls.library.functions.gnutls_certificate_type_get(
 
2026
                session._c_object)
 
2027
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
 
2028
            # ...do the normal thing
 
2029
            return session.peer_certificate
2277
2030
        list_size = ctypes.c_uint(1)
2278
 
        cert_list = (gnutls.certificate_get_peers
 
2031
        cert_list = (gnutls.library.functions
 
2032
                     .gnutls_certificate_get_peers
2279
2033
                     (session._c_object, ctypes.byref(list_size)))
2280
2034
        if not bool(cert_list) and list_size.value != 0:
2281
 
            raise gnutls.Error("error getting peer certificate")
 
2035
            raise gnutls.errors.GNUTLSError("error getting peer"
 
2036
                                            " certificate")
2282
2037
        if list_size.value == 0:
2283
2038
            return None
2284
2039
        cert = cert_list[0]
2288
2043
    def fingerprint(openpgp):
2289
2044
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
2290
2045
        # New GnuTLS "datum" with the OpenPGP public key
2291
 
        datum = gnutls.datum_t(
 
2046
        datum = gnutls.library.types.gnutls_datum_t(
2292
2047
            ctypes.cast(ctypes.c_char_p(openpgp),
2293
2048
                        ctypes.POINTER(ctypes.c_ubyte)),
2294
2049
            ctypes.c_uint(len(openpgp)))
2295
2050
        # New empty GnuTLS certificate
2296
 
        crt = gnutls.openpgp_crt_t()
2297
 
        gnutls.openpgp_crt_init(ctypes.byref(crt))
 
2051
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
2052
        gnutls.library.functions.gnutls_openpgp_crt_init(
 
2053
            ctypes.byref(crt))
2298
2054
        # Import the OpenPGP public key into the certificate
2299
 
        gnutls.openpgp_crt_import(crt, ctypes.byref(datum),
2300
 
                                  gnutls.OPENPGP_FMT_RAW)
 
2055
        gnutls.library.functions.gnutls_openpgp_crt_import(
 
2056
            crt, ctypes.byref(datum),
 
2057
            gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
2301
2058
        # Verify the self signature in the key
2302
2059
        crtverify = ctypes.c_uint()
2303
 
        gnutls.openpgp_crt_verify_self(crt, 0,
2304
 
                                       ctypes.byref(crtverify))
 
2060
        gnutls.library.functions.gnutls_openpgp_crt_verify_self(
 
2061
            crt, 0, ctypes.byref(crtverify))
2305
2062
        if crtverify.value != 0:
2306
 
            gnutls.openpgp_crt_deinit(crt)
2307
 
            raise gnutls.CertificateSecurityError("Verify failed")
 
2063
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
2064
            raise gnutls.errors.CertificateSecurityError(
 
2065
                "Verify failed")
2308
2066
        # New buffer for the fingerprint
2309
2067
        buf = ctypes.create_string_buffer(20)
2310
2068
        buf_len = ctypes.c_size_t()
2311
2069
        # Get the fingerprint from the certificate into the buffer
2312
 
        gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
2313
 
                                           ctypes.byref(buf_len))
 
2070
        gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint(
 
2071
            crt, ctypes.byref(buf), ctypes.byref(buf_len))
2314
2072
        # Deinit the certificate
2315
 
        gnutls.openpgp_crt_deinit(crt)
 
2073
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
2316
2074
        # Convert the buffer to a Python bytestring
2317
2075
        fpr = ctypes.string_at(buf, buf_len.value)
2318
2076
        # Convert the bytestring to hexadecimal notation
2944
2702
        
2945
2703
        # "Use a log level over 10 to enable all debugging options."
2946
2704
        # - GnuTLS manual
2947
 
        gnutls.global_set_log_level(11)
 
2705
        gnutls.library.functions.gnutls_global_set_log_level(11)
2948
2706
        
2949
 
        @gnutls.log_func
 
2707
        @gnutls.library.types.gnutls_log_func
2950
2708
        def debug_gnutls(level, string):
2951
2709
            logger.debug("GnuTLS: %s", string[:-1])
2952
2710
        
2953
 
        gnutls.global_set_log_function(debug_gnutls)
 
2711
        gnutls.library.functions.gnutls_global_set_log_function(
 
2712
            debug_gnutls)
2954
2713
        
2955
2714
        # Redirect stdin so all checkers get /dev/null
2956
2715
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)