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