/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: 2019-03-05 21:39:15 UTC
  • Revision ID: teddy@recompile.se-20190305213915-xm1vw00jyy3a5tfn
mandos-ctl: Add more tests, including tests for all commands

* mandos-ctl (Test_string_to_delta.test_handles_basic_rfc3339): Add a
                                                  few more test cases.
  (TestCmd.setUp.MockClient.Set, TestCmd.setUp.MockClient.Get): Don't
  append to self.calls, since nobody should use it to check for Set()
  or Get() calls; instead, the return value of Get() should be
  inspected, and the MockClient.attributes dict should be inspected
  after (implicitly) calling Set().
  (Unique): New; stand-in for unittest.mock.sentinel.
  (TestPropertyCmd): New; abstract class testing PropertyCmd classes.
  (TestBumpTimeoutCmd, TestStartCheckerCmd, TestStopCheckerCmd,
  TestApproveByDefaultCmd, TestDenyByDefaultCmd): New.
  (TestValueArgumentPropertyCmd): New; abstract class for testing
                                  those PropertyCmd classes which also
                                  inherit from ValueArgumentMixIn.
  (TestSetCheckerCmd, TestSetHostCmd, TestSetSecretCmd,
  TestSetTimeoutCmd, TestSetExtendedTimeoutCmd, TestSetIntervalCmd,
  TestSetApprovalDelayCmd, TestSetApprovalDurationCmd): New.

Show diffs side-by-side

added added

removed removed

Lines of Context:
80
80
 
81
81
import dbus
82
82
import dbus.service
83
 
import gi
84
83
from gi.repository import GLib
85
84
from dbus.mainloop.glib import DBusGMainLoop
86
85
import ctypes
116
115
if sys.version_info.major == 2:
117
116
    str = unicode
118
117
 
119
 
if sys.version_info < (3, 2):
120
 
    configparser.Configparser = configparser.SafeConfigParser
121
 
 
122
 
version = "1.8.6"
 
118
version = "1.8.3"
123
119
stored_state_file = "clients.pickle"
124
120
 
125
121
logger = logging.getLogger()
279
275
 
280
276
 
281
277
# Pretend that we have an Avahi module
282
 
class avahi(object):
283
 
    """This isn't so much a class as it is a module-like namespace."""
 
278
class Avahi(object):
 
279
    """This isn't so much a class as it is a module-like namespace.
 
280
    It is instantiated once, and simulates having an Avahi module."""
284
281
    IF_UNSPEC = -1               # avahi-common/address.h
285
282
    PROTO_UNSPEC = -1            # avahi-common/address.h
286
283
    PROTO_INET = 0               # avahi-common/address.h
290
287
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
291
288
    DBUS_PATH_SERVER = "/"
292
289
 
293
 
    @staticmethod
294
 
    def string_array_to_txt_array(t):
 
290
    def string_array_to_txt_array(self, t):
295
291
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
296
292
                           for s in t), signature="ay")
297
293
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
302
298
    SERVER_RUNNING = 2           # avahi-common/defs.h
303
299
    SERVER_COLLISION = 3         # avahi-common/defs.h
304
300
    SERVER_FAILURE = 4           # avahi-common/defs.h
 
301
avahi = Avahi()
305
302
 
306
303
 
307
304
class AvahiError(Exception):
507
504
 
508
505
 
509
506
# Pretend that we have a GnuTLS module
510
 
class gnutls(object):
511
 
    """This isn't so much a class as it is a module-like namespace."""
 
507
class GnuTLS(object):
 
508
    """This isn't so much a class as it is a module-like namespace.
 
509
    It is instantiated once, and simulates having a GnuTLS module."""
512
510
 
513
511
    library = ctypes.util.find_library("gnutls")
514
512
    if library is None:
515
513
        library = ctypes.util.find_library("gnutls-deb0")
516
514
    _library = ctypes.cdll.LoadLibrary(library)
517
515
    del library
 
516
    _need_version = b"3.3.0"
 
517
    _tls_rawpk_version = b"3.6.6"
 
518
 
 
519
    def __init__(self):
 
520
        # Need to use "self" here, since this method is called before
 
521
        # the assignment to the "gnutls" global variable happens.
 
522
        if self.check_version(self._need_version) is None:
 
523
            raise self.Error("Needs GnuTLS {} or later"
 
524
                             .format(self._need_version))
518
525
 
519
526
    # Unless otherwise indicated, the constants and types below are
520
527
    # all from the gnutls/gnutls.h C header file.
562
569
 
563
570
    # Exceptions
564
571
    class Error(Exception):
 
572
        # We need to use the class name "GnuTLS" here, since this
 
573
        # exception might be raised from within GnuTLS.__init__,
 
574
        # which is called before the assignment to the "gnutls"
 
575
        # global variable has happened.
565
576
        def __init__(self, message=None, code=None, args=()):
566
577
            # Default usage is by a message string, but if a return
567
578
            # code is passed, convert it to a string with
568
579
            # gnutls.strerror()
569
580
            self.code = code
570
581
            if message is None and code is not None:
571
 
                message = gnutls.strerror(code)
