/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: 2012-11-14 21:03:24 UTC
  • Revision ID: teddy@recompile.se-20121114210324-n7m7k0ki2ncmje3a
* mandos-ctl (string_to_delta): Try to parse RFC 3339 duration before
                                anything else.  This also minimizes
                                the changes from the last release.
  (PGPEngine): Use straight subprocess.Popen() to call "gpg".
* mandos-monitor (UserInterface.run): Fix grammar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2014 Teddy Hogeborn
15
 
# Copyright © 2008-2014 Björn Påhlsson
 
14
# Copyright © 2008-2012 Teddy Hogeborn
 
15
# Copyright © 2008-2012 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
88
88
    except ImportError:
89
89
        SO_BINDTODEVICE = None
90
90
 
91
 
version = "1.6.5"
 
91
version = "1.6.0"
92
92
stored_state_file = "clients.pickle"
93
93
 
94
94
logger = logging.getLogger()
95
 
syslogger = None
 
95
syslogger = (logging.handlers.SysLogHandler
 
96
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
 
97
              address = str("/dev/log")))
96
98
 
97
99
try:
98
100
    if_nametoindex = (ctypes.cdll.LoadLibrary
114
116
def initlogger(debug, level=logging.WARNING):
115
117
    """init logger and add loglevel"""
116
118
    
117
 
    syslogger = (logging.handlers.SysLogHandler
118
 
                 (facility =
119
 
                  logging.handlers.SysLogHandler.LOG_DAEMON,
120
 
                  address = str("/dev/log")))
121
119
    syslogger.setFormatter(logging.Formatter
122
120
                           ('Mandos [%(process)d]: %(levelname)s:'
123
121
                            ' %(message)s'))
174
172
    def password_encode(self, password):
175
173
        # Passphrase can not be empty and can not contain newlines or
176
174
        # NUL bytes.  So we prefix it and hex encode it.
177
 
        encoded = b"mandos" + binascii.hexlify(password)
178
 
        if len(encoded) > 2048:
179
 
            # GnuPG can't handle long passwords, so encode differently
180
 
            encoded = (b"mandos" + password.replace(b"\\", b"\\\\")
181
 
                       .replace(b"\n", b"\\n")
182
 
                       .replace(b"\0", b"\\x00"))
183
 
        return encoded
 
175
        return b"mandos" + binascii.hexlify(password)
184
176
    
185
177
    def encrypt(self, data, password):
186
178
        passphrase = self.password_encode(password)
448
440
    runtime_expansions: Allowed attributes for runtime expansion.
449
441
    expires:    datetime.datetime(); time (UTC) when a client will be
450
442
                disabled, or None
451
 
    server_settings: The server_settings dict from main()
452
443
    """
453
444
    
454
445
    runtime_expansions = ("approval_delay", "approval_duration",
529
520
        
530
521
        return settings
531
522
    
532
 
    def __init__(self, settings, name = None, server_settings=None):
 
523
    def __init__(self, settings, name = None):
533
524
        self.name = name
534
 
        if server_settings is None:
535
 
            server_settings = {}
536
 
        self.server_settings = server_settings
537
525
        # adding all client settings
538
526
        for setting, value in settings.iteritems():
539
527
            setattr(self, setting, value)
692
680
        # If a checker exists, make sure it is not a zombie
693
681
        try:
694
682
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
695
 
        except AttributeError:
696
 
            pass
697
 
        except OSError as error:
698
 
            if error.errno != errno.ECHILD:
699
 
                raise
 
683
        except (AttributeError, OSError) as error:
 
684
            if (isinstance(error, OSError)
 
685
                and error.errno != errno.ECHILD):
 
686
                raise error
700
687
        else:
701
688
            if pid:
702
689
                logger.warning("Checker was a zombie")
724
711
                # in normal mode, that is already done by daemon(),
725
712
                # and in debug mode we don't want to.  (Stdin is
726
713
                # always replaced by /dev/null.)
727
 
                # The exception is when not debugging but nevertheless
728
 
                # running in the foreground; use the previously
729
 
                # created wnull.
730
 
                popen_args = {}
731
 
                if (not self.server_settings["debug"]
732
 
                    and self.server_settings["foreground"]):
733
 
                    popen_args.update({"stdout": wnull,
734
 
                                       "stderr": wnull })
735
714
                self.checker = subprocess.Popen(command,
736
715
                                                close_fds=True,
737
 
                                                shell=True, cwd="/",
738
 
                                                **popen_args)
 
716
                                                shell=True, cwd="/")
739
717
            except OSError as error:
740
718
                logger.error("Failed to start subprocess",
741
719
                             exc_info=error)
936
914
            # The byte_arrays option is not supported yet on
937
915
            # signatures other than "ay".
938
916
            if prop._dbus_signature != "ay":
939
 
                raise ValueError("Byte arrays not supported for non-"
940
 
                                 "'ay' signature {0!r}"
941
 
                                 .format(prop._dbus_signature))
 
917
                raise ValueError
942
918
            value = dbus.ByteArray(b''.join(chr(byte)
943
919
                                            for byte in value))
944
920
        prop(value)
1352
1328
                                       *args, **kwargs)
1353
1329
    
1354
1330
    def start_checker(self, *args, **kwargs):
1355
 
        old_checker_pid = getattr(self.checker, "pid", None)
 
1331
        old_checker = self.checker
 
1332
        if self.checker is not None:
 
1333
            old_checker_pid = self.checker.pid
 
1334
        else:
 
1335
            old_checker_pid = None
1356
1336
        r = Client.start_checker(self, *args, **kwargs)
1357
1337
        # Only if new checker process was started
1358
1338
        if (self.checker is not None
1703
1683
            logger.debug("Protocol version: %r", line)
1704
1684
            try:
1705
1685
                if int(line.strip().split()[0]) > 1:
1706
 
                    raise RuntimeError(line)
 
1686
                    raise RuntimeError
1707
1687
            except (ValueError, IndexError, RuntimeError) as error:
1708
1688
                logger.error("Unknown protocol version: %s", error)
1709
1689
                return
1916
1896
    
1917
1897
    def add_pipe(self, parent_pipe, proc):
1918
1898
        """Dummy function; override as necessary"""
1919
 
        raise NotImplementedError()
 
1899
        raise NotImplementedError
1920
1900
 
1921
1901
 
1922
1902
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1998
1978
                if self.address_family == socket.AF_INET6:
1999
1979
                    any_address = "::" # in6addr_any
2000
1980
                else:
2001
 
                    any_address = "0.0.0.0" # INADDR_ANY
 
1981
                    any_address = socket.INADDR_ANY
2002
1982
                self.server_address = (any_address,
2003
1983
                                       self.server_address[1])
2004
1984
            elif not self.server_address[1]:
2259
2239
            else:
2260
2240
                raise ValueError("Unknown suffix {0!r}"
2261
2241
                                 .format(suffix))
2262
 
        except IndexError as e:
 
2242
        except (ValueError, IndexError) as e:
2263
2243
            raise ValueError(*(e.args))
2264
2244
        timevalue += delta
2265
2245
    return timevalue
2309
2289
                        help="Run self-test")
2310
2290
    parser.add_argument("--debug", action="store_true",
2311
2291
                        help="Debug mode; run in foreground and log"
2312
 
                        " to terminal", default=None)
 
2292
                        " to terminal")
2313
2293
    parser.add_argument("--debuglevel", metavar="LEVEL",
2314
2294
                        help="Debug level for stdout output")
2315
2295
    parser.add_argument("--priority", help="GnuTLS"
2322
2302
                        " files")
2323
2303
    parser.add_argument("--no-dbus", action="store_false",
2324
2304
                        dest="use_dbus", help="Do not provide D-Bus"
2325
 
                        " system bus interface", default=None)
 
2305
                        " system bus interface")
2326
2306
    parser.add_argument("--no-ipv6", action="store_false",
2327
 
                        dest="use_ipv6", help="Do not use IPv6",
2328
 
                        default=None)
 
2307
                        dest="use_ipv6", help="Do not use IPv6")
2329
2308
    parser.add_argument("--no-restore", action="store_false",
2330
2309
                        dest="restore", help="Do not restore stored"
2331
 
                        " state", default=None)
 
2310
                        " state")
2332
2311
    parser.add_argument("--socket", type=int,
2333
2312
                        help="Specify a file descriptor to a network"
2334
2313
                        " socket to use instead of creating one")
2335
2314
    parser.add_argument("--statedir", metavar="DIR",
2336
2315
                        help="Directory to save/restore state in")
2337
2316
    parser.add_argument("--foreground", action="store_true",
2338
 
                        help="Run in foreground", default=None)
 
2317
                        help="Run in foreground")
2339
2318
    
2340
2319
    options = parser.parse_args()
2341
2320
    
2342
2321
    if options.check:
2343
2322
        import doctest
2344
 
        fail_count, test_count = doctest.testmod()
2345
 
        sys.exit(os.EX_OK if fail_count == 0 else 1)
 
2323
        doctest.testmod()
 
2324
        sys.exit()
2346
2325
    
2347
2326
    # Default values for config file for server-global settings
2348
2327
    server_defaults = { "interface": "",
2350
2329
                        "port": "",
2351
2330
                        "debug": "False",
2352
2331
                        "priority":
2353
 
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224:+SIGN-RSA-RMD160",
 
2332
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2354
2333
                        "servicename": "Mandos",
2355
2334
                        "use_dbus": "True",
2356
2335
                        "use_ipv6": "True",
2400
2379
    for option in server_settings.keys():
2401
2380
        if type(server_settings[option]) is str:
2402
2381
            server_settings[option] = unicode(server_settings[option])
2403
 
    # Force all boolean options to be boolean
2404
 
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
2405
 
                   "foreground"):
2406
 
        server_settings[option] = bool(server_settings[option])
2407
2382
    # Debug implies foreground
2408
2383
    if server_settings["debug"]:
2409
2384
        server_settings["foreground"] = True
2457
2432
                              socketfd=(server_settings["socket"]
2458
2433
                                        or None))
2459
2434
    if not foreground:
2460
 
        pidfilename = "/run/mandos.pid"
2461
 
        if not os.path.isdir("/run/."):
2462
 
            pidfilename = "/var/run/mandos.pid"
 
2435
        pidfilename = "/var/run/mandos.pid"
2463
2436
        pidfile = None
2464
2437
        try:
2465
2438
            pidfile = open(pidfilename, "w")
2482
2455
        os.setuid(uid)
2483
2456
    except OSError as error:
2484
2457
        if error.errno != errno.EPERM:
2485
 
            raise
 
2458
            raise error
2486
2459
    
2487
2460
    if debug:
2488
2461
        # Enable all possible GnuTLS debugging
2551
2524
    old_client_settings = {}
2552
2525
    clients_data = {}
2553
2526
    
2554
 
    # This is used to redirect stdout and stderr for checker processes
2555
 
    global wnull
2556
 
    wnull = open(os.devnull, "w") # A writable /dev/null
2557
 
    # Only used if server is running in foreground but not in debug
2558
 
    # mode
2559
 
    if debug or not foreground:
2560
 
        wnull.close()
2561
 
    
2562
2527
    # Get client data and settings from last running state.
2563
2528
    if server_settings["restore"]:
2564
2529
        try:
2580
2545
    
2581
2546
    with PGPEngine() as pgp:
2582
2547
        for client_name, client in clients_data.iteritems():
2583
 
            # Skip removed clients
2584
 
            if client_name not in client_settings:
2585
 
                continue
2586
 
            
2587
2548
            # Decide which value to use after restoring saved state.
2588
2549
            # We have three different values: Old config file,
2589
2550
            # new config file, and saved state.
2651
2612
    # Create all client objects
2652
2613
    for client_name, client in clients_data.iteritems():
2653
2614
        tcp_server.clients[client_name] = client_class(
2654
 
            name = client_name, settings = client,
2655
 
            server_settings = server_settings)
 
2615
            name = client_name, settings = client)
2656
2616
    
2657
2617
    if not tcp_server.clients:
2658
2618
        logger.warning("No clients defined")
2741
2701
        service.cleanup()
2742
2702
        
2743
2703
        multiprocessing.active_children()
2744
 
        wnull.close()
2745
2704
        if not (tcp_server.clients or client_settings):
2746
2705
            return
2747
2706
        
2759
2718
                # A list of attributes that can not be pickled
2760
2719
                # + secret.
2761
2720
                exclude = set(("bus", "changedstate", "secret",
2762
 
                               "checker", "server_settings"))
 
2721
                               "checker"))
2763
2722
                for name, typ in (inspect.getmembers
2764
2723
                                  (dbus.service.Object)):
2765
2724
                    exclude.add(name)
2793
2752
            else:
2794
2753
                logger.warning("Could not save persistent state:",
2795
2754
                               exc_info=e)
2796
 
                raise
 
2755
                raise e
2797
2756
        
2798
2757
        # Delete all clients, and settings from config
2799
2758
        while tcp_server.clients: