/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
3 by Björn Påhlsson
Python based server
1
#!/usr/bin/python
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2
# -*- mode: python; coding: utf-8 -*-
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3
# 
4
# Mandos server - give out binary blobs to connecting clients.
5
# 
6
# This program is partly derived from an example program for an Avahi
7
# service publisher, downloaded from
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
46 by Teddy Hogeborn
* network-protocol.txt: New.
9
# methods "add" and "remove" in the "AvahiService" class, the
10
# "server_state_changed" and "entry_group_state_changed" functions,
11
# and some lines in "main".
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
12
# 
28 by Teddy Hogeborn
* server.conf: New file.
13
# Everything else is
14
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
15
# 
16
# This program is free software: you can redistribute it and/or modify
17
# it under the terms of the GNU General Public License as published by
18
# the Free Software Foundation, either version 3 of the License, or
19
# (at your option) any later version.
20
#
21
#     This program is distributed in the hope that it will be useful,
22
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
23
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
#     GNU General Public License for more details.
25
# 
26
# You should have received a copy of the GNU General Public License
27
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
# 
28 by Teddy Hogeborn
* server.conf: New file.
29
# Contact the authors at <mandos@fukt.bsnet.se>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
30
# 
3 by Björn Påhlsson
Python based server
31
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
32
from __future__ import division
33
3 by Björn Påhlsson
Python based server
34
import SocketServer
35
import socket
36
import select
37
from optparse import OptionParser
38
import datetime
39
import errno
40
import gnutls.crypto
41
import gnutls.connection
42
import gnutls.errors
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
43
import gnutls.library.functions
44
import gnutls.library.constants
45
import gnutls.library.types
3 by Björn Påhlsson
Python based server
46
import ConfigParser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
47
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
48
import re
49
import os
50
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
51
from sets import Set
52
import subprocess
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
53
import atexit
54
import stat
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
55
import logging
56
import logging.handlers
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
57
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
58
import dbus
59
import gobject
60
import avahi
61
from dbus.mainloop.glib import DBusGMainLoop
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
62
import ctypes
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
63
57 by Teddy Hogeborn
* mandos (version): New variable.
64
version = "1.0"
13 by Björn Påhlsson
Added following support:
65
66
logger = logging.Logger('mandos')
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
67
syslogger = logging.handlers.SysLogHandler\
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
68
            (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
69
             address = "/dev/log")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
70
syslogger.setFormatter(logging.Formatter\
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
71
                        ('Mandos: %(levelname)s: %(message)s'))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
72
logger.addHandler(syslogger)
13 by Björn Påhlsson
Added following support:
73
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
74
console = logging.StreamHandler()
75
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
76
                                       ' %(message)s'))
77
logger.addHandler(console)
28 by Teddy Hogeborn
* server.conf: New file.
78
79
class AvahiError(Exception):
80
    def __init__(self, value):
81
        self.value = value
82
    def __str__(self):
83
        return repr(self.value)
84
85
class AvahiServiceError(AvahiError):
86
    pass
87
88
class AvahiGroupError(AvahiError):
89
    pass
90
91
92
class AvahiService(object):
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
93
    """An Avahi (Zeroconf) service.
94
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
95
    interface: integer; avahi.IF_UNSPEC or an interface index.
96
               Used to optionally bind to the specified interface.
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
97
    name: string; Example: 'Mandos'
98
    type: string; Example: '_mandos._tcp'.
99
                  See <http://www.dns-sd.org/ServiceTypes.html>
100
    port: integer; what port to announce
101
    TXT: list of strings; TXT record for the service
102
    domain: string; Domain to publish on, default to .local if empty.
103
    host: string; Host to publish records for, default is localhost
104
    max_renames: integer; maximum number of renames
105
    rename_count: integer; counter so we only rename after collisions
106
                  a sensible number of times
28 by Teddy Hogeborn
* server.conf: New file.
107
    """
108
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
109
                 type = None, port = None, TXT = None, domain = "",
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
110
                 host = "", max_renames = 32768):
28 by Teddy Hogeborn
* server.conf: New file.
111
        self.interface = interface
112
        self.name = name
113
        self.type = type
114
        self.port = port
115
        if TXT is None:
116
            self.TXT = []
117
        else:
118
            self.TXT = TXT
119
        self.domain = domain
120
        self.host = host
121
        self.rename_count = 0
122
    def rename(self):
123
        """Derived from the Avahi example code"""
124
        if self.rename_count >= self.max_renames:
125
            logger.critical(u"No suitable service name found after %i"
126
                            u" retries, exiting.", rename_count)
