/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
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
14
# Copyright © 2008 Teddy Hogeborn
15
# Copyright © 2008 Björn Påhlsson
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
16
# 
17
# This program is free software: you can redistribute it and/or modify
18
# it under the terms of the GNU General Public License as published by
19
# the Free Software Foundation, either version 3 of the License, or
20
# (at your option) any later version.
21
#
22
#     This program is distributed in the hope that it will be useful,
23
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
24
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
#     GNU General Public License for more details.
26
# 
27
# You should have received a copy of the GNU General Public License
109 by Teddy Hogeborn
* .bzrignore: New.
28
# along with this program.  If not, see
29
# <http://www.gnu.org/licenses/>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
30
# 
28 by Teddy Hogeborn
* server.conf: New file.
31
# Contact the authors at <mandos@fukt.bsnet.se>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
32
# 
3 by Björn Påhlsson
Python based server
33
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
34
from __future__ import division, with_statement, absolute_import
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
35
3 by Björn Påhlsson
Python based server
36
import SocketServer
37
import socket
38
from optparse import OptionParser
39
import datetime
40
import errno
41
import gnutls.crypto
42
import gnutls.connection
43
import gnutls.errors
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
44
import gnutls.library.functions
45
import gnutls.library.constants
46
import gnutls.library.types
3 by Björn Påhlsson
Python based server
47
import ConfigParser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
48
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
49
import re
50
import os
51
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
52
from sets import Set
53
import subprocess
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
54
import atexit
55
import stat
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
56
import logging
57
import logging.handlers
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
58
import pwd
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
59
from contextlib import closing
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
60
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
61
import dbus
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
62
import dbus.service
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
63
import gobject
64
import avahi
65
from dbus.mainloop.glib import DBusGMainLoop
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
66
import ctypes
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
67
import ctypes.util
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
68
232 by Teddy Hogeborn
* Makefile (version): Changed to "1.0.2".
69
version = "1.0.2"
13 by Björn Påhlsson
Added following support:
70
71
logger = logging.Logger('mandos')
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
72
syslogger = (logging.handlers.SysLogHandler
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
74
              address = "/dev/log"))
75
syslogger.setFormatter(logging.Formatter
76
                       ('Mandos: %(levelname)s: %(message)s'))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
77
logger.addHandler(syslogger)
13 by Björn Påhlsson
Added following support:
78
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
79
console = logging.StreamHandler()
80
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
81
                                       ' %(message)s'))
82
logger.addHandler(console)
28 by Teddy Hogeborn
* server.conf: New file.
83
84
class AvahiError(Exception):
85
    def __init__(self, value):
86
        self.value = value
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
87
        super(AvahiError, self).__init__()
28 by Teddy Hogeborn
* server.conf: New file.
88
    def __str__(self):
89
        return repr(self.value)
90
91
class AvahiServiceError(AvahiError):
92
    pass
93
94
class AvahiGroupError(AvahiError):
95
    pass
96
97
98
class AvahiService(object):
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
99
    """An Avahi (Zeroconf) service.
100
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
101
    interface: integer; avahi.IF_UNSPEC or an interface index.
102
               Used to optionally bind to the specified interface.
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
103
    name: string; Example: 'Mandos'
104
    type: string; Example: '_mandos._tcp'.
105
                  See <http://www.dns-sd.org/ServiceTypes.html>
106
    port: integer; what port to announce
107
    TXT: list of strings; TXT record for the service
108
    domain: string; Domain to publish on, default to .local if empty.
109
    host: string; Host to publish records for, default is localhost
110
    max_renames: integer; maximum number of renames
111
    rename_count: integer; counter so we only rename after collisions
112
                  a sensible number of times
28 by Teddy Hogeborn
* server.conf: New file.
113
    """
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
115
                 servicetype = None, port = None, TXT = None,
116
                 domain = "", host = "", max_renames = 32768):
28 by Teddy Hogeborn
* server.conf: New file.
117
        self.interface = interface
118
        self.name = name
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
119
        self.type = servicetype
28 by Teddy Hogeborn
* server.conf: New file.
120
        self.port = port
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
121
        self.TXT = TXT if TXT is not None else []
28 by Teddy Hogeborn
* server.conf: New file.
122
        self.domain = domain
123
        self.host = host
124
        self.rename_count = 0
76 by Teddy Hogeborn
* plugins.d/password-request.c (init_gnutls_global): Renamed
125
        self.max_renames = max_renames
28 by Teddy Hogeborn
* server.conf: New file.
126
    def rename(self):
127
        """Derived from the Avahi example code"""
128
        if self.rename_count >= self.max_renames:
109 by Teddy Hogeborn
* .bzrignore: New.
129
            logger.critical(u"No suitable Zeroconf service name found"
130
                            u" after %i retries, exiting.",
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
131
                            self.rename_count)
28 by Teddy Hogeborn
* server.conf: New file.
132
            raise AvahiServiceError("Too many renames")
76 by Teddy Hogeborn
* plugins.d/password-request.c (init_gnutls_global): Renamed
133
        self.name = server.GetAlternativeServiceName(self.name)
109 by Teddy Hogeborn
* .bzrignore: New.
134
        logger.info(u"Changing Zeroconf service name to %r ...",
135
                    str(self.name))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
136
        syslogger.setFormatter(logging.Formatter
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
137
                               ('Mandos (%s): %%(levelname)s:'
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
138
                                ' %%(message)s' % self.name))
28 by Teddy Hogeborn
* server.conf: New file.
139
        self.remove()
140
        self.add()
141
        self.rename_count += 1
142
    def remove(self):
143
        """Derived from the Avahi example code"""
144
        if group is not None:
145
            group.Reset()
146
    def add(self):
147
        """Derived from the Avahi example code"""
148
        global group
149
        if group is None:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
150
            group = dbus.Interface(bus.get_object
151
                                   (avahi.DBUS_NAME,
28 by Teddy Hogeborn
* server.conf: New file.
152
                                    server.EntryGroupNew()),
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
153
                                   avahi.DBUS_INTERFACE_ENTRY_GROUP)
