/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)
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
229
        self.use_dbus = False   # During __init__
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
230
        # Uppercase and remove spaces from fingerprint for later
231
        # comparison purposes with return value from the fingerprint()
232
        # function
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
233
        self.fingerprint = (config["fingerprint"].upper()
234
                            .replace(u" ", u""))
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
235
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
44 by Teddy Hogeborn
* ca.pem: Removed.
236
        if "secret" in config:
237
            self.secret = config["secret"].decode(u"base64")
238
        elif "secfile" in config:
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
239
            with closing(open(os.path.expanduser
240
                              (os.path.expandvars
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
241
                               (config["secfile"])))) as secfile:
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
242
                self.secret = secfile.read()
3 by Björn Påhlsson
Python based server
243
        else:
28 by Teddy Hogeborn
* server.conf: New file.
244
            raise TypeError(u"No secret or secfile for client %s"
245
                            % self.name)
51 by Teddy Hogeborn
* clients.conf: Better comments.
246
        self.host = config.get("host", "")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
247
        self.created = datetime.datetime.utcnow()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
248
        self.enabled = False
249
        self.last_enabled = None
28 by Teddy Hogeborn
* server.conf: New file.
250
        self.last_checked_ok = None
44 by Teddy Hogeborn
* ca.pem: Removed.
251
        self.timeout = string_to_delta(config["timeout"])
252
        self.interval = string_to_delta(config["interval"])
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
253
        self.disable_hook = disable_hook
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
254
        self.checker = None
255
        self.checker_initiator_tag = None
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
256
        self.disable_initiator_tag = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
257
        self.checker_callback_tag = None
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
258
        self.checker_command = config["checker"]
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
259
        self.last_connect = None
260
        # Only now, when this client is initialized, can it show up on
261
        # the D-Bus
262
        self.use_dbus = use_dbus
263
        if self.use_dbus:
264
            self.dbus_object_path = (dbus.ObjectPath
265
                                     ("/clients/"
266
                                      + self.name.replace(".", "_")))