127
            raise AvahiServiceError("Too many renames")
128
        name = server.GetAlternativeServiceName(name)
44 by Teddy Hogeborn
* ca.pem: Removed.
129
        logger.error(u"Changing name to %r ...", name)
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
130
        syslogger.setFormatter(logging.Formatter\
131
                               ('Mandos (%s): %%(levelname)s:'
132
                               ' %%(message)s' % name))
28 by Teddy Hogeborn
* server.conf: New file.
133
        self.remove()
134
        self.add()
135
        self.rename_count += 1
136
    def remove(self):
137
        """Derived from the Avahi example code"""
138
        if group is not None:
139
            group.Reset()
140
    def add(self):
141
        """Derived from the Avahi example code"""
142
        global group
143
        if group is None:
144
            group = dbus.Interface\
145
                    (bus.get_object(avahi.DBUS_NAME,
146
                                    server.EntryGroupNew()),
147
                     avahi.DBUS_INTERFACE_ENTRY_GROUP)
148
            group.connect_to_signal('StateChanged',
149
                                    entry_group_state_changed)
150
        logger.debug(u"Adding service '%s' of type '%s' ...",
151
                     service.name, service.type)
152
        group.AddService(
153
                self.interface,         # interface
154
                avahi.PROTO_INET6,      # protocol
155
                dbus.UInt32(0),         # flags
156
                self.name, self.type,
157
                self.domain, self.host,
158
                dbus.UInt16(self.port),
159
                avahi.string_array_to_txt_array(self.TXT))
160
        group.Commit()
