/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: 2009-01-26 23:47:44 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090126234744-c19lb7zn1qsrcwmv
* debian/control (mandos/Depends): Added "python-gobject".

Show diffs side-by-side

added added

removed removed

Lines of Context:
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.7"
 
69
version = "1.0.5"
70
70
 
71
71
logger = logging.Logger('mandos')
72
72
syslogger = (logging.handlers.SysLogHandler
73
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
74
74
              address = "/dev/log"))
75
75
syslogger.setFormatter(logging.Formatter
76
 
                       ('Mandos [%(process)d]: %(levelname)s:'
77
 
                        ' %(message)s'))
 
76
                       ('Mandos: %(levelname)s: %(message)s'))
78
77
logger.addHandler(syslogger)
79
78
 
80
79
console = logging.StreamHandler()
81
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
82
 
                                       ' %(levelname)s: %(message)s'))
 
80
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
 
81
                                       ' %(message)s'))
83
82
logger.addHandler(console)
84
83
 
85
84
class AvahiError(Exception):
114
113
    """
115
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
116
115
                 servicetype = None, port = None, TXT = None,
117
 
                 domain = "", host = "", max_renames = 32768,
118
 
                 protocol = avahi.PROTO_UNSPEC):
 
116
                 domain = "", host = "", max_renames = 32768):
119
117
        self.interface = interface
120
118
        self.name = name
121
119
        self.type = servicetype
125
123
        self.host = host
126
124
        self.rename_count = 0
127
125
        self.max_renames = max_renames
128
 
        self.protocol = protocol
129
126
    def rename(self):
130
127
        """Derived from the Avahi example code"""
131
128
        if self.rename_count >= self.max_renames:
160
157
                     service.name, service.type)
161
158
        group.AddService(
162
159
                self.interface,         # interface
163
 
                self.protocol,          # protocol
 
160
                avahi.PROTO_INET6,      # protocol
164
161
                dbus.UInt32(0),         # flags
165
162
                self.name, self.type,
166
163
                self.domain, self.host,
205
202
                     client lives.  %() expansions are done at
206
203
                     runtime with vars(self) as dict, so that for
207
204
                     instance %(name)s can be used in the command.
208
 
    current_checker_command: string; current running checker_command
209
205
    use_dbus: bool(); Whether to provide D-Bus interface and signals
210
206
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
211
207
    """
260
256
        self.disable_initiator_tag = None
261
257
        self.checker_callback_tag = None
262
258
        self.checker_command = config["checker"]
263
 
        self.current_checker_command = None
264
259
        self.last_connect = None
265
260
        # Only now, when this client is initialized, can it show up on
266
261
        # the D-Bus
381
376
        # checkers alone, the checker would have to take more time
382
377
        # than 'timeout' for the client to be declared invalid, which
383
378
        # is as it should be.
384
 
        
385
 
        # If a checker exists, make sure it is not a zombie
386
 
        if self.checker is not None:
387
 
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
388
 
            if pid:
389
 
                logger.warning("Checker was a zombie")
390
 
                gobject.source_remove(self.checker_callback_tag)
391
 
                self.checker_callback(pid, status,
392
 
                                      self.current_checker_command)
393
 
        # Start a new checker if needed
394
379
        if self.checker is None:
395
380
            try:
396
381
                # In case checker_command has exactly one % operator
406
391
                    logger.error(u'Could not format string "%s":'
407
392
                                 u' %s', self.checker_command, error)
408
393
                    return True # Try again later
409
 
                self.current_checker_command = command
410
394
            try:
411
395
                logger.info(u"Starting checker %r for %s",
412
396
                            command, self.name)
427
411
                                             (self.checker.pid,
428
412
                                              self.checker_callback,
429
413
                                              data=command))
430
 
                # The checker may have completed before the gobject
431
 
                # watch was added.  Check for this.
432
 
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
433
 
                if pid:
434
 
                    gobject.source_remove(self.checker_callback_tag)
435
 
                    self.checker_callback(pid, status, command)
436
414
            except OSError, error:
437
415
                logger.error(u"Failed to start subprocess: %s",
438
416
                             error)
698
676
        # using OpenPGP certificates.
699
677
        
700
678
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
701
 
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
702
 
        #                     "+DHE-DSS"))
 
679
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
680
        #                "+DHE-DSS"))
703
681
        # Use a fallback default, since this MUST be set.
704
682
        priority = self.server.settings.get("priority", "NORMAL")
705
683
        (gnutls.library.functions
721
699
            session.bye()
722
700
            return
723
701
        logger.debug(u"Fingerprint: %s", fpr)
724
 
        
725
702
        for c in self.server.clients:
726
703
            if c.fingerprint == fpr:
727
704
                client = c
753
730
 
754
731
class IPv6_TCPServer(SocketServer.ForkingMixIn,
755
732
                     SocketServer.TCPServer, object):
756
 
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
 
733
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
757
734
    Attributes:
758
735
        settings:       Server settings
759
736
        clients:        Set() of Client objects
767
744
        if "clients" in kwargs:
768
745
            self.clients = kwargs["clients"]
769
746
            del kwargs["clients"]
770
 
        if "use_ipv6" in kwargs:
771
 
            if not kwargs["use_ipv6"]:
772
 
                self.address_family = socket.AF_INET
773
 
            del kwargs["use_ipv6"]
774
747
        self.enabled = False
775
748
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
776
749
    def server_bind(self):
790
763
                                 u" bind to interface %s",
791
764
                                 self.settings["interface"])
792
765
                else:
793
 
                    raise
 
766
                    raise error
794
767
        # Only bind(2) the socket if we really need to.