28 by Teddy Hogeborn
* server.conf: New file.
154
            group.connect_to_signal('StateChanged',
155
                                    entry_group_state_changed)
109 by Teddy Hogeborn
* .bzrignore: New.
156
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
28 by Teddy Hogeborn
* server.conf: New file.
157
                     service.name, service.type)
158
        group.AddService(
159
                self.interface,         # interface
160
                avahi.PROTO_INET6,      # protocol
161
                dbus.UInt32(0),         # flags
162
                self.name, self.type,
163
                self.domain, self.host,
164
                dbus.UInt16(self.port),
165
                avahi.string_array_to_txt_array(self.TXT))
166
        group.Commit()
167
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
168
# From the Avahi example code:
28 by Teddy Hogeborn
* server.conf: New file.
169
group = None                            # our entry group
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
170
# End of Avahi example code
171
172
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
173
def _datetime_to_dbus_struct(dt, variant_level=0):
174
    """Convert a UTC datetime.datetime() to a D-Bus struct.
175
    The format is special to this application, since we could not find
176
    any other standard way."""
177
    return dbus.Struct((dbus.Int16(dt.year),
178
                        dbus.Byte(dt.month),
179
                        dbus.Byte(dt.day),
180
                        dbus.Byte(dt.hour),
181
                        dbus.Byte(dt.minute),
182
                        dbus.Byte(dt.second),
183
                        dbus.UInt32(dt.microsecond)),
184
                       signature="nyyyyyu",
185
                       variant_level=variant_level)