161
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
162
# From the Avahi example code:
28 by Teddy Hogeborn
* server.conf: New file.
163
group = None                            # our entry group
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
164
# End of Avahi example code
165
166
3 by Björn Påhlsson
Python based server
167
class Client(object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
168
    """A representation of a client host served by this server.
169
    Attributes:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
170
    name:      string; from the config file, used in log messages
171
    fingerprint: string (40 or 32 hexadecimal digits); used to
172
                 uniquely identify the client
173
    secret:    bytestring; sent verbatim (over TLS) to client
51 by Teddy Hogeborn
* clients.conf: Better comments.
174
    host:      string; available for use by the checker command
28 by Teddy Hogeborn
* server.conf: New file.
175
    created:   datetime.datetime(); object creation, not client host
176
    last_checked_ok: datetime.datetime() or None if not yet checked OK
177
    timeout:   datetime.timedelta(); How long from last_checked_ok
178
                                     until this client is invalid
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
179
    interval:  datetime.timedelta(); How often to start a new checker
180
    stop_hook: If set, called by stop() as stop_hook(self)
181
    checker:   subprocess.Popen(); a running checker process used
182
                                   to see if the client lives.
28 by Teddy Hogeborn
* server.conf: New file.
183
                                   'None' if no process is running.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
184
    checker_initiator_tag: a gobject event source tag, or None
185
    stop_initiator_tag:    - '' -
186
    checker_callback_tag:  - '' -
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
187
    checker_command: string; External command which is run to check if
28 by Teddy Hogeborn
* server.conf: New file.
188
                     client lives.  %() expansions are done at
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
189
                     runtime with vars(self) as dict, so that for
190
                     instance %(name)s can be used in the command.
191
    Private attibutes:
192
    _timeout: Real variable for 'timeout'
193
    _interval: Real variable for 'interval'
28 by Teddy Hogeborn
* server.conf: New file.
194
    _timeout_milliseconds: Used when calling gobject.timeout_add()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
195
    _interval_milliseconds: - '' -
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
196
    """
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
197
    def _set_timeout(self, timeout):
198
        "Setter function for 'timeout' attribute"
199
        self._timeout = timeout
200
        self._timeout_milliseconds = ((self.timeout.days
201
                                       * 24 * 60 * 60 * 1000)
202
                                      + (self.timeout.seconds * 1000)
203
                                      + (self.timeout.microseconds
204
                                         // 1000))
205
    timeout = property(lambda self: self._timeout,
206
                       _set_timeout)
207
    del _set_timeout
208
    def _set_interval(self, interval):
209
        "Setter function for 'interval' attribute"
210
        self._interval = interval
211
        self._interval_milliseconds = ((self.interval.days
212
                                        * 24 * 60 * 60 * 1000)
213
                                       + (self.interval.seconds
214
                                          * 1000)
215
                                       + (self.interval.microseconds
216
                                          // 1000))
217
    interval = property(lambda self: self._interval,
218
                        _set_interval)
219
    del _set_interval
44 by Teddy Hogeborn
* ca.pem: Removed.
220
    def __init__(self, name = None, stop_hook=None, config={}):
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
221
        """Note: the 'checker' key in 'config' sets the
222
        'checker_command' attribute and *not* the 'checker'
223
        attribute."""
3 by Björn Påhlsson
Python based server
224
        self.name = name
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
225
        logger.debug(u"Creating client %r", self.name)
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
226
        # Uppercase and remove spaces from fingerprint for later
227
        # comparison purposes with return value from the fingerprint()
228
        # function
44 by Teddy Hogeborn
* ca.pem: Removed.
229
        self.fingerprint = config["fingerprint"].upper()\
230
                           .replace(u" ", u"")
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
231
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
44 by Teddy Hogeborn
* ca.pem: Removed.
232
        if "secret" in config:
233
            self.secret = config["secret"].decode(u"base64")
234
        elif "secfile" in config:
235
            sf = open(config["secfile"])
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
236
            self.secret = sf.read()
237
            sf.close()
3 by Björn Påhlsson
Python based server
238
        else:
28 by Teddy Hogeborn
* server.conf: New file.
239
            raise TypeError(u"No secret or secfile for client %s"
240
                            % self.name)
51 by Teddy Hogeborn
* clients.conf: Better comments.
241
        self.host = config.get("host", "")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
242
        self.created = datetime.datetime.now()
28 by Teddy Hogeborn
* server.conf: New file.
243
        self.last_checked_ok = None
44 by Teddy Hogeborn
* ca.pem: Removed.
244
        self.timeout = string_to_delta(config["timeout"])
245
        self.interval = string_to_delta(config["interval"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
246
        self.stop_hook = stop_hook
247
        self.checker = None
248
        self.checker_initiator_tag = None
249
        self.stop_initiator_tag = None
250
        self.checker_callback_tag = None
44 by Teddy Hogeborn
* ca.pem: Removed.
251
        self.check_command = config["checker"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
252
    def start(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
253
        """Start this client's checker and timeout hooks"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
254
        # Schedule a new checker to be started an 'interval' from now,
255
        # and every interval from then on.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
256
        self.checker_initiator_tag = gobject.timeout_add\
257
                                     (self._interval_milliseconds,
258
                                      self.start_checker)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
259
        # Also start a new checker *right now*.
260
        self.start_checker()
261
        # Schedule a stop() when 'timeout' has passed
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
262
        self.stop_initiator_tag = gobject.timeout_add\
263
                                  (self._timeout_milliseconds,
264
                                   self.stop)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
265
    def stop(self):
266
        """Stop this client.
28 by Teddy Hogeborn
* server.conf: New file.
267
        The possibility that a client might be restarted is left open,
268
        but not currently used."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
269
        # If this client doesn't have a secret, it is already stopped.
51 by Teddy Hogeborn
* clients.conf: Better comments.
270
        if hasattr(self, "secret") and self.secret:
44 by Teddy Hogeborn
* ca.pem: Removed.
271
            logger.info(u"Stopping client %s", self.name)
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
272
            self.secret = None
273
        else:
274
            return False
28 by Teddy Hogeborn
* server.conf: New file.
275
        if getattr(self, "stop_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
276
            gobject.source_remove(self.stop_initiator_tag)
277
            self.stop_initiator_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
278
        if getattr(self, "checker_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
279
            gobject.source_remove(self.checker_initiator_tag)
280
            self.checker_initiator_tag = None
281
        self.stop_checker()
282
        if self.stop_hook:
283
            self.stop_hook(self)
284
        # Do not run this again if called by a gobject.timeout_add
285
        return False
286
    def __del__(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
287
        self.stop_hook = None
288
        self.stop()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
289
    def checker_callback(self, pid, condition):
290
        """The checker has completed, so take appropriate actions."""
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
291
        now = datetime.datetime.now()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
292
        self.checker_callback_tag = None
293
        self.checker = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
294
        if os.WIFEXITED(condition) \
295
               and (os.WEXITSTATUS(condition) == 0):
44 by Teddy Hogeborn
* ca.pem: Removed.
296
            logger.info(u"Checker for %(name)s succeeded",
297
                        vars(self))
28 by Teddy Hogeborn
* server.conf: New file.
298
            self.last_checked_ok = now
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
299
            gobject.source_remove(self.stop_initiator_tag)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
300
            self.stop_initiator_tag = gobject.timeout_add\
301
                                      (self._timeout_milliseconds,
302
                                       self.stop)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
303
        elif not os.WIFEXITED(condition):
13 by Björn Påhlsson
Added following support:
304
            logger.warning(u"Checker for %(name)s crashed?",
305
                           vars(self))
306
        else:
44 by Teddy Hogeborn
* ca.pem: Removed.
307
            logger.info(u"Checker for %(name)s failed",
308
                        vars(self))
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
309
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
310
        """Start a new checker subprocess if one is not running.
311
        If a checker already exists, leave it running and do
312
        nothing."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
313
        # The reason for not killing a running checker is that if we
314
        # did that, then if a checker (for some reason) started
315
        # running slowly and taking more than 'interval' time, the
316
        # client would inevitably timeout, since no checker would get
317
        # a chance to run to completion.  If we instead leave running
318
        # checkers alone, the checker would have to take more time
319
        # than 'timeout' for the client to be declared invalid, which
320
        # is as it should be.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
321
        if self.checker is None:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
322
            try:
28 by Teddy Hogeborn
* server.conf: New file.
323
                # In case check_command has exactly one % operator
51 by Teddy Hogeborn
* clients.conf: Better comments.
324
                command = self.check_command % self.host
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
325
            except TypeError:
28 by Teddy Hogeborn
* server.conf: New file.
326
                # Escape attributes for the shell
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
327
                escaped_attrs = dict((key, re.escape(str(val)))
328
                                     for key, val in
329
                                     vars(self).iteritems())
13 by Björn Påhlsson
Added following support:
330
                try:
331
                    command = self.check_command % escaped_attrs
332
                except TypeError, error:
28 by Teddy Hogeborn
* server.conf: New file.
333
                    logger.error(u'Could not format string "%s":'
334
                                 u' %s', self.check_command, error)
13 by Björn Påhlsson
Added following support:
335
                    return True # Try again later
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
336
            try:
44 by Teddy Hogeborn
* ca.pem: Removed.
337
                logger.info(u"Starting checker %r for %s",
338
                            command, self.name)
28 by Teddy Hogeborn
* server.conf: New file.
339
                self.checker = subprocess.Popen(command,
340
                                                close_fds=True,
341
                                                shell=True, cwd="/")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
342
                self.checker_callback_tag = gobject.child_watch_add\
343
                                            (self.checker.pid,
344
                                             self.checker_callback)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
345
            except subprocess.OSError, error:
13 by Björn Påhlsson
Added following support:
346
                logger.error(u"Failed to start subprocess: %s",
347
                             error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
348
        # Re-run this periodically if run by gobject.timeout_add
349
        return True
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
350
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
351
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
352
        if self.checker_callback_tag:
353
            gobject.source_remove(self.checker_callback_tag)
354
            self.checker_callback_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
355
        if getattr(self, "checker", None) is None:
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
356
            return
51 by Teddy Hogeborn
* clients.conf: Better comments.
357
        logger.debug(u"Stopping checker for %(name)s", vars(self))
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
358
        try:
359
            os.kill(self.checker.pid, signal.SIGTERM)
360
            #os.sleep(0.5)
361
            #if self.checker.poll() is None:
362
            #    os.kill(self.checker.pid, signal.SIGKILL)
363
        except OSError, error:
28 by Teddy Hogeborn
* server.conf: New file.
364
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
365
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
366
        self.checker = None
28 by Teddy Hogeborn
* server.conf: New file.
367
    def still_valid(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
368
        """Has the timeout not yet passed for this client?"""
28 by Teddy Hogeborn
* server.conf: New file.
369
        now = datetime.datetime.now()
370
        if self.last_checked_ok is None:
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
371
            return now < (self.created + self.timeout)
372
        else:
28 by Teddy Hogeborn
* server.conf: New file.
373
            return now < (self.last_checked_ok + self.timeout)
3 by Björn Påhlsson
Python based server
374
375
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
376
def peer_certificate(session):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
377
    "Return the peer's OpenPGP certificate as a bytestring"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
378
    # If not an OpenPGP certificate...
379
    if gnutls.library.functions.gnutls_certificate_type_get\
380
            (session._c_object) \
381
           != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
382
        # ...do the normal thing
383
        return session.peer_certificate
384
    list_size = ctypes.c_uint()
385
    cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
386
        (session._c_object, ctypes.byref(list_size))
387
    if list_size.value == 0:
388
        return None
389
    cert = cert_list[0]
390
    return ctypes.string_at(cert.data, cert.size)
391
392
393
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
394
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
395
    # New GnuTLS "datum" with the OpenPGP public key
396
    datum = gnutls.library.types.gnutls_datum_t\
397
        (ctypes.cast(ctypes.c_char_p(openpgp),
398
                     ctypes.POINTER(ctypes.c_ubyte)),
399
         ctypes.c_uint(len(openpgp)))
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
400
    # New empty GnuTLS certificate
401
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
402
    gnutls.library.functions.gnutls_openpgp_crt_init\
403
        (ctypes.byref(crt))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
404
    # Import the OpenPGP public key into the certificate
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
405
    gnutls.library.functions.gnutls_openpgp_crt_import\
406
                    (crt, ctypes.byref(datum),
407
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
408
    # New buffer for the fingerprint
409
    buffer = ctypes.create_string_buffer(20)
410
    buffer_length = ctypes.c_size_t()
411
    # Get the fingerprint from the certificate into the buffer
412
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
413
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
414
    # Deinit the certificate
415
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
416
    # Convert the buffer to a Python bytestring
417
    fpr = ctypes.string_at(buffer, buffer_length.value)
418
    # Convert the bytestring to hexadecimal notation
419
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
420
    return hex_fpr
421
422
3 by Björn Påhlsson
Python based server
423
class tcp_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
424
    """A TCP request handler class.
425
    Instantiated by IPv6_TCPServer for each request to handle it.
426
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
427
    
3 by Björn Påhlsson
Python based server
428
    def handle(self):
44 by Teddy Hogeborn
* ca.pem: Removed.
429
        logger.info(u"TCP connection from: %s",
13 by Björn Påhlsson
Added following support:
430
                     unicode(self.client_address))
41 by Teddy Hogeborn
Merge.
431
        session = gnutls.connection.ClientSession\
432
                  (self.request, gnutls.connection.X509Credentials())
433
        
24.1.12 by Björn Påhlsson
merge +
434
        line = self.request.makefile().readline()
435
        logger.debug(u"Protocol version: %r", line)
24.1.11 by Björn Påhlsson
Added support for protocol version handling
436
        try:
437
            if int(line.strip().split()[0]) > 1:
438
                raise RuntimeError
439
        except (ValueError, IndexError, RuntimeError), error:
440
            logger.error(u"Unknown protocol version: %s", error)
441
            return
442
        
28 by Teddy Hogeborn
* server.conf: New file.
443
        # Note: gnutls.connection.X509Credentials is really a generic
444
        # GnuTLS certificate credentials object so long as no X.509
445
        # keys are added to it.  Therefore, we can use it here despite
446
        # using OpenPGP certificates.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
447
        
448
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
449
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
450
        #                "+DHE-DSS"))
28 by Teddy Hogeborn
* server.conf: New file.
451
        priority = "NORMAL"             # Fallback default, since this
452
                                        # MUST be set.
453
        if self.server.settings["priority"]:
454
            priority = self.server.settings["priority"]
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
455
        gnutls.library.functions.gnutls_priority_set_direct\
456
            (session._c_object, priority, None);
457
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
458
        try:
459
            session.handshake()
460
        except gnutls.errors.GNUTLSError, error:
44 by Teddy Hogeborn
* ca.pem: Removed.
461
            logger.warning(u"Handshake failed: %s", error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
462
            # Do not run session.bye() here: the session is not
463
            # established.  Just abandon the request.
464
            return
3 by Björn Påhlsson
Python based server
465
        try:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
466
            fpr = fingerprint(peer_certificate(session))
467
        except (TypeError, gnutls.errors.GNUTLSError), error:
44 by Teddy Hogeborn
* ca.pem: Removed.
468
            logger.warning(u"Bad certificate: %s", error)
3 by Björn Påhlsson
Python based server
469
            session.bye()
470
            return
13 by Björn Påhlsson
Added following support:
471
        logger.debug(u"Fingerprint: %s", fpr)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
472
        client = None
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
473
        for c in self.server.clients:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
474
            if c.fingerprint == fpr:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
475
                client = c
476
                break
28 by Teddy Hogeborn
* server.conf: New file.
477
        if not client:
44 by Teddy Hogeborn
* ca.pem: Removed.
478
            logger.warning(u"Client not found for fingerprint: %s",
479
                           fpr)
28 by Teddy Hogeborn
* server.conf: New file.
480
            session.bye()
481
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
482
        # Have to check if client.still_valid(), since it is possible
483
        # that the client timed out while establishing the GnuTLS
484
        # session.
28 by Teddy Hogeborn
* server.conf: New file.
485
        if not client.still_valid():
44 by Teddy Hogeborn
* ca.pem: Removed.
486
            logger.warning(u"Client %(name)s is invalid",
487
                           vars(client))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
488
            session.bye()
489
            return
490
        sent_size = 0
491
        while sent_size < len(client.secret):
492
            sent = session.send(client.secret[sent_size:])
13 by Björn Påhlsson
Added following support:
493
            logger.debug(u"Sent: %d, remaining: %d",
494
                         sent, len(client.secret)
495
                         - (sent_size + sent))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
496
            sent_size += sent
3 by Björn Påhlsson
Python based server
497
        session.bye()
498
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
499
3 by Björn Påhlsson
Python based server
500
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
501
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
502
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
503
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
504
        clients:        Set() of Client objects
505
    """
506
    address_family = socket.AF_INET6
507
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
508
        if "settings" in kwargs:
509
            self.settings = kwargs["settings"]
510
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
511
        if "clients" in kwargs:
512
            self.clients = kwargs["clients"]
513
            del kwargs["clients"]
514
        return super(type(self), self).__init__(*args, **kwargs)
515
    def server_bind(self):
516
        """This overrides the normal server_bind() function
517
        to bind to an interface if one was specified, and also NOT to
518
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
519
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
520
            # 25 is from /usr/include/asm-i486/socket.h
521
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
522
            try:
523
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
524
                                       SO_BINDTODEVICE,
525
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
526
            except socket.error, error:
527
                if error[0] == errno.EPERM:
44 by Teddy Hogeborn
* ca.pem: Removed.
528
                    logger.error(u"No permission to"
529
                                 u" bind to interface %s",
530
                                 self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
531
                else:
532
                    raise error
533
        # Only bind(2) the socket if we really need to.
534
        if self.server_address[0] or self.server_address[1]:
535
            if not self.server_address[0]:
536
                in6addr_any = "::"
537
                self.server_address = (in6addr_any,
538
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
539
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
540
                self.server_address = (self.server_address[0],
541
                                       0)
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
542
#                 if self.settings["interface"]:
543
#                     self.server_address = (self.server_address[0],
544
#                                            0, # port
545
#                                            0, # flowinfo
546
#                                            if_nametoindex
547
#                                            (self.settings
548
#                                             ["interface"]))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
549
            return super(type(self), self).server_bind()
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
550
3 by Björn Påhlsson
Python based server
551
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
552
def string_to_delta(interval):
553
    """Parse a string and return a datetime.timedelta
554
555
    >>> string_to_delta('7d')
556
    datetime.timedelta(7)
557
    >>> string_to_delta('60s')
558
    datetime.timedelta(0, 60)
559
    >>> string_to_delta('60m')
560
    datetime.timedelta(0, 3600)
561
    >>> string_to_delta('24h')
562
    datetime.timedelta(1)
563
    >>> string_to_delta(u'1w')
564
    datetime.timedelta(7)
565
    """
566
    try:
567
        suffix=unicode(interval[-1])
568
        value=int(interval[:-1])
569
        if suffix == u"d":
570
            delta = datetime.timedelta(value)
571
        elif suffix == u"s":
572
            delta = datetime.timedelta(0, value)
573
        elif suffix == u"m":
574
            delta = datetime.timedelta(0, 0, 0, 0, value)
575
        elif suffix == u"h":
576
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
577
        elif suffix == u"w":
578
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
579
        else:
580
            raise ValueError
581
    except (ValueError, IndexError):
582
        raise ValueError
583
    return delta
584
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
585
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
586
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
587
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
588
    if state == avahi.SERVER_COLLISION:
44 by Teddy Hogeborn
* ca.pem: Removed.
589
        logger.error(u"Server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
590
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
591
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
592
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
593
594
595
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
596
    """Derived from the Avahi example code"""
13 by Björn Påhlsson
Added following support:
597
    logger.debug(u"state change: %i", state)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
598
    
599
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
13 by Björn Påhlsson
Added following support:
600
        logger.debug(u"Service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
601
    elif state == avahi.ENTRY_GROUP_COLLISION:
28 by Teddy Hogeborn
* server.conf: New file.
602
        logger.warning(u"Service name collision.")
603
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
604
    elif state == avahi.ENTRY_GROUP_FAILURE:
28 by Teddy Hogeborn
* server.conf: New file.
605
        logger.critical(u"Error in group state changed %s",
606
                        unicode(error))
607
        raise AvahiGroupError("State changed: %s", str(error))
608
24.1.13 by Björn Påhlsson
mandosclient
609
def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
610
    """Call the C function if_nametoindex(), or equivalent"""
24.1.13 by Björn Påhlsson
mandosclient
611
    global if_nametoindex
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
612
    try:
28 by Teddy Hogeborn
* server.conf: New file.
613
        if "ctypes.util" not in sys.modules:
614
            import ctypes.util
24.1.13 by Björn Påhlsson
mandosclient
615
        if_nametoindex = ctypes.cdll.LoadLibrary\
616
            (ctypes.util.find_library("c")).if_nametoindex
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
617
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
618
        if "struct" not in sys.modules:
619
            import struct
620
        if "fcntl" not in sys.modules:
621
            import fcntl
24.1.13 by Björn Påhlsson
mandosclient
622
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
623
            "Get an interface index the hard way, i.e. using fcntl()"
624
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
625
            s = socket.socket()
626
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
627
                                struct.pack("16s16x", interface))
628
            s.close()
629
            interface_index = struct.unpack("I", ifreq[16:20])[0]
630
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
631
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
632
633
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
634
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
635
    """See daemon(3).  Standard BSD Unix function.
636
    This should really exist as os.daemon, but it doesn't (yet)."""
637
    if os.fork():
638
        sys.exit()
639
    os.setsid()
640
    if not nochdir:
641
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
642
    if os.fork():
643
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
644
    if not noclose:
645
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
646
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
647
        if not stat.S_ISCHR(os.fstat(null).st_mode):
648
            raise OSError(errno.ENODEV,
649
                          "/dev/null not a character device")
650
        os.dup2(null, sys.stdin.fileno())
651
        os.dup2(null, sys.stdout.fileno())
652
        os.dup2(null, sys.stderr.fileno())
653
        if null > 2:
654
            os.close(null)
655
656
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
657
def main():
658
    global main_loop_started
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
659
    main_loop_started = False
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
660
    
59 by Teddy Hogeborn
* mandos (main): Use program name in --version output.
661
    parser = OptionParser(version = "%%prog %s" % version)
3 by Björn Påhlsson
Python based server
662
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
663
                      metavar="IF", help="Bind to interface IF")
664
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
665
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
666
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
667
                      help="Port number to receive requests on")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
668
    parser.add_option("--check", action="store_true", default=False,
669
                      help="Run self-test")
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
670
    parser.add_option("--debug", action="store_true",
28 by Teddy Hogeborn
* server.conf: New file.
671
                      help="Debug mode; run in foreground and log to"
672
                      " terminal")
673
    parser.add_option("--priority", type="string", help="GnuTLS"
674
                      " priority string (see GnuTLS documentation)")
675
    parser.add_option("--servicename", type="string", metavar="NAME",
676
                      help="Zeroconf service name")
677
    parser.add_option("--configdir", type="string",
678
                      default="/etc/mandos", metavar="DIR",
679
                      help="Directory to search for configuration"
680
                      " files")
3 by Björn Påhlsson
Python based server
681
    (options, args) = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
682
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
683
    if options.check:
684
        import doctest
685
        doctest.testmod()
686
        sys.exit()
3 by Björn Påhlsson
Python based server
687
    
28 by Teddy Hogeborn
* server.conf: New file.
688
    # Default values for config file for server-global settings
689
    server_defaults = { "interface": "",
690
                        "address": "",
691
                        "port": "",
692
                        "debug": "False",
693
                        "priority":
694
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
695
                        "servicename": "Mandos",
696
                        }
697
    
698
    # Parse config file for server-global settings
699
    server_config = ConfigParser.SafeConfigParser(server_defaults)
700
    del server_defaults
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
701
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
702
    server_section = "server"
703
    # Convert the SafeConfigParser object to a dict
704
    server_settings = dict(server_config.items(server_section))
705
    # Use getboolean on the boolean config option
706
    server_settings["debug"] = server_config.getboolean\
707
                               (server_section, "debug")
708
    del server_config
709
    
710
    # Override the settings from the config file with command line
711
    # options, if set.
712
    for option in ("interface", "address", "port", "debug",
713
                   "priority", "servicename", "configdir"):
714
        value = getattr(options, option)
715
        if value is not None:
716
            server_settings[option] = value
717
    del options
718
    # Now we have our good server settings in "server_settings"
719
    
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
720
    debug = server_settings["debug"]
721
    
722
    if not debug:
723
        syslogger.setLevel(logging.WARNING)
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
724
        console.setLevel(logging.WARNING)
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
725
    
726
    if server_settings["servicename"] != "Mandos":
727
        syslogger.setFormatter(logging.Formatter\
728
                               ('Mandos (%s): %%(levelname)s:'
729
                                ' %%(message)s'
730
                                % server_settings["servicename"]))
731
    
28 by Teddy Hogeborn
* server.conf: New file.
732
    # Parse config file with clients
733
    client_defaults = { "timeout": "1h",
734
                        "interval": "5m",
51 by Teddy Hogeborn
* clients.conf: Better comments.
735
                        "checker": "fping -q -- %%(host)s",
28 by Teddy Hogeborn
* server.conf: New file.
736
                        }
737
    client_config = ConfigParser.SafeConfigParser(client_defaults)
738
    client_config.read(os.path.join(server_settings["configdir"],
739
                                    "clients.conf"))
740
    
741
    global service
742
    service = AvahiService(name = server_settings["servicename"],
743
                           type = "_mandos._tcp", );
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
744
    if server_settings["interface"]:
745
        service.interface = if_nametoindex(server_settings["interface"])
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
746
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
747
    global main_loop
748
    global bus
749
    global server
750
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
751
    DBusGMainLoop(set_as_default=True )
752
    main_loop = gobject.MainLoop()
753
    bus = dbus.SystemBus()
754
    server = dbus.Interface(
755
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
756
            avahi.DBUS_INTERFACE_SERVER )
757
    # End of Avahi example code
758
    
759
    clients = Set()
760
    def remove_from_clients(client):
761
        clients.remove(client)
762
        if not clients:
44 by Teddy Hogeborn
* ca.pem: Removed.
763
            logger.critical(u"No clients left, exiting")
28 by Teddy Hogeborn
* server.conf: New file.
764
            sys.exit()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
765
    
44 by Teddy Hogeborn
* ca.pem: Removed.
766
    clients.update(Set(Client(name = section,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
767
                              stop_hook = remove_from_clients,
44 by Teddy Hogeborn
* ca.pem: Removed.
768
                              config
769
                              = dict(client_config.items(section)))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
770
                       for section in client_config.sections()))
51 by Teddy Hogeborn
* clients.conf: Better comments.
771
    if not clients:
772
        logger.critical(u"No clients defined")
773
        sys.exit(1)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
774
    
775
    if not debug:
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
776
        logger.removeHandler(console)
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
777
        daemon()
51 by Teddy Hogeborn
* clients.conf: Better comments.
778
    
50 by Teddy Hogeborn
* Makefile: Do DocBook manual conversion in a better way.
779
    pidfilename = "/var/run/mandos/mandos.pid"
780
    pid = os.getpid()
781
    try:
782
        pidfile = open(pidfilename, "w")
783
        pidfile.write(str(pid) + "\n")
784
        pidfile.close()
785
        del pidfile
786
    except IOError, err:
51 by Teddy Hogeborn
* clients.conf: Better comments.
787
        logger.error(u"Could not write %s file with PID %d",
50 by Teddy Hogeborn
* Makefile: Do DocBook manual conversion in a better way.
788
                     pidfilename, os.getpid())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
789
    
790
    def cleanup():
791
        "Cleanup function; run on exit"
792
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
793
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
794
        if not group is None:
795
            group.Free()
796
            group = None
797
        # End of Avahi example code
798
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
799
        while clients:
800
            client = clients.pop()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
801
            client.stop_hook = None
802
            client.stop()
803
    
804
    atexit.register(cleanup)
805
    
806
    if not debug:
807
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
808
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
809
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
810
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
811
    for client in clients:
812
        client.start()
813
    
28 by Teddy Hogeborn
* server.conf: New file.
814
    tcp_server = IPv6_TCPServer((server_settings["address"],
815
                                 server_settings["port"]),
3 by Björn Påhlsson
Python based server
816
                                tcp_handler,
28 by Teddy Hogeborn
* server.conf: New file.
817
                                settings=server_settings,
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
818
                                clients=clients)
28 by Teddy Hogeborn
* server.conf: New file.
819
    # Find out what port we got
820
    service.port = tcp_server.socket.getsockname()[1]
44 by Teddy Hogeborn
* ca.pem: Removed.
821
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
822
                u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
823
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
824
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
825
    
826
    try:
827
        # From the Avahi example code
828
        server.connect_to_signal("StateChanged", server_state_changed)
829
        try:
830
            server_state_changed(server.GetState())
831
        except dbus.exceptions.DBusException, error:
832
            logger.critical(u"DBusException: %s", error)
833
            sys.exit(1)
834
        # End of Avahi example code
835
        
836
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
837
                             lambda *args, **kwargs:
838
                             tcp_server.handle_request\
839
                             (*args[2:], **kwargs) or True)
840
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
841
        logger.debug(u"Starting main loop")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
842
        main_loop_started = True
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
843
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
844
    except AvahiError, error:
845
        logger.critical(u"AvahiError: %s" + unicode(error))
846
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
847
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
848
        if debug:
849
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
850
851
if __name__ == '__main__':
852
    main()