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