/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-08-18 00:42:22 UTC
  • Revision ID: teddy@recompile.se-20190818004222-lfrgtnmqz766a08e
Client: Use the systemd sysusers.d mechanism, if present

* Makefile (install-client-nokey): Also install sysusers.d file, if
                                   $(SYSUSERS) exists.
* sysusers.d-mandos.conf: Adjust comment to match reality.

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
83
84
from gi.repository import GLib
84
85
from dbus.mainloop.glib import DBusGMainLoop
85
86
import ctypes
87
88
import xml.dom.minidom
88
89
import inspect
89
90
 
 
91
if sys.version_info.major == 2:
 
92
    __metaclass__ = type
 
93
 
90
94
# Try to find the value of SO_BINDTODEVICE:
91
95
try:
92
96
    # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and
115
119
if sys.version_info.major == 2:
116
120
    str = unicode
117
121
 
118
 
version = "1.8.3"
 
122
if sys.version_info < (3, 2):
 
123
    configparser.Configparser = configparser.SafeConfigParser
 
124
 
 
125
version = "1.8.7"
119
126
stored_state_file = "clients.pickle"
120
127
 
121
128
logger = logging.getLogger()
179
186
    pass
180
187
 
181
188
 
182
 
class PGPEngine(object):
 
189
class PGPEngine:
183
190
    """A simple class for OpenPGP symmetric encryption & decryption"""
184
191
 
185
192
    def __init__(self):
275
282
 
276
283
 
277
284
# Pretend that we have an Avahi module
278
 
class avahi(object):
 
285
class avahi:
279
286
    """This isn't so much a class as it is a module-like namespace."""
280
287
    IF_UNSPEC = -1               # avahi-common/address.h
281
288
    PROTO_UNSPEC = -1            # avahi-common/address.h
315
322
    pass
316
323
 
317
324
 
318
 
class AvahiService(object):
 
325
class AvahiService:
319
326
    """An Avahi (Zeroconf) service.
320
327
 
321
328
    Attributes:
503
510
 
504
511
 
505
512
# Pretend that we have a GnuTLS module
506
 
class gnutls(object):
 
513
class gnutls:
507
514
    """This isn't so much a class as it is a module-like namespace."""
508
515
 
509
516
    library = ctypes.util.find_library("gnutls")
572
579
        pass
573
580
 
574
581
    # Classes
575
 
    class Credentials(object):
 
582
    class Credentials:
576
583
        def __init__(self):
577
584
            self._c_object = gnutls.certificate_credentials_t()
578
585
            gnutls.certificate_allocate_credentials(
582
589
        def __del__(self):
583
590
            gnutls.certificate_free_credentials(self._c_object)
584
591
 
585
 
    class ClientSession(object):
 
592
    class ClientSession:
586
593
        def __init__(self, socket, credentials=None):
587
594
            self._c_object = gnutls.session_t()
588
595
            gnutls_flags = gnutls.CLIENT
589
 
            if gnutls.check_version("3.5.6"):
 
596
            if gnutls.check_version(b"3.5.6"):
590
597
                gnutls_flags |= gnutls.NO_TICKETS
591
598
            if gnutls.has_rawpk:
592
599
                gnutls_flags |= gnutls.ENABLE_RAWPK
794
801
                                                    ctypes.c_size_t)]
795
802
        openpgp_crt_get_fingerprint.restype = _error_code
796
803
 
797
 
    if check_version("3.6.4"):
 
804
    if check_version(b"3.6.4"):
798
805
        certificate_type_get2 = _library.gnutls_certificate_type_get2
799
806
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
800
807
        certificate_type_get2.restype = _error_code
814
821
    connection.close()
815
822
 
816
823
 
817
 
class Client(object):
 