572
 
            return super(gnutls.Error, self).__init__(
 
582
                message = GnuTLS.strerror(code)
 
583
            return super(GnuTLS.Error, self).__init__(
573
584
                message, *args)
574
585
 
575
586
    class CertificateSecurityError(Error):
590
601
        def __init__(self, socket, credentials=None):
591
602
            self._c_object = gnutls.session_t()
592
603
            gnutls_flags = gnutls.CLIENT
593
 
            if gnutls.check_version(b"3.5.6"):
 
604
            if gnutls.check_version("3.5.6"):
594
605
                gnutls_flags |= gnutls.NO_TICKETS
595
606
            if gnutls.has_rawpk:
596
607
                gnutls_flags |= gnutls.ENABLE_RAWPK
733
744
    check_version.argtypes = [ctypes.c_char_p]
734
745
    check_version.restype = ctypes.c_char_p
735
746
 
736
 
    _need_version = b"3.3.0"
737
 
    if check_version(_need_version) is None:
738
 
        raise self.Error("Needs GnuTLS {} or later"
739
 
                         .format(_need_version))
740
 
 
741
 
    _tls_rawpk_version = b"3.6.6"
742
747
    has_rawpk = bool(check_version(_tls_rawpk_version))
743
748
 
744
749
    if has_rawpk:
798
803
                                                    ctypes.c_size_t)]
799
804
        openpgp_crt_get_fingerprint.restype = _error_code
800
805
 
801
 
    if check_version(b"3.6.4"):
 
806
    if check_version("3.6.4"):
802
807
        certificate_type_get2 = _library.gnutls_certificate_type_get2
803
808
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
804
809
        certificate_type_get2.restype = _error_code
805
810
 
806
811
    # Remove non-public functions
807
812
    del _error_code, _retry_on_error
 
813
# Create the global "gnutls" object, simulating a module
 
814
gnutls = GnuTLS()
808
815
 
809
816
 
810
817
def call_pipe(connection,       # : multiprocessing.Connection
825
832
    approved:   bool(); 'None' if not yet approved/disapproved
826
833
    approval_delay: datetime.timedelta(); Time to wait for approval
827
834
    approval_duration: datetime.timedelta(); Duration of one approval
828
 
    checker: multiprocessing.Process(); a running checker process used
829
 
             to see if the client lives. 'None' if no process is
830
 
             running.
 
835
    checker:    subprocess.Popen(); a running checker process used
 
836
                                    to see if the client lives.
 
837
                                    'None' if no process is running.
831
838
    checker_callback_tag: a GLib event source tag, or None
832
839
    checker_command: string; External command which is run to check
833
840
                     if client lives.  %() expansions are done at
1040
1047
    def checker_callback(self, source, condition, connection,
1041
1048
                         command):
1042
1049
        """The checker has completed, so take appropriate actions."""
 
1050
        self.checker_callback_tag = None
 
1051
        self.checker = None
1043
1052
        # Read return code from connection (see call_pipe)
1044
1053
        returncode = connection.recv()
1045
1054
        connection.close()
1046
 
        self.checker.join()
1047
 
        self.checker_callback_tag = None
1048
 
        self.checker = None
1049
1055
 
1050
1056
        if returncode >= 0:
1051
1057
            self.last_checker_status = returncode
2292
2298
            approval_required = False
2293
2299
            try:
2294
2300
                if gnutls.has_rawpk:
2295
 
                    fpr = b""
 
2301
                    fpr = ""
2296
2302
                    try:
2297
2303
                        key_id = self.key_id(
2298
2304
                            self.peer_certificate(session))
2302
2308
                    logger.debug("Key ID: %s", key_id)
2303
2309
 
2304
2310
                else:
2305
 
                    key_id = b""
 
2311
                    key_id = ""
2306
2312
                    try:
2307
2313
                        fpr = self.fingerprint(
2308
2314
                            self.peer_certificate(session))
2610
2616
                    raise
2611
2617
        # Only bind(2) the socket if we really need to.
2612
2618
        if self.server_address[0] or self.server_address[1]:
2613
 
            if self.server_address[1]:
2614
 
                self.allow_reuse_address = True
2615
2619
            if not self.server_address[0]:
2616
2620
                if self.address_family == socket.AF_INET6:
2617
2621
                    any_address = "::"  # in6addr_any
3002
3006
    del priority
3003
3007
 
3004
3008
    # Parse config file for server-global settings
3005
 
    server_config = configparser.ConfigParser(server_defaults)
 
3009
    server_config = configparser.SafeConfigParser(server_defaults)
3006
3010
    del server_defaults
3007
3011
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
3008
 
    # Convert the ConfigParser object to a dict
 
3012
    # Convert the SafeConfigParser object to a dict
3009
3013
    server_settings = server_config.defaults()
3010
3014
    # Use the appropriate methods on the non-string config options
3011
3015
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
3083
3087
                                  server_settings["servicename"])))
3084
3088
 
3085
3089
    # Parse config file with clients
3086
 
    client_config = configparser.ConfigParser(Client.client_defaults)
 
3090
    client_config = configparser.SafeConfigParser(Client
 
3091
                                                  .client_defaults)
3087
3092
    client_config.read(os.path.join(server_settings["configdir"],
3088
3093
                                    "clients.conf"))
3089
3094
 
3160
3165
        # Close all input and output, do double fork, etc.
3161
3166
        daemon()
3162
3167
 
3163
 
    if gi.version_info < (3, 10, 2):
3164
 
        # multiprocessing will use threads, so before we use GLib we
3165
 
        # need to inform GLib that threads will be used.
3166
 
        GLib.threads_init()
 
3168
    # multiprocessing will use threads, so before we use GLib we need
 
3169
    # to inform GLib that threads will be used.
 
3170
    GLib.threads_init()
3167
3171
 
3168
3172
    global main_loop
3169
3173
    # From the Avahi example code
3249
3253
                        for k in ("name", "host"):
3250
3254
                            if isinstance(value[k], bytes):
3251
3255
                                value[k] = value[k].decode("utf-8")
3252
 
                        if "key_id" not in value:
 
3256
                        if not value.has_key("key_id"):
3253
3257
                            value["key_id"] = ""
3254
 
                        elif "fingerprint" not in value:
 
3258
                        elif not value.has_key("fingerprint"):
3255
3259
                            value["fingerprint"] = ""
3256
3260
                    #  old_client_settings
3257
3261
                    # .keys()