/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2008-10-05 17:38:31 UTC
  • Revision ID: teddy@fukt.bsnet.se-20081005173831-fysrfayl4yvhlo6x
* INSTALL: Add instructions on how to set the correct network
           interface on the cient, and also how to test the server and
           verify the password.

* TODO: Clean up old stuff.

* debian/mandos-client.README.Debian: Separate into sections with
                                      headlines.  Add instructions on
                                      how to test the server and
                                      verify the password.

* plugin-runner.conf: Add reminder to update initrd image.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
14
# Copyright © 2008 Teddy Hogeborn & Björn Påhlsson
15
15
16
16
# This program is free software: you can redistribute it and/or modify
17
17
# it under the terms of the GNU General Public License as published by
34
34
 
35
35
import SocketServer
36
36
import socket
37
 
import select
38
37
from optparse import OptionParser
39
38
import datetime
40
39
import errno
62
61
import avahi
63
62
from dbus.mainloop.glib import DBusGMainLoop
64
63
import ctypes
 
64
import ctypes.util
65
65
 
66
66
version = "1.0"
67
67
 
81
81
class AvahiError(Exception):
82
82
    def __init__(self, value):
83
83
        self.value = value
 
84
        super(AvahiError, self).__init__()
84
85
    def __str__(self):
85
86
        return repr(self.value)
86
87
 
108
109
                  a sensible number of times
109
110
    """
110
111
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
111
 
                 type = None, port = None, TXT = None, domain = "",
 
112
                 servicetype = None, port = None, TXT = None, domain = "",
112
113
                 host = "", max_renames = 32768):
113
114
        self.interface = interface
114
115
        self.name = name
115
 
        self.type = type
 
116
        self.type = servicetype
116
117
        self.port = port
117
118
        if TXT is None:
118
119
            self.TXT = []
127
128
        if self.rename_count >= self.max_renames:
128
129
            logger.critical(u"No suitable Zeroconf service name found"
129
130
                            u" after %i retries, exiting.",
130
 
                            rename_count)
 
131
                            self.rename_count)
131
132
            raise AvahiServiceError("Too many renames")
132
133
        self.name = server.GetAlternativeServiceName(self.name)
133
134
        logger.info(u"Changing Zeroconf service name to %r ...",
222
223
    interval = property(lambda self: self._interval,
223
224
                        _set_interval)
224
225
    del _set_interval
225
 
    def __init__(self, name = None, stop_hook=None, config={}):
 
226
    def __init__(self, name = None, stop_hook=None, config=None):
226
227
        """Note: the 'checker' key in 'config' sets the
227
228
        'checker_command' attribute and *not* the 'checker'
228
229
        attribute."""
 
230
        if config is None:
 
231
            config = {}
229
232
        self.name = name
230
233
        logger.debug(u"Creating client %r", self.name)
231
234
        # Uppercase and remove spaces from fingerprint for later
237
240
        if "secret" in config:
238
241
            self.secret = config["secret"].decode(u"base64")
239
242
        elif "secfile" in config:
240
 
            sf = open(config["secfile"])
241
 
            self.secret = sf.read()
242
 
            sf.close()
 
243
            secfile = open(os.path.expanduser(os.path.expandvars
 
244
                                              (config["secfile"])))
 
245
            self.secret = secfile.read()
 
246
            secfile.close()
243
247
        else:
244
248
            raise TypeError(u"No secret or secfile for client %s"
245
249
                            % self.name)
415
419
                    (crt, ctypes.byref(datum),
416
420
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
417
421
    # Verify the self signature in the key
418
 
    crtverify = ctypes.c_uint();
 
422
    crtverify = ctypes.c_uint()
419
423
    gnutls.library.functions.gnutls_openpgp_crt_verify_self\
420
424
        (crt, 0, ctypes.byref(crtverify))
421
425
    if crtverify.value != 0:
422
426
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
423
427
        raise gnutls.errors.CertificateSecurityError("Verify failed")
424
428
    # New buffer for the fingerprint
425
 
    buffer = ctypes.create_string_buffer(20)
426
 
    buffer_length = ctypes.c_size_t()
 
429
    buf = ctypes.create_string_buffer(20)
 
430
    buf_len = ctypes.c_size_t()
427
431
    # Get the fingerprint from the certificate into the buffer
428
432
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
429
 
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
 
433
        (crt, ctypes.byref(buf), ctypes.byref(buf_len))
430
434
    # Deinit the certificate
431
435
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
432
436
    # Convert the buffer to a Python bytestring
433
 
    fpr = ctypes.string_at(buffer, buffer_length.value)
 
437
    fpr = ctypes.string_at(buf, buf_len.value)
434
438
    # Convert the bytestring to hexadecimal notation
435
439
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
436
440
    return hex_fpr
437
441
 
438
442
 
439
 
class tcp_handler(SocketServer.BaseRequestHandler, object):
 
443
class TCP_handler(SocketServer.BaseRequestHandler, object):
440
444
    """A TCP request handler class.