824
class Client:
818
825
    """A representation of a client host served by this server.
819
826
 
820
827
    Attributes:
821
828
    approved:   bool(); 'None' if not yet approved/disapproved
822
829
    approval_delay: datetime.timedelta(); Time to wait for approval
823
830
    approval_duration: datetime.timedelta(); Duration of one approval
824
 
    checker:    subprocess.Popen(); a running checker process used
825
 
                                    to see if the client lives.
826
 
                                    'None' if no process is running.
 
831
    checker: multiprocessing.Process(); a running checker process used
 
832
             to see if the client lives. 'None' if no process is
 
833
             running.
827
834
    checker_callback_tag: a GLib event source tag, or None
828
835
    checker_command: string; External command which is run to check
829
836
                     if client lives.  %() expansions are done at
1036
1043
    def checker_callback(self, source, condition, connection,
1037
1044
                         command):
1038
1045
        """The checker has completed, so take appropriate actions."""
1039
 
        self.checker_callback_tag = None
1040
 
        self.checker = None
1041
1046
        # Read return code from connection (see call_pipe)
1042
1047
        returncode = connection.recv()
1043
1048
        connection.close()
 
1049
        self.checker.join()
 
1050
        self.checker_callback_tag = None
 
1051
        self.checker = None
1044
1052
 
1045
1053
        if returncode >= 0:
1046
1054
            self.last_checker_status = returncode
2208
2216
    del _interface
2209
2217
 
2210
2218
 
2211
 
class ProxyClient(object):
 
2219
class ProxyClient:
2212
2220
    def __init__(self, child_pipe, key_id, fpr, address):
2213
2221
        self._pipe = child_pipe
2214
2222
        self._pipe.send(('init', key_id, fpr, address))
2287
2295
            approval_required = False
2288
2296
            try:
2289
2297
                if gnutls.has_rawpk:
2290
 
                    fpr = ""
 
2298
                    fpr = b""
2291
2299
                    try:
2292
2300
                        key_id = self.key_id(
2293
2301
                            self.peer_certificate(session))
2297
2305
                    logger.debug("Key ID: %s", key_id)
2298
2306
 
2299
2307
                else:
2300
 
                    key_id = ""
 
2308
                    key_id = b""
2301
2309
                    try:
2302
2310
                        fpr = self.fingerprint(
2303
2311
                            self.peer_certificate(session))
2487
2495
        return hex_fpr
2488
2496
 
2489
2497
 
2490
 
class MultiprocessingMixIn(object):
 
2498
class MultiprocessingMixIn:
2491
2499
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
2492
2500
 
2493
2501
    def sub_process_main(self, request, address):
2505
2513
        return proc
2506
2514
 
2507
2515
 
2508
 
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
2516
class MultiprocessingMixInWithPipe(MultiprocessingMixIn):
2509
2517
    """ adds a pipe to the MixIn """
2510
2518
 
2511
2519
    def process_request(self, request, client_address):
2526
2534
 
2527
2535
 
2528
2536
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2529
 
                     socketserver.TCPServer, object):
 
2537
                     socketserver.TCPServer):
2530
2538
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2531
2539
 
2532
2540
    Attributes:
2605
2613
                    raise
2606
2614
        # Only bind(2) the socket if we really need to.
2607
2615
        if self.server_address[0] or self.server_address[1]:
 
2616
            if self.server_address[1]:
 
2617
                self.allow_reuse_address = True
2608
2618
            if not self.server_address[0]:
2609
2619
                if self.address_family == socket.AF_INET6:
2610
2620
                    any_address = "::"  # in6addr_any
2995
3005
    del priority
2996
3006
 
2997
3007
    # Parse config file for server-global settings
2998
 
    server_config = configparser.SafeConfigParser(server_defaults)
 
3008
    server_config = configparser.ConfigParser(server_defaults)
2999
3009
    del server_defaults
3000
3010
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
3001
 
    # Convert the SafeConfigParser object to a dict
 
3011
    # Convert the ConfigParser object to a dict
3002
3012
    server_settings = server_config.defaults()
3003
3013
    # Use the appropriate methods on the non-string config options
3004
3014
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
3076
3086
                                  server_settings["servicename"])))
3077
3087
 
3078
3088
    # Parse config file with clients
3079
 
    client_config = configparser.SafeConfigParser(Client
3080
 
                                                  .client_defaults)
 
3089
    client_config = configparser.ConfigParser(Client.client_defaults)
3081
3090
    client_config.read(os.path.join(server_settings["configdir"],
3082
3091
                                    "clients.conf"))
3083
3092
 
3154
3163
        # Close all input and output, do double fork, etc.
3155
3164
        daemon()
3156
3165
 
3157
 
    # multiprocessing will use threads, so before we use GLib we need
3158
 
    # to inform GLib that threads will be used.
3159
 
    GLib.threads_init()
 
3166
    if gi.version_info < (3, 10, 2):
 
3167
        # multiprocessing will use threads, so before we use GLib we
 
3168
        # need to inform GLib that threads will be used.
 
3169
        GLib.threads_init()
3160
3170
 
3161
3171
    global main_loop
3162
3172
    # From the Avahi example code
3242
3252
                        for k in ("name", "host"):
3243
3253
                            if isinstance(value[k], bytes):
3244
3254
                                value[k] = value[k].decode("utf-8")
3245
 
                        if not value.has_key("key_id"):
 
3255
                        if "key_id" not in value:
3246
3256
                            value["key_id"] = ""
3247
 
                        elif not value.has_key("fingerprint"):
 
3257
                        elif "fingerprint" not in value:
3248
3258
                            value["fingerprint"] = ""
3249
3259
                    #  old_client_settings
3250
3260
                    # .keys()