267
            dbus.service.Object.__init__(self, bus,
268
                                         self.dbus_object_path)
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
269
    
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
270
    def enable(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
271
        """Start this client's checker and timeout hooks"""
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
272
        self.last_enabled = datetime.datetime.utcnow()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
273
        # Schedule a new checker to be started an 'interval' from now,
274
        # and every interval from then on.
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
275
        self.checker_initiator_tag = (gobject.timeout_add
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
276
                                      (self.interval_milliseconds(),
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
277
                                       self.start_checker))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
278
        # Also start a new checker *right now*.
279
        self.start_checker()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
280
        # Schedule a disable() when 'timeout' has passed
281
        self.disable_initiator_tag = (gobject.timeout_add
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
282
                                   (self.timeout_milliseconds(),
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
283
                                    self.disable))
284
        self.enabled = True
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
285
        if self.use_dbus:
286
            # Emit D-Bus signals
287
            self.PropertyChanged(dbus.String(u"enabled"),
288
                                 dbus.Boolean(True, variant_level=1))
289
            self.PropertyChanged(dbus.String(u"last_enabled"),
290
                                 (_datetime_to_dbus(self.last_enabled,
291
                                                    variant_level=1)))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
292
    
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
293
    def disable(self):
294
        """Disable this client."""
295
        if not getattr(self, "enabled", False):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
296
            return False
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
297
        logger.info(u"Disabling client %s", self.name)
298
        if getattr(self, "disable_initiator_tag", False):
299
            gobject.source_remove(self.disable_initiator_tag)
300
            self.disable_initiator_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
301
        if getattr(self, "checker_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
302
            gobject.source_remove(self.checker_initiator_tag)
303
            self.checker_initiator_tag = None
304
        self.stop_checker()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
305
        if self.disable_hook:
306
            self.disable_hook(self)
307
        self.enabled = False
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
308
        if self.use_dbus:
309
            # Emit D-Bus signal
310
            self.PropertyChanged(dbus.String(u"enabled"),
311
                                 dbus.Boolean(False, variant_level=1))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
312
        # Do not run this again if called by a gobject.timeout_add
313
        return False
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
314
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
315
    def __del__(self):
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
316
        self.disable_hook = None
317
        self.disable()
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
318
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
319
    def checker_callback(self, pid, condition, command):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
320
        """The checker has completed, so take appropriate actions."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
321
        self.checker_callback_tag = None
322
        self.checker = None
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
323
        if self.use_dbus:
324
            # Emit D-Bus signal
325
            self.PropertyChanged(dbus.String(u"checker_running"),
326
                                 dbus.Boolean(False, variant_level=1))
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
327
        if os.WIFEXITED(condition):
328
            exitstatus = os.WEXITSTATUS(condition)
329
            if exitstatus == 0:
330
                logger.info(u"Checker for %(name)s succeeded",
331
                            vars(self))
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
332
                self.checked_ok()
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
333
            else:
334
                logger.info(u"Checker for %(name)s failed",
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
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
338
                self.CheckerCompleted(dbus.Int16(exitstatus),
339
                                      dbus.Int64(condition),
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
340
                                      dbus.String(command))
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
341
        else:
13 by Björn Påhlsson
Added following support:
342
            logger.warning(u"Checker for %(name)s crashed?",
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
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
346
                self.CheckerCompleted(dbus.Int16(-1),
347
                                      dbus.Int64(condition),
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
348
                                      dbus.String(command))
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
349
    
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
350
    def checked_ok(self):
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
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
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
452
    _interface = u"se.bsnet.fukt.Mandos.Client"
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
453
    
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
454
    # CheckedOK - method
455
    CheckedOK = dbus.service.method(_interface)(checked_ok)
456
    CheckedOK.__name__ = "CheckedOK"
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
457
    
458
    # CheckerCompleted - signal
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
459
    @dbus.service.signal(_interface, signature="nxs")
460
    def CheckerCompleted(self, exitcode, waitstatus, 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.
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
716
        # client.checked_ok()
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:
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1038
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1039
    
44 by Teddy Hogeborn
* ca.pem: Removed.
1040
    clients.update(Set(Client(name = section,
1041
                              config
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1042
                              = dict(client_config.items(section)),
1043
                              use_dbus = use_dbus)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1044
                       for section in client_config.sections()))
51 by Teddy Hogeborn
* clients.conf: Better comments.
1045
    if not clients:
244 by Teddy Hogeborn
* debian/control (Build-Depends): Bug fix: Added "docbook-xml".
1046
        logger.warning(u"No clients defined")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1047
    
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1048
    if debug:
1049
        # Redirect stdin so all checkers get /dev/null
1050
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1051
        os.dup2(null, sys.stdin.fileno())
1052
        if null > 2:
1053
            os.close(null)
1054
    else:
1055
        # No console logging
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
1056
        logger.removeHandler(console)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1057
        # Close all input and output, do double fork, etc.
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1058
        daemon()
51 by Teddy Hogeborn
* clients.conf: Better comments.
1059
    
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1060
    try:
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1061
        pid = os.getpid()
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1062
        pidfile.write(str(pid) + "\n")
1063
        pidfile.close()
1064
        del pidfile
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1065
    except IOError:
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1066
        logger.error(u"Could not write to file %r with PID %d",
1067
                     pidfilename, pid)
1068
    except NameError:
1069
        # "pidfile" was never created
1070
        pass
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1071
    del pidfilename
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1072
    
1073
    def cleanup():
1074
        "Cleanup function; run on exit"
1075
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1076
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1077
        if not group is None:
1078
            group.Free()
1079
            group = None
1080
        # End of Avahi example code
1081
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1082
        while clients:
1083
            client = clients.pop()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1084
            client.disable_hook = None
1085
            client.disable()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1086
    
1087
    atexit.register(cleanup)
1088
    
1089
    if not debug:
1090
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
1091
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1092
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1093
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1094
    if use_dbus:
1095
        class MandosServer(dbus.service.Object):
1096
            """A D-Bus proxy object"""
1097
            def __init__(self):
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1098
                dbus.service.Object.__init__(self, bus, "/")
1099
            _interface = u"se.bsnet.fukt.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1100
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1101
            @dbus.service.signal(_interface, signature="oa{sv}")
1102
            def ClientAdded(self, objpath, properties):
1103
                "D-Bus signal"
1104
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1105
            
1106
            @dbus.service.signal(_interface, signature="os")
1107
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1108
                "D-Bus signal"
1109
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1110
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1111
            @dbus.service.method(_interface, out_signature="ao")
1112
            def GetAllClients(self):
1113
                return dbus.Array(c.dbus_object_path for c in clients)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1114
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1115
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1116
            def GetAllClientsWithProperties(self):
1117
                return dbus.Dictionary(
1118
                    ((c.dbus_object_path, c.GetAllProperties())
1119
                     for c in clients),
1120
                    signature="oa{sv}")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1121
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1122
            @dbus.service.method(_interface, in_signature="o")
1123
            def RemoveClient(self, object_path):
1124
                for c in clients:
1125
                    if c.dbus_object_path == object_path:
1126
                        clients.remove(c)
1127
                        # Don't signal anything except ClientRemoved
1128
                        c.use_dbus = False
1129
                        c.disable()
1130
                        # Emit D-Bus signal
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1131
                        self.ClientRemoved(object_path, c.name)
1132
                        return
1133
                raise KeyError
1134
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1135
            del _interface
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1136
        
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1137
        mandos_server = MandosServer()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1138
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1139
    for client in clients:
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1140
        if use_dbus:
1141
            # Emit D-Bus signal
1142
            mandos_server.ClientAdded(client.dbus_object_path,
1143
                                      client.GetAllProperties())
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1144
        client.enable()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1145
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1146
    tcp_server.enable()
1147
    tcp_server.server_activate()
1148
    
28 by Teddy Hogeborn
* server.conf: New file.
1149
    # Find out what port we got
1150
    service.port = tcp_server.socket.getsockname()[1]
44 by Teddy Hogeborn
* ca.pem: Removed.
1151
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
1152
                u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
1153
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1154
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
1155
    
1156
    try:
1157
        # From the Avahi example code
1158
        server.connect_to_signal("StateChanged", server_state_changed)
1159
        try:
1160
            server_state_changed(server.GetState())
1161
        except dbus.exceptions.DBusException, error:
1162
            logger.critical(u"DBusException: %s", error)
1163
            sys.exit(1)
1164
        # End of Avahi example code
1165
        
1166
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
1167
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1168
                             (tcp_server.handle_request
1169
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
1170
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
1171
        logger.debug(u"Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1172
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
1173
    except AvahiError, error:
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
1174
        logger.critical(u"AvahiError: %s", error)
28 by Teddy Hogeborn
* server.conf: New file.
1175
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1176
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1177
        if debug:
1178
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1179
1180
if __name__ == '__main__':
1181
    main()