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