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