441
445
    Instantiated by IPv6_TCPServer for each request to handle it.
442
446
    Note: This will run in its own forked process."""
469
473
        if self.server.settings["priority"]:
470
474
            priority = self.server.settings["priority"]
471
475
        gnutls.library.functions.gnutls_priority_set_direct\
472
 
            (session._c_object, priority, None);
 
476
            (session._c_object, priority, None)
473
477
        
474
478
        try:
475
479
            session.handshake()
529
533
            self.clients = kwargs["clients"]
530
534
            del kwargs["clients"]
531
535
        self.enabled = False
532
 
        return super(type(self), self).__init__(*args, **kwargs)
 
536
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
533
537
    def server_bind(self):
534
538
        """This overrides the normal server_bind() function
535
539
        to bind to an interface if one was specified, and also NOT to
564
568
#                                            if_nametoindex
565
569
#                                            (self.settings
566
570
#                                             ["interface"]))
567
 
            return super(type(self), self).server_bind()
 
571
            return super(IPv6_TCPServer, self).server_bind()
568
572
    def server_activate(self):
569
573
        if self.enabled:
570
 
            return super(type(self), self).server_activate()
 
574
            return super(IPv6_TCPServer, self).server_activate()
571
575
    def enable(self):
572
576
        self.enabled = True
573
577
 
591
595
    timevalue = datetime.timedelta(0)
592
596
    for s in interval.split():
593
597
        try:
594
 
            suffix=unicode(s[-1])
595
 
            value=int(s[:-1])
 
598
            suffix = unicode(s[-1])
 
599
            value = int(s[:-1])
596
600
            if suffix == u"d":
597
601
                delta = datetime.timedelta(value)
598
602
            elif suffix == u"s":
638
642
    """Call the C function if_nametoindex(), or equivalent"""
639
643
    global if_nametoindex
640
644
    try:
641
 
        if "ctypes.util" not in sys.modules:
642
 
            import ctypes.util
643
645
        if_nametoindex = ctypes.cdll.LoadLibrary\
644
646
            (ctypes.util.find_library("c")).if_nametoindex
645
647
    except (OSError, AttributeError):
683
685
 
684
686
 
685
687
def main():
686
 
    global main_loop_started
687
 
    main_loop_started = False
688
 
    
689
688
    parser = OptionParser(version = "%%prog %s" % version)
690
689
    parser.add_option("-i", "--interface", type="string",
691
690
                      metavar="IF", help="Bind to interface IF")
706
705
                      default="/etc/mandos", metavar="DIR",
707
706
                      help="Directory to search for configuration"
708
707
                      " files")
709
 
    (options, args) = parser.parse_args()
 
708
    options = parser.parse_args()[0]
710
709
    
711
710
    if options.check:
712
711
        import doctest
769
768
    clients = Set()
770
769
    tcp_server = IPv6_TCPServer((server_settings["address"],
771
770
                                 server_settings["port"]),
772
 
                                tcp_handler,
 
771
                                TCP_handler,
773
772
                                settings=server_settings,
774
773
                                clients=clients)
 
774
    pidfilename = "/var/run/mandos.pid"
 
775
    try:
 
776
        pidfile = open(pidfilename, "w")
 
777
    except IOError, error:
 
778
        logger.error("Could not open file %r", pidfilename)
 
779
    
775
780
    uid = 65534
776
781
    gid = 65534
777
782
    try:
794
799
    except OSError, error:
795
800
        if error[0] != errno.EPERM:
796
801
            raise error
797
 
 
 
802
    
798
803
    global service
799
804
    service = AvahiService(name = server_settings["servicename"],
800
 
                           type = "_mandos._tcp", );
 
805
                           servicetype = "_mandos._tcp", )
801
806
    if server_settings["interface"]:
802
807
        service.interface = if_nametoindex\
803
808
                            (server_settings["interface"])
841
846
        # Close all input and output, do double fork, etc.
842
847
        daemon()
843
848
    
844
 
    pidfilename = "/var/run/mandos.pid"
845
 
    pid = os.getpid()
846
849
    try:
847
 
        pidfile = open(pidfilename, "w")
 
850
        pid = os.getpid()
848
851
        pidfile.write(str(pid) + "\n")
849
852
        pidfile.close()
850
853
        del pidfile
851
 
    except IOError, err:
852
 
        logger.error(u"Could not write %s file with PID %d",
853
 
                     pidfilename, os.getpid())
 
854
    except IOError:
 
855
        logger.error(u"Could not write to file %r with PID %d",
 
856
                     pidfilename, pid)
 
857
    except NameError:
 
858
        # "pidfile" was never created
 
859
        pass
 
860
    del pidfilename
854
861
    
855
862
    def cleanup():
856
863
        "Cleanup function; run on exit"
902
909
                             (*args[2:], **kwargs) or True)
903
910
        
904
911
        logger.debug(u"Starting main loop")
905
 
        main_loop_started = True
906
912
        main_loop.run()
907
913
    except AvahiError, error:
908
914
        logger.critical(u"AvahiError: %s" + unicode(error))