795
768
        if self.server_address[0] or self.server_address[1]:
796
769
            if not self.server_address[0]:
797
 
                if self.address_family == socket.AF_INET6:
798
 
                    any_address = "::" # in6addr_any
799
 
                else:
800
 
                    any_address = socket.INADDR_ANY
801
 
                self.server_address = (any_address,
 
770
                in6addr_any = "::"
 
771
                self.server_address = (in6addr_any,
802
772
                                       self.server_address[1])
803
773
            elif not self.server_address[1]:
804
774
                self.server_address = (self.server_address[0],
820
790
 
821
791
def string_to_delta(interval):
822
792
    """Parse a string and return a datetime.timedelta
823
 
    
 
793
 
824
794
    >>> string_to_delta('7d')
825
795
    datetime.timedelta(7)
826
796
    >>> string_to_delta('60s')
949
919
                      " files")
950
920
    parser.add_option("--no-dbus", action="store_false",
951
921
                      dest="use_dbus",
952
 
                      help=optparse.SUPPRESS_HELP) # XXX: Not done yet
953
 
    parser.add_option("--no-ipv6", action="store_false",
954
 
                      dest="use_ipv6", help="Do not use IPv6")
 
922
                      help="Do not provide D-Bus system bus"
 
923
                      " interface")
955
924
    options = parser.parse_args()[0]
956
925
    
957
926
    if options.check:
968
937
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
969
938
                        "servicename": "Mandos",
970
939
                        "use_dbus": "True",
971
 
                        "use_ipv6": "True",
972
940
                        }
973
941
    
974
942
    # Parse config file for server-global settings
982
950
                                                        "debug")
983
951
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
984
952
                                                           "use_dbus")
985
 
    server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
986
 
                                                           "use_ipv6")
987
953
    if server_settings["port"]:
988
954
        server_settings["port"] = server_config.getint("DEFAULT",
989
955
                                                       "port")
993
959
    # options, if set.
994
960
    for option in ("interface", "address", "port", "debug",
995
961
                   "priority", "servicename", "configdir",
996
 
                   "use_dbus", "use_ipv6"):
 
962
                   "use_dbus"):
997
963
        value = getattr(options, option)
998
964
        if value is not None:
999
965
            server_settings[option] = value
1003
969
    # For convenience
1004
970
    debug = server_settings["debug"]
1005
971
    use_dbus = server_settings["use_dbus"]
1006
 
    use_dbus = False            # XXX: Not done yet
1007
 
    use_ipv6 = server_settings["use_ipv6"]
1008
972
    
1009
973
    if not debug:
1010
974
        syslogger.setLevel(logging.WARNING)
1031
995
                                 server_settings["port"]),
1032
996
                                TCP_handler,
1033
997
                                settings=server_settings,
1034
 
                                clients=clients, use_ipv6=use_ipv6)
 
998
                                clients=clients)
1035
999
    pidfilename = "/var/run/mandos.pid"
1036
1000
    try:
1037
1001
        pidfile = open(pidfilename, "w")
1038
 
    except IOError:
 
1002
    except IOError, error:
1039
1003
        logger.error("Could not open file %r", pidfilename)
1040
1004
    
1041
1005
    try:
1053
1017
                uid = 65534
1054
1018
                gid = 65534
1055
1019
    try:
 
1020
        os.setuid(uid)
1056
1021
        os.setgid(gid)
1057
 
        os.setuid(uid)
1058
1022
    except OSError, error:
1059
1023
        if error[0] != errno.EPERM:
1060
1024
            raise error
1061
1025
    
1062
 
    # Enable all possible GnuTLS debugging
1063
 
    if debug:
1064
 
        # "Use a log level over 10 to enable all debugging options."
1065
 
        # - GnuTLS manual
1066
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
1067
 
        
1068
 
        @gnutls.library.types.gnutls_log_func
1069
 
        def debug_gnutls(level, string):
1070
 
            logger.debug("GnuTLS: %s", string[:-1])
1071
 
        
1072
 
        (gnutls.library.functions
1073
 
         .gnutls_global_set_log_function(debug_gnutls))
1074
 
    
1075
1026
    global service
1076
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1077
1027
    service = AvahiService(name = server_settings["servicename"],
1078
 
                           servicetype = "_mandos._tcp",
1079
 
                           protocol = protocol)
 
1028
                           servicetype = "_mandos._tcp", )
1080
1029
    if server_settings["interface"]:
1081
1030
        service.interface = (if_nametoindex
1082
1031
                             (server_settings["interface"]))
1209
1158
    
1210
1159
    # Find out what port we got
1211
1160
    service.port = tcp_server.socket.getsockname()[1]
1212
 
    if use_ipv6:
1213
 
        logger.info(u"Now listening on address %r, port %d,"
1214
 
                    " flowinfo %d, scope_id %d"
1215
 
                    % tcp_server.socket.getsockname())
1216
 
    else:                       # IPv4
1217
 
        logger.info(u"Now listening on address %r, port %d"
1218
 
                    % tcp_server.socket.getsockname())
 
1161
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
 
1162
                u" scope_id %d" % tcp_server.socket.getsockname())
1219
1163
    
1220
1164
    #service.interface = tcp_server.socket.getsockname()[3]
1221
1165
    
1241
1185
        sys.exit(1)
1242
1186
    except KeyboardInterrupt:
1243
1187
        if debug:
1244
 
            print >> sys.stderr
1245
 
        logger.debug("Server received KeyboardInterrupt")
1246
 
    logger.debug("Server exiting")
 
1188
            print
1247
1189
 
1248
1190
if __name__ == '__main__':
1249
1191
    main()