186
187
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
188
class Client(dbus.service.Object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
189
    """A representation of a client host served by this server.
190
    Attributes:
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
191
    name:       string; from the config file, used in log messages
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
192
    fingerprint: string (40 or 32 hexadecimal digits); used to
193
                 uniquely identify the client
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
194
    secret:     bytestring; sent verbatim (over TLS) to client
195
    host:       string; available for use by the checker command
196
    created:    datetime.datetime(); (UTC) object creation
197
    last_started: datetime.datetime(); (UTC)
198
    started:    bool()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
199
    last_checked_ok: datetime.datetime(); (UTC) or None
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
200
    timeout:    datetime.timedelta(); How long from last_checked_ok
201
                                      until this client is invalid
202
    interval:   datetime.timedelta(); How often to start a new checker
203
    stop_hook:  If set, called by stop() as stop_hook(self)
204
    checker:    subprocess.Popen(); a running checker process used
205
                                    to see if the client lives.
206
                                    'None' if no process is running.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
207
    checker_initiator_tag: a gobject event source tag, or None
208
    stop_initiator_tag:    - '' -
209
    checker_callback_tag:  - '' -
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
210
    checker_command: string; External command which is run to check if
28 by Teddy Hogeborn
* server.conf: New file.
211
                     client lives.  %() expansions are done at
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
212
                     runtime with vars(self) as dict, so that for
213
                     instance %(name)s can be used in the command.
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
214
    dbus_object_path: dbus.ObjectPath
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
215
    Private attibutes:
216
    _timeout: Real variable for 'timeout'
217
    _interval: Real variable for 'interval'
28 by Teddy Hogeborn
* server.conf: New file.
218
    _timeout_milliseconds: Used when calling gobject.timeout_add()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
219
    _interval_milliseconds: - '' -
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
220
    """
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
221
    def _set_timeout(self, timeout):
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
222
        "Setter function for the 'timeout' attribute"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
223
        self._timeout = timeout
224
        self._timeout_milliseconds = ((self.timeout.days
225
                                       * 24 * 60 * 60 * 1000)
226
                                      + (self.timeout.seconds * 1000)
227
                                      + (self.timeout.microseconds
228
                                         // 1000))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
229
        # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
230
        self.PropertyChanged(dbus.String(u"timeout"),
231
                             (dbus.UInt64(self._timeout_milliseconds,
232
                                          variant_level=1)))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
233
    timeout = property(lambda self: self._timeout, _set_timeout)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
234
    del _set_timeout
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
235
    
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
236
    def _set_interval(self, interval):
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
237
        "Setter function for the 'interval' attribute"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
238
        self._interval = interval
239
        self._interval_milliseconds = ((self.interval.days
240
                                        * 24 * 60 * 60 * 1000)
241
                                       + (self.interval.seconds
242
                                          * 1000)
243
                                       + (self.interval.microseconds
244
                                          // 1000))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
245
        # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
246
        self.PropertyChanged(dbus.String(u"interval"),
247
                             (dbus.UInt64(self._interval_milliseconds,
248
                                          variant_level=1)))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
249
    interval = property(lambda self: self._interval, _set_interval)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
250
    del _set_interval
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
251
    
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
252
    def __init__(self, name = None, stop_hook=None, config=None):
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
253
        """Note: the 'checker' key in 'config' sets the
254
        'checker_command' attribute and *not* the 'checker'
255
        attribute."""
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
256
        self.dbus_object_path = (dbus.ObjectPath
257
                                 ("/Mandos/clients/"
258
                                  + name.replace(".", "_")))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
259
        dbus.service.Object.__init__(self, bus,
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
260
                                     self.dbus_object_path)
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
261
        if config is None:
262
            config = {}
3 by Björn Påhlsson
Python based server
263
        self.name = name
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
264
        logger.debug(u"Creating client %r", self.name)
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
265
        # Uppercase and remove spaces from fingerprint for later
266
        # comparison purposes with return value from the fingerprint()
267
        # function
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
268
        self.fingerprint = (config["fingerprint"].upper()
269
                            .replace(u" ", u""))
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
270
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
44 by Teddy Hogeborn
* ca.pem: Removed.
271
        if "secret" in config:
272
            self.secret = config["secret"].decode(u"base64")
273
        elif "secfile" in config:
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
274
            with closing(open(os.path.expanduser
275
                              (os.path.expandvars
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
276
                               (config["secfile"])))) as secfile:
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
277
                self.secret = secfile.read()
3 by Björn Påhlsson
Python based server
278
        else:
28 by Teddy Hogeborn
* server.conf: New file.
279
            raise TypeError(u"No secret or secfile for client %s"
280
                            % self.name)
51 by Teddy Hogeborn
* clients.conf: Better comments.
281
        self.host = config.get("host", "")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
282
        self.created = datetime.datetime.utcnow()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
283
        self.started = False
284
        self.last_started = None
28 by Teddy Hogeborn
* server.conf: New file.
285
        self.last_checked_ok = None
44 by Teddy Hogeborn
* ca.pem: Removed.
286
        self.timeout = string_to_delta(config["timeout"])
287
        self.interval = string_to_delta(config["interval"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
288
        self.stop_hook = stop_hook
289
        self.checker = None
290
        self.checker_initiator_tag = None
291
        self.stop_initiator_tag = None
292
        self.checker_callback_tag = None
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
293
        self.checker_command = config["checker"]
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
294
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
295
    def start(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
296
        """Start this client's checker and timeout hooks"""
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
297
        self.last_started = datetime.datetime.utcnow()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
298
        # Schedule a new checker to be started an 'interval' from now,
299
        # and every interval from then on.
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
300
        self.checker_initiator_tag = (gobject.timeout_add
301
                                      (self._interval_milliseconds,
302
                                       self.start_checker))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
303
        # Also start a new checker *right now*.
304
        self.start_checker()
305
        # Schedule a stop() when 'timeout' has passed
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
306
        self.stop_initiator_tag = (gobject.timeout_add
307
                                   (self._timeout_milliseconds,
308
                                    self.stop))
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
309
        self.started = True
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
310
        # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
311
        self.PropertyChanged(dbus.String(u"started"),
312
                             dbus.Boolean(True, variant_level=1))
313
        self.PropertyChanged(dbus.String(u"last_started"),
314
                             (_datetime_to_dbus_struct
315
                              (self.last_started, variant_level=1)))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
316
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
317
    def stop(self):
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
318
        """Stop this client."""
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
319
        if not getattr(self, "started", False):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
320
            return False
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
321
        logger.info(u"Stopping client %s", self.name)
28 by Teddy Hogeborn
* server.conf: New file.
322
        if getattr(self, "stop_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
323
            gobject.source_remove(self.stop_initiator_tag)
324
            self.stop_initiator_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
325
        if getattr(self, "checker_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
326
            gobject.source_remove(self.checker_initiator_tag)
327
            self.checker_initiator_tag = None
328
        self.stop_checker()
329
        if self.stop_hook:
330
            self.stop_hook(self)
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
331
        self.started = False
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
332
        # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
333
        self.PropertyChanged(dbus.String(u"started"),
334
                             dbus.Boolean(False, variant_level=1))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
335
        # Do not run this again if called by a gobject.timeout_add
336
        return False
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
337
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
338
    def __del__(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
339
        self.stop_hook = None
340
        self.stop()
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
341
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
342
    def checker_callback(self, pid, condition, command):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
343
        """The checker has completed, so take appropriate actions."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
344
        self.checker_callback_tag = None
345
        self.checker = None
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
346
        # Emit D-Bus signal
347
        self.PropertyChanged(dbus.String(u"checker_running"),
348
                             dbus.Boolean(False, variant_level=1))
349
        if (os.WIFEXITED(condition)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
350
            and (os.WEXITSTATUS(condition) == 0)):
44 by Teddy Hogeborn
* ca.pem: Removed.
351
            logger.info(u"Checker for %(name)s succeeded",
352
                        vars(self))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
353
            # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
354
            self.CheckerCompleted(dbus.Boolean(True),
355
                                  dbus.UInt16(condition),
356
                                  dbus.String(command))
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
357
            self.bump_timeout()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
358
        elif not os.WIFEXITED(condition):
13 by Björn Påhlsson
Added following support:
359
            logger.warning(u"Checker for %(name)s crashed?",
360
                           vars(self))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
361
            # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
362
            self.CheckerCompleted(dbus.Boolean(False),
363
                                  dbus.UInt16(condition),
364
                                  dbus.String(command))
13 by Björn Påhlsson
Added following support:
365
        else:
44 by Teddy Hogeborn
* ca.pem: Removed.
366
            logger.info(u"Checker for %(name)s failed",
367
                        vars(self))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
368
            # Emit D-Bus signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
369
            self.CheckerCompleted(dbus.Boolean(False),
370
                                  dbus.UInt16(condition),
371
                                  dbus.String(command))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
372
    
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
373
    def bump_timeout(self):
374
        """Bump up the timeout for this client.
375
        This should only be called when the client has been seen,
376
        alive and well.
377
        """
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
378
        self.last_checked_ok = datetime.datetime.utcnow()
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
379
        gobject.source_remove(self.stop_initiator_tag)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
380
        self.stop_initiator_tag = (gobject.timeout_add
381
                                   (self._timeout_milliseconds,
382
                                    self.stop))
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
383
        self.PropertyChanged(dbus.String(u"last_checked_ok"),
384
                             (_datetime_to_dbus_struct
385
                              (self.last_checked_ok,
386
                               variant_level=1)))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
387
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
388
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
389
        """Start a new checker subprocess if one is not running.
390
        If a checker already exists, leave it running and do
391
        nothing."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
392
        # The reason for not killing a running checker is that if we
393
        # did that, then if a checker (for some reason) started
394
        # running slowly and taking more than 'interval' time, the
395
        # client would inevitably timeout, since no checker would get
396
        # a chance to run to completion.  If we instead leave running
397
        # checkers alone, the checker would have to take more time
398
        # than 'timeout' for the client to be declared invalid, which
399
        # is as it should be.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
400
        if self.checker is None:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
401
            try:
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
402
                # In case checker_command has exactly one % operator
403
                command = self.checker_command % self.host
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
404
            except TypeError:
28 by Teddy Hogeborn
* server.conf: New file.
405
                # Escape attributes for the shell
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
406
                escaped_attrs = dict((key, re.escape(str(val)))
407
                                     for key, val in
408
                                     vars(self).iteritems())
13 by Björn Påhlsson
Added following support:
409
                try:
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
410
                    command = self.checker_command % escaped_attrs
13 by Björn Påhlsson
Added following support:
411
                except TypeError, error:
28 by Teddy Hogeborn
* server.conf: New file.
412
                    logger.error(u'Could not format string "%s":'
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
413
                                 u' %s', self.checker_command, error)
13 by Björn Påhlsson
Added following support:
414
                    return True # Try again later
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
415
            try:
44 by Teddy Hogeborn
* ca.pem: Removed.
416
                logger.info(u"Starting checker %r for %s",
417
                            command, self.name)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
418
                # We don't need to redirect stdout and stderr, since
419
                # in normal mode, that is already done by daemon(),
420
                # and in debug mode we don't want to.  (Stdin is
421
                # always replaced by /dev/null.)
28 by Teddy Hogeborn
* server.conf: New file.
422
                self.checker = subprocess.Popen(command,
423
                                                close_fds=True,
424
                                                shell=True, cwd="/")
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
425
                # Emit D-Bus signal
426
                self.CheckerStarted(command)
427
                self.PropertyChanged(dbus.String("checker_running"),
428
                                     dbus.Boolean(True, variant_level=1))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
429
                self.checker_callback_tag = (gobject.child_watch_add
430
                                             (self.checker.pid,
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
431
                                              self.checker_callback,
432
                                              data=command))
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
433
            except OSError, error:
13 by Björn Påhlsson
Added following support:
434
                logger.error(u"Failed to start subprocess: %s",
435
                             error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
436
        # Re-run this periodically if run by gobject.timeout_add
437
        return True
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
438
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
439
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
440
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
441
        if self.checker_callback_tag:
442
            gobject.source_remove(self.checker_callback_tag)
443
            self.checker_callback_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
444
        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.
445
            return
51 by Teddy Hogeborn
* clients.conf: Better comments.
446
        logger.debug(u"Stopping checker for %(name)s", vars(self))
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
447
        try:
448
            os.kill(self.checker.pid, signal.SIGTERM)
449
            #os.sleep(0.5)
450
            #if self.checker.poll() is None:
451
            #    os.kill(self.checker.pid, signal.SIGKILL)
452
        except OSError, error:
28 by Teddy Hogeborn
* server.conf: New file.
453
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
454
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
455
        self.checker = None
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
456
        self.PropertyChanged(dbus.String(u"checker_running"),
457
                             dbus.Boolean(False, variant_level=1))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
458
    
28 by Teddy Hogeborn
* server.conf: New file.
459
    def still_valid(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
460
        """Has the timeout not yet passed for this client?"""
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
461
        if not getattr(self, "started", False):
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
462
            return False
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
463
        now = datetime.datetime.utcnow()
28 by Teddy Hogeborn
* server.conf: New file.
464
        if self.last_checked_ok is None:
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
465
            return now < (self.created + self.timeout)
466
        else:
28 by Teddy Hogeborn
* server.conf: New file.
467
            return now < (self.last_checked_ok + self.timeout)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
468
    
469
    ## D-Bus methods & signals
470
    _interface = u"org.mandos_system.Mandos.Client"
471
    
472
    # BumpTimeout - method
473
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
474
    BumpTimeout.__name__ = "BumpTimeout"
475
    
476
    # CheckerCompleted - signal
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
477
    @dbus.service.signal(_interface, signature="bqs")
478
    def CheckerCompleted(self, success, condition, command):
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
479
        "D-Bus signal"
480
        pass
481
    
482
    # CheckerStarted - signal
483
    @dbus.service.signal(_interface, signature="s")
484
    def CheckerStarted(self, command):
485
        "D-Bus signal"
486
        pass
487
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
488
    # GetAllProperties - method
489
    @dbus.service.method(_interface, out_signature="a{sv}")
490
    def GetAllProperties(self):
491
        "D-Bus method"
492
        return dbus.Dictionary({
493
                dbus.String("name"):
494
                    dbus.String(self.name, variant_level=1),
495
                dbus.String("fingerprint"):
496
                    dbus.String(self.fingerprint, variant_level=1),
497
                dbus.String("host"):
498
                    dbus.String(self.host, variant_level=1),
499
                dbus.String("created"):
500
                    _datetime_to_dbus_struct(self.created,
501
                                             variant_level=1),
502
                dbus.String("last_started"):
503
                    (_datetime_to_dbus_struct(self.last_started,
504
                                              variant_level=1)
505
                     if self.last_started is not None
506
                     else dbus.Boolean(False, variant_level=1)),
507
                dbus.String("started"):
508
                    dbus.Boolean(self.started, variant_level=1),
509
                dbus.String("last_checked_ok"):
510
                    (_datetime_to_dbus_struct(self.last_checked_ok,
511
                                              variant_level=1)
512
                     if self.last_checked_ok is not None
513
                     else dbus.Boolean (False, variant_level=1)),
514
                dbus.String("timeout"):
515
                    dbus.UInt64(self._timeout_milliseconds,
516
                                variant_level=1),
517
                dbus.String("interval"):
518
                    dbus.UInt64(self._interval_milliseconds,
519
                                variant_level=1),
520
                dbus.String("checker"):
521
                    dbus.String(self.checker_command,
522
                                variant_level=1),
523
                dbus.String("checker_running"):
524
                    dbus.Boolean(self.checker is not None,
525
                                 variant_level=1),
526
                }, signature="sv")
527
    
528
    # IsStillValid - method
529
    IsStillValid = (dbus.service.method(_interface, out_signature="b")
530
                    (still_valid))
531
    IsStillValid.__name__ = "IsStillValid"
532
    
533
    # PropertyChanged - signal
534
    @dbus.service.signal(_interface, signature="sv")
535
    def PropertyChanged(self, property, value):
536
        "D-Bus signal"
537
        pass
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
538
    
539
    # SetChecker - method
540
    @dbus.service.method(_interface, in_signature="s")
541
    def SetChecker(self, checker):
542
        "D-Bus setter method"
543
        self.checker_command = checker
544
    
545
    # SetHost - method
546
    @dbus.service.method(_interface, in_signature="s")
547
    def SetHost(self, host):
548
        "D-Bus setter method"
549
        self.host = host
550
    
551
    # SetInterval - method
552
    @dbus.service.method(_interface, in_signature="t")
553
    def SetInterval(self, milliseconds):
554
        self.interval = datetime.timdeelta(0, 0, 0, milliseconds)
555
    
556
    # SetSecret - method
557
    @dbus.service.method(_interface, in_signature="ay",
558
                         byte_arrays=True)
559
    def SetSecret(self, secret):
560
        "D-Bus setter method"
561
        self.secret = str(secret)
562
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
563
    # SetTimeout - method
564
    @dbus.service.method(_interface, in_signature="t")
565
    def SetTimeout(self, milliseconds):
566
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
567
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
568
    # Start - method
569
    Start = dbus.service.method(_interface)(start)
570
    Start.__name__ = "Start"
571
    
572
    # StartChecker - method
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
573
    @dbus.service.method(_interface)
574
    def StartChecker(self):
575
        "D-Bus method"
576
        self.start_checker()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
577
    
578
    # Stop - method
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
579
    @dbus.service.method(_interface)
580
    def Stop(self):
581
        "D-Bus method"
582
        self.stop()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
583
    
584
    # StopChecker - method
585
    StopChecker = dbus.service.method(_interface)(stop_checker)
586
    StopChecker.__name__ = "StopChecker"
587
    
588
    del _interface
3 by Björn Påhlsson
Python based server
589
590
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
591
def peer_certificate(session):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
592
    "Return the peer's OpenPGP certificate as a bytestring"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
593
    # If not an OpenPGP certificate...
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
594
    if (gnutls.library.functions
595
        .gnutls_certificate_type_get(session._c_object)
596
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
597
        # ...do the normal thing
598
        return session.peer_certificate
599
    list_size = ctypes.c_uint()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
600
    cert_list = (gnutls.library.functions
601
                 .gnutls_certificate_get_peers
602
                 (session._c_object, ctypes.byref(list_size)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
603
    if list_size.value == 0:
604
        return None
605
    cert = cert_list[0]
606
    return ctypes.string_at(cert.data, cert.size)
607
608
609
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
610
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
611
    # New GnuTLS "datum" with the OpenPGP public key
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
612
    datum = (gnutls.library.types
613
             .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
614
                                         ctypes.POINTER
615
                                         (ctypes.c_ubyte)),
616
                             ctypes.c_uint(len(openpgp))))
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
617
    # New empty GnuTLS certificate
618
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
619
    (gnutls.library.functions
620
     .gnutls_openpgp_crt_init(ctypes.byref(crt)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
621
    # Import the OpenPGP public key into the certificate
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
622
    (gnutls.library.functions
623
     .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
624
                                gnutls.library.constants
625
                                .GNUTLS_OPENPGP_FMT_RAW))
24.1.62 by Björn Påhlsson
merge + small bugfix
626
    # Verify the self signature in the key
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
627
    crtverify = ctypes.c_uint()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
628
    (gnutls.library.functions
629
     .gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
99 by Teddy Hogeborn
* mandos (fingerprint): Bug fix: Check crtverify.value, not crtverify.
630
    if crtverify.value != 0:
24.1.62 by Björn Påhlsson
merge + small bugfix
631
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
632
        raise gnutls.errors.CertificateSecurityError("Verify failed")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
633
    # New buffer for the fingerprint
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
634
    buf = ctypes.create_string_buffer(20)
635
    buf_len = ctypes.c_size_t()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
636
    # Get the fingerprint from the certificate into the buffer
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
637
    (gnutls.library.functions
638
     .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
639
                                         ctypes.byref(buf_len)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
640
    # Deinit the certificate
641
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
642
    # Convert the buffer to a Python bytestring
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
643
    fpr = ctypes.string_at(buf, buf_len.value)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
644
    # Convert the bytestring to hexadecimal notation
645
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
646
    return hex_fpr
647
648
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
649
class TCP_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
650
    """A TCP request handler class.
651
    Instantiated by IPv6_TCPServer for each request to handle it.
652
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
653
    
3 by Björn Påhlsson
Python based server
654
    def handle(self):
44 by Teddy Hogeborn
* ca.pem: Removed.
655
        logger.info(u"TCP connection from: %s",
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
656
                    unicode(self.client_address))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
657
        session = (gnutls.connection
658
                   .ClientSession(self.request,
659
                                  gnutls.connection
660
                                  .X509Credentials()))
41 by Teddy Hogeborn
Merge.
661
        
24.1.12 by Björn Påhlsson
merge +
662
        line = self.request.makefile().readline()
663
        logger.debug(u"Protocol version: %r", line)
24.1.11 by Björn Påhlsson
Added support for protocol version handling
664
        try:
665
            if int(line.strip().split()[0]) > 1:
666
                raise RuntimeError
667
        except (ValueError, IndexError, RuntimeError), error:
668
            logger.error(u"Unknown protocol version: %s", error)
669
            return
670
        
28 by Teddy Hogeborn
* server.conf: New file.
671
        # Note: gnutls.connection.X509Credentials is really a generic
672
        # GnuTLS certificate credentials object so long as no X.509
673
        # keys are added to it.  Therefore, we can use it here despite
674
        # using OpenPGP certificates.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
675
        
676
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
677
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
678
        #                "+DHE-DSS"))
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
679
        # Use a fallback default, since this MUST be set.
680
        priority = self.server.settings.get("priority", "NORMAL")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
681
        (gnutls.library.functions
682
         .gnutls_priority_set_direct(session._c_object,
683
                                     priority, None))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
684
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
685
        try:
686
            session.handshake()
687
        except gnutls.errors.GNUTLSError, error:
44 by Teddy Hogeborn
* ca.pem: Removed.
688
            logger.warning(u"Handshake failed: %s", error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
689
            # Do not run session.bye() here: the session is not
690
            # established.  Just abandon the request.
691
            return
3 by Björn Påhlsson
Python based server
692
        try:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
693
            fpr = fingerprint(peer_certificate(session))
694
        except (TypeError, gnutls.errors.GNUTLSError), error:
44 by Teddy Hogeborn
* ca.pem: Removed.
695
            logger.warning(u"Bad certificate: %s", error)
3 by Björn Påhlsson
Python based server
696
            session.bye()
697
            return
13 by Björn Påhlsson
Added following support:
698
        logger.debug(u"Fingerprint: %s", fpr)
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
699
        for c in self.server.clients:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
700
            if c.fingerprint == fpr:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
701
                client = c
702
                break
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
703
        else:
44 by Teddy Hogeborn
* ca.pem: Removed.
704
            logger.warning(u"Client not found for fingerprint: %s",
705
                           fpr)
28 by Teddy Hogeborn
* server.conf: New file.
706
            session.bye()
707
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
708
        # Have to check if client.still_valid(), since it is possible
709
        # that the client timed out while establishing the GnuTLS
710
        # session.
28 by Teddy Hogeborn
* server.conf: New file.
711
        if not client.still_valid():
44 by Teddy Hogeborn
* ca.pem: Removed.
712
            logger.warning(u"Client %(name)s is invalid",
713
                           vars(client))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
714
            session.bye()
715
            return
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
716
        ## This won't work here, since we're in a fork.
717
        # client.bump_timeout()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
718
        sent_size = 0
719
        while sent_size < len(client.secret):
720
            sent = session.send(client.secret[sent_size:])
13 by Björn Påhlsson
Added following support:
721
            logger.debug(u"Sent: %d, remaining: %d",
722
                         sent, len(client.secret)
723
                         - (sent_size + sent))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
724
            sent_size += sent
3 by Björn Påhlsson
Python based server
725
        session.bye()
726
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
727
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
728
class IPv6_TCPServer(SocketServer.ForkingMixIn,
729
                     SocketServer.TCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
730
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
731
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
732
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
733
        clients:        Set() of Client objects
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
734
        enabled:        Boolean; whether this server is activated yet
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
735
    """
736
    address_family = socket.AF_INET6
737
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
738
        if "settings" in kwargs:
739
            self.settings = kwargs["settings"]
740
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
741
        if "clients" in kwargs:
742
            self.clients = kwargs["clients"]
743
            del kwargs["clients"]
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
744
        self.enabled = False
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
745
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
746
    def server_bind(self):
747
        """This overrides the normal server_bind() function
748
        to bind to an interface if one was specified, and also NOT to
749
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
750
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
751
            # 25 is from /usr/include/asm-i486/socket.h
752
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
753
            try:
754
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
755
                                       SO_BINDTODEVICE,
756
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
757
            except socket.error, error:
758
                if error[0] == errno.EPERM:
44 by Teddy Hogeborn
* ca.pem: Removed.
759
                    logger.error(u"No permission to"
760
                                 u" bind to interface %s",
761
                                 self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
762
                else:
763
                    raise error
764
        # Only bind(2) the socket if we really need to.
765
        if self.server_address[0] or self.server_address[1]:
766
            if not self.server_address[0]:
767
                in6addr_any = "::"
768
                self.server_address = (in6addr_any,
769
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
770
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
771
                self.server_address = (self.server_address[0],
772
                                       0)
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
773
#                 if self.settings["interface"]:
774
#                     self.server_address = (self.server_address[0],
775
#                                            0, # port
776
#                                            0, # flowinfo
777
#                                            if_nametoindex
778
#                                            (self.settings
779
#                                             ["interface"]))
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
780
            return super(IPv6_TCPServer, self).server_bind()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
781
    def server_activate(self):
782
        if self.enabled:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
783
            return super(IPv6_TCPServer, self).server_activate()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
784
    def enable(self):
785
        self.enabled = True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
786
3 by Björn Påhlsson
Python based server
787
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
788
def string_to_delta(interval):
789
    """Parse a string and return a datetime.timedelta
790
791
    >>> string_to_delta('7d')
792
    datetime.timedelta(7)
793
    >>> string_to_delta('60s')
794
    datetime.timedelta(0, 60)
795
    >>> string_to_delta('60m')
796
    datetime.timedelta(0, 3600)
797
    >>> string_to_delta('24h')
798
    datetime.timedelta(1)
799
    >>> string_to_delta(u'1w')
800
    datetime.timedelta(7)
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
801
    >>> string_to_delta('5m 30s')
802
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
803
    """
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
804
    timevalue = datetime.timedelta(0)
805
    for s in interval.split():
806
        try:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
807
            suffix = unicode(s[-1])
808
            value = int(s[:-1])
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
809
            if suffix == u"d":
810
                delta = datetime.timedelta(value)
811
            elif suffix == u"s":
812
                delta = datetime.timedelta(0, value)
813
            elif suffix == u"m":
814
                delta = datetime.timedelta(0, 0, 0, 0, value)
815
            elif suffix == u"h":
816
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
817
            elif suffix == u"w":
818
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
819
            else:
820
                raise ValueError
821
        except (ValueError, IndexError):
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
822
            raise ValueError
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
823
        timevalue += delta
824
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
825
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
826
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
827
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
828
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
829
    if state == avahi.SERVER_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
830
        logger.error(u"Zeroconf server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
831
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
832
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
833
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
834
835
836
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
837
    """Derived from the Avahi example code"""
109 by Teddy Hogeborn
* .bzrignore: New.
838
    logger.debug(u"Avahi state change: %i", state)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
839
    
840
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
109 by Teddy Hogeborn
* .bzrignore: New.
841
        logger.debug(u"Zeroconf service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
842
    elif state == avahi.ENTRY_GROUP_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
843
        logger.warning(u"Zeroconf service name collision.")
28 by Teddy Hogeborn
* server.conf: New file.
844
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
845
    elif state == avahi.ENTRY_GROUP_FAILURE:
109 by Teddy Hogeborn
* .bzrignore: New.
846
        logger.critical(u"Avahi: Error in group state changed %s",
28 by Teddy Hogeborn
* server.conf: New file.
847
                        unicode(error))
848
        raise AvahiGroupError("State changed: %s", str(error))
849
24.1.13 by Björn Påhlsson
mandosclient
850
def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
851
    """Call the C function if_nametoindex(), or equivalent"""
24.1.13 by Björn Påhlsson
mandosclient
852
    global if_nametoindex
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
853
    try:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
854
        if_nametoindex = (ctypes.cdll.LoadLibrary
855
                          (ctypes.util.find_library("c"))
856
                          .if_nametoindex)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
857
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
858
        if "struct" not in sys.modules:
859
            import struct
860
        if "fcntl" not in sys.modules:
861
            import fcntl
24.1.13 by Björn Påhlsson
mandosclient
862
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
863
            "Get an interface index the hard way, i.e. using fcntl()"
864
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
865
            with closing(socket.socket()) as s:
866
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
867
                                    struct.pack("16s16x", interface))
28 by Teddy Hogeborn
* server.conf: New file.
868
            interface_index = struct.unpack("I", ifreq[16:20])[0]
869
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
870
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
871
872
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
873
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
874
    """See daemon(3).  Standard BSD Unix function.
875
    This should really exist as os.daemon, but it doesn't (yet)."""
876
    if os.fork():
877
        sys.exit()
878
    os.setsid()
879
    if not nochdir:
880
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
881
    if os.fork():
882
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
883
    if not noclose:
884
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
885
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
886
        if not stat.S_ISCHR(os.fstat(null).st_mode):
887
            raise OSError(errno.ENODEV,
888
                          "/dev/null not a character device")
889
        os.dup2(null, sys.stdin.fileno())
890
        os.dup2(null, sys.stdout.fileno())
891
        os.dup2(null, sys.stderr.fileno())
892
        if null > 2:
893
            os.close(null)
894
895
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
896
def main():
59 by Teddy Hogeborn
* mandos (main): Use program name in --version output.
897
    parser = OptionParser(version = "%%prog %s" % version)
3 by Björn Påhlsson
Python based server
898
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
899
                      metavar="IF", help="Bind to interface IF")
900
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
901
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
902
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
903
                      help="Port number to receive requests on")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
904
    parser.add_option("--check", action="store_true", default=False,
905
                      help="Run self-test")
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
906
    parser.add_option("--debug", action="store_true",
28 by Teddy Hogeborn
* server.conf: New file.
907
                      help="Debug mode; run in foreground and log to"
908
                      " terminal")
909
    parser.add_option("--priority", type="string", help="GnuTLS"
910
                      " priority string (see GnuTLS documentation)")
911
    parser.add_option("--servicename", type="string", metavar="NAME",
912
                      help="Zeroconf service name")
913
    parser.add_option("--configdir", type="string",
914
                      default="/etc/mandos", metavar="DIR",
915
                      help="Directory to search for configuration"
916
                      " files")
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
917
    options = parser.parse_args()[0]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
918
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
919
    if options.check:
920
        import doctest
921
        doctest.testmod()
922
        sys.exit()
3 by Björn Påhlsson
Python based server
923
    
28 by Teddy Hogeborn
* server.conf: New file.
924
    # Default values for config file for server-global settings
925
    server_defaults = { "interface": "",
926
                        "address": "",
927
                        "port": "",
928
                        "debug": "False",
929
                        "priority":
930
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
931
                        "servicename": "Mandos",
932
                        }
933
    
934
    # Parse config file for server-global settings
935
    server_config = ConfigParser.SafeConfigParser(server_defaults)
936
    del server_defaults
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
938
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
939
    server_settings = server_config.defaults()
28 by Teddy Hogeborn
* server.conf: New file.
940
    # Use getboolean on the boolean config option
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
941
    server_settings["debug"] = (server_config.getboolean
942
                                ("DEFAULT", "debug"))
28 by Teddy Hogeborn
* server.conf: New file.
943
    del server_config
944
    
945
    # Override the settings from the config file with command line
946
    # options, if set.
947
    for option in ("interface", "address", "port", "debug",
948
                   "priority", "servicename", "configdir"):
949
        value = getattr(options, option)
950
        if value is not None:
951
            server_settings[option] = value
952
    del options
953
    # Now we have our good server settings in "server_settings"
954
    
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
955
    debug = server_settings["debug"]
956
    
957
    if not debug:
958
        syslogger.setLevel(logging.WARNING)
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
959
        console.setLevel(logging.WARNING)
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
960
    
961
    if server_settings["servicename"] != "Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
962
        syslogger.setFormatter(logging.Formatter
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
963
                               ('Mandos (%s): %%(levelname)s:'
964
                                ' %%(message)s'
965
                                % server_settings["servicename"]))
966
    
28 by Teddy Hogeborn
* server.conf: New file.
967
    # Parse config file with clients
968
    client_defaults = { "timeout": "1h",
969
                        "interval": "5m",
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
970
                        "checker": "fping -q -- %(host)s",
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
971
                        "host": "",
28 by Teddy Hogeborn
* server.conf: New file.
972
                        }
973
    client_config = ConfigParser.SafeConfigParser(client_defaults)
974
    client_config.read(os.path.join(server_settings["configdir"],
975
                                    "clients.conf"))
976
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
977
    clients = Set()
978
    tcp_server = IPv6_TCPServer((server_settings["address"],
979
                                 server_settings["port"]),
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
980
                                TCP_handler,
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
981
                                settings=server_settings,
982
                                clients=clients)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
983
    pidfilename = "/var/run/mandos.pid"
984
    try:
985
        pidfile = open(pidfilename, "w")
986
    except IOError, error:
987
        logger.error("Could not open file %r", pidfilename)
988
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
989
    try:
990
        uid = pwd.getpwnam("_mandos").pw_uid
991
    except KeyError:
992
        try:
993
            uid = pwd.getpwnam("mandos").pw_uid
994
        except KeyError:
995
            try:
996
                uid = pwd.getpwnam("nobody").pw_uid
997
            except KeyError:
998
                uid = 65534
999
    try:
1000
        gid = pwd.getpwnam("_mandos").pw_gid
1001
    except KeyError:
1002
        try:
1003
            gid = pwd.getpwnam("mandos").pw_gid
1004
        except KeyError:
1005
            try:
1006
                gid = pwd.getpwnam("nogroup").pw_gid
1007
            except KeyError:
1008
                gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1009
    try:
1010
        os.setuid(uid)
1011
        os.setgid(gid)
1012
    except OSError, error:
1013
        if error[0] != errno.EPERM:
1014
            raise error
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1015
    
28 by Teddy Hogeborn
* server.conf: New file.
1016
    global service
1017
    service = AvahiService(name = server_settings["servicename"],
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1018
                           servicetype = "_mandos._tcp", )
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1019
    if server_settings["interface"]:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1020
        service.interface = (if_nametoindex
1021
                             (server_settings["interface"]))
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
1022
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1023
    global main_loop
1024
    global bus
1025
    global server
1026
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1027
    DBusGMainLoop(set_as_default=True )
1028
    main_loop = gobject.MainLoop()
1029
    bus = dbus.SystemBus()
109 by Teddy Hogeborn
* .bzrignore: New.
1030
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1031
                                           avahi.DBUS_PATH_SERVER),
1032
                            avahi.DBUS_INTERFACE_SERVER)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1033
    # End of Avahi example code
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1034
    bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1035
    
44 by Teddy Hogeborn
* ca.pem: Removed.
1036
    clients.update(Set(Client(name = section,
1037
                              config
1038
                              = dict(client_config.items(section)))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1039
                       for section in client_config.sections()))
51 by Teddy Hogeborn
* clients.conf: Better comments.
1040
    if not clients:
1041
        logger.critical(u"No clients defined")
1042
        sys.exit(1)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1043
    
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1044
    if debug:
1045
        # Redirect stdin so all checkers get /dev/null
1046
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1047
        os.dup2(null, sys.stdin.fileno())
1048
        if null > 2:
1049
            os.close(null)
1050
    else:
1051
        # No console logging
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
1052
        logger.removeHandler(console)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1053
        # Close all input and output, do double fork, etc.
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1054
        daemon()
51 by Teddy Hogeborn
* clients.conf: Better comments.
1055
    
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1056
    try:
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1057
        pid = os.getpid()
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1058
        pidfile.write(str(pid) + "\n")
1059
        pidfile.close()
1060
        del pidfile
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1061
    except IOError:
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1062
        logger.error(u"Could not write to file %r with PID %d",
1063
                     pidfilename, pid)
1064
    except NameError:
1065
        # "pidfile" was never created
1066
        pass
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1067
    del pidfilename
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1068
    
1069
    def cleanup():
1070
        "Cleanup function; run on exit"
1071
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1072
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1073
        if not group is None:
1074
            group.Free()
1075
            group = None
1076
        # End of Avahi example code
1077
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1078
        while clients:
1079
            client = clients.pop()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1080
            client.stop_hook = None
1081
            client.stop()
1082
    
1083
    atexit.register(cleanup)
1084
    
1085
    if not debug:
1086
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
1087
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1088
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1089
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1090
    class MandosServer(dbus.service.Object):
1091
        """A D-Bus proxy object"""
1092
        def __init__(self):
1093
            dbus.service.Object.__init__(self, bus,
1094
                                         "/Mandos")
1095
        _interface = u"org.mandos_system.Mandos"
1096
        
1097
        @dbus.service.signal(_interface, signature="oa{sv}")
1098
        def ClientAdded(self, objpath, properties):
1099
            "D-Bus signal"
1100
            pass
1101
        
1102
        @dbus.service.signal(_interface, signature="o")
1103
        def ClientRemoved(self, objpath):
1104
            "D-Bus signal"
1105
            pass
1106
        
1107
        @dbus.service.method(_interface, out_signature="ao")
1108
        def GetAllClients(self):
1109
            return dbus.Array(c.dbus_object_path for c in clients)
1110
        
1111
        @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1112
        def GetAllClientsWithProperties(self):
1113
            return dbus.Dictionary(
1114
                ((c.dbus_object_path, c.GetAllProperties())
1115
                 for c in clients),
1116
                signature="oa{sv}")
1117
        
1118
        @dbus.service.method(_interface, in_signature="o")
1119
        def RemoveClient(self, object_path):
1120
            for c in clients:
1121
                if c.dbus_object_path == object_path:
1122
                    c.stop()
1123
                    clients.remove(c)
1124
                    return
1125
            raise KeyError
1126
        
1127
        del _interface
1128
    
1129
    mandos_server = MandosServer()
1130
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1131
    for client in clients:
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1132
        # Emit D-Bus signal
1133
        mandos_server.ClientAdded(client.dbus_object_path,
1134
                                  client.GetAllProperties())
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1135
        client.start()
1136
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1137
    tcp_server.enable()
1138
    tcp_server.server_activate()
1139
    
28 by Teddy Hogeborn
* server.conf: New file.
1140
    # Find out what port we got
1141
    service.port = tcp_server.socket.getsockname()[1]
44 by Teddy Hogeborn
* ca.pem: Removed.
1142
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
1143
                u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
1144
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1145
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
1146
    
1147
    try:
1148
        # From the Avahi example code
1149
        server.connect_to_signal("StateChanged", server_state_changed)
1150
        try:
1151
            server_state_changed(server.GetState())
1152
        except dbus.exceptions.DBusException, error:
1153
            logger.critical(u"DBusException: %s", error)
1154
            sys.exit(1)
1155
        # End of Avahi example code
1156
        
1157
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
1158
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1159
                             (tcp_server.handle_request
1160
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
1161
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
1162
        logger.debug(u"Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1163
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
1164
    except AvahiError, error:
1165
        logger.critical(u"AvahiError: %s" + unicode(error))
1166
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1167
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1168
        if debug:
1169
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1170
1171
if __name__ == '__main__':
1172
    main()