/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
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
598
    list_size = ctypes.c_uint(1)
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)))
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
602
    if not bool(cert_list) and list_size.value != 0:
603
        raise gnutls.errors.GNUTLSError("error getting peer"
604
                                        " certificate")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
605
    if list_size.value == 0:
606
        return None
607
    cert = cert_list[0]
608
    return ctypes.string_at(cert.data, cert.size)
609
610
611
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
612
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
613
    # 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
614
    datum = (gnutls.library.types
615
             .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
616
                                         ctypes.POINTER
617
                                         (ctypes.c_ubyte)),
618
                             ctypes.c_uint(len(openpgp))))
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
619
    # New empty GnuTLS certificate
620
    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
621
    (gnutls.library.functions
622
     .gnutls_openpgp_crt_init(ctypes.byref(crt)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
623
    # 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
624
    (gnutls.library.functions
625
     .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
626
                                gnutls.library.constants
627
                                .GNUTLS_OPENPGP_FMT_RAW))
24.1.62 by Björn Påhlsson
merge + small bugfix
628
    # Verify the self signature in the key
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
629
    crtverify = ctypes.c_uint()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
630
    (gnutls.library.functions
631
     .gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
99 by Teddy Hogeborn
* mandos (fingerprint): Bug fix: Check crtverify.value, not crtverify.
632
    if crtverify.value != 0:
24.1.62 by Björn Påhlsson
merge + small bugfix
633
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
634
        raise gnutls.errors.CertificateSecurityError("Verify failed")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
635
    # New buffer for the fingerprint
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
636
    buf = ctypes.create_string_buffer(20)
637
    buf_len = ctypes.c_size_t()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
638
    # 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
639
    (gnutls.library.functions
640
     .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
641
                                         ctypes.byref(buf_len)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
642
    # Deinit the certificate
643
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
644
    # Convert the buffer to a Python bytestring
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
645
    fpr = ctypes.string_at(buf, buf_len.value)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
646
    # Convert the bytestring to hexadecimal notation
647
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
648
    return hex_fpr
649
650
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
651
class TCP_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
652
    """A TCP request handler class.
653
    Instantiated by IPv6_TCPServer for each request to handle it.
654
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
655
    
3 by Björn Påhlsson
Python based server
656
    def handle(self):
44 by Teddy Hogeborn
* ca.pem: Removed.
657
        logger.info(u"TCP connection from: %s",
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
658
                    unicode(self.client_address))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
659
        session = (gnutls.connection
660
                   .ClientSession(self.request,
661
                                  gnutls.connection
662
                                  .X509Credentials()))
41 by Teddy Hogeborn
Merge.
663
        
24.1.12 by Björn Påhlsson
merge +
664
        line = self.request.makefile().readline()
665
        logger.debug(u"Protocol version: %r", line)
24.1.11 by Björn Påhlsson
Added support for protocol version handling
666
        try:
667
            if int(line.strip().split()[0]) > 1:
668
                raise RuntimeError
669
        except (ValueError, IndexError, RuntimeError), error:
670
            logger.error(u"Unknown protocol version: %s", error)
671
            return
672
        
28 by Teddy Hogeborn
* server.conf: New file.
673
        # Note: gnutls.connection.X509Credentials is really a generic
674
        # GnuTLS certificate credentials object so long as no X.509
675
        # keys are added to it.  Therefore, we can use it here despite
676
        # using OpenPGP certificates.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
677
        
678
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
679
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
680
        #                "+DHE-DSS"))
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
681
        # Use a fallback default, since this MUST be set.
682
        priority = self.server.settings.get("priority", "NORMAL")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
683
        (gnutls.library.functions
684
         .gnutls_priority_set_direct(session._c_object,
685
                                     priority, None))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
686
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
687
        try:
688
            session.handshake()
689
        except gnutls.errors.GNUTLSError, error:
44 by Teddy Hogeborn
* ca.pem: Removed.
690
            logger.warning(u"Handshake failed: %s", error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
691
            # Do not run session.bye() here: the session is not
692
            # established.  Just abandon the request.
693
            return
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
694
        logger.debug(u"Handshake succeeded")
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)
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
702
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
703
        for c in self.server.clients:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
704
            if c.fingerprint == fpr:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
705
                client = c
706
                break
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
707
        else:
44 by Teddy Hogeborn
* ca.pem: Removed.
708
            logger.warning(u"Client not found for fingerprint: %s",
709
                           fpr)
28 by Teddy Hogeborn
* server.conf: New file.
710
            session.bye()
711
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
712
        # Have to check if client.still_valid(), since it is possible
713
        # that the client timed out while establishing the GnuTLS
714
        # session.
28 by Teddy Hogeborn
* server.conf: New file.
715
        if not client.still_valid():
44 by Teddy Hogeborn
* ca.pem: Removed.
716
            logger.warning(u"Client %(name)s is invalid",
717
                           vars(client))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
718
            session.bye()
719
            return
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
720
        ## 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
721
        # client.checked_ok()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
722
        sent_size = 0
723
        while sent_size < len(client.secret):
724
            sent = session.send(client.secret[sent_size:])
13 by Björn Påhlsson
Added following support:
725
            logger.debug(u"Sent: %d, remaining: %d",
726
                         sent, len(client.secret)
727
                         - (sent_size + sent))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
728
            sent_size += sent
3 by Björn Påhlsson
Python based server
729
        session.bye()
730
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
731
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
732
class IPv6_TCPServer(SocketServer.ForkingMixIn,
733
                     SocketServer.TCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
734
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
735
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
736
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
737
        clients:        Set() of Client objects
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
738
        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
739
    """
740
    address_family = socket.AF_INET6
741
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
742
        if "settings" in kwargs:
743
            self.settings = kwargs["settings"]
744
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
745
        if "clients" in kwargs:
746
            self.clients = kwargs["clients"]
747
            del kwargs["clients"]
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
748
        self.enabled = False
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
749
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
750
    def server_bind(self):
751
        """This overrides the normal server_bind() function
752
        to bind to an interface if one was specified, and also NOT to
753
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
754
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
755
            # 25 is from /usr/include/asm-i486/socket.h
756
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
757
            try:
758
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
759
                                       SO_BINDTODEVICE,
760
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
761
            except socket.error, error:
762
                if error[0] == errno.EPERM:
44 by Teddy Hogeborn
* ca.pem: Removed.
763
                    logger.error(u"No permission to"
764
                                 u" bind to interface %s",
765
                                 self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
766
                else:
767
                    raise error
768
        # Only bind(2) the socket if we really need to.
769
        if self.server_address[0] or self.server_address[1]:
770
            if not self.server_address[0]:
771
                in6addr_any = "::"
772
                self.server_address = (in6addr_any,
773
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
774
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
775
                self.server_address = (self.server_address[0],
776
                                       0)
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
777
#                 if self.settings["interface"]:
778
#                     self.server_address = (self.server_address[0],
779
#                                            0, # port
780
#                                            0, # flowinfo
781
#                                            if_nametoindex
782
#                                            (self.settings
783
#                                             ["interface"]))
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
784
            return super(IPv6_TCPServer, self).server_bind()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
785
    def server_activate(self):
786
        if self.enabled:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
787
            return super(IPv6_TCPServer, self).server_activate()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
788
    def enable(self):
789
        self.enabled = True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
790
3 by Björn Påhlsson
Python based server
791
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
792
def string_to_delta(interval):
793
    """Parse a string and return a datetime.timedelta
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
794
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
795
    >>> string_to_delta('7d')
796
    datetime.timedelta(7)
797
    >>> string_to_delta('60s')
798
    datetime.timedelta(0, 60)
799
    >>> string_to_delta('60m')
800
    datetime.timedelta(0, 3600)
801
    >>> string_to_delta('24h')
802
    datetime.timedelta(1)
803
    >>> string_to_delta(u'1w')
804
    datetime.timedelta(7)
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
805
    >>> string_to_delta('5m 30s')
806
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
807
    """
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
808
    timevalue = datetime.timedelta(0)
809
    for s in interval.split():
810
        try:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
811
            suffix = unicode(s[-1])
812
            value = int(s[:-1])
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
813
            if suffix == u"d":
814
                delta = datetime.timedelta(value)
815
            elif suffix == u"s":
816
                delta = datetime.timedelta(0, value)
817
            elif suffix == u"m":
818
                delta = datetime.timedelta(0, 0, 0, 0, value)
819
            elif suffix == u"h":
820
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
821
            elif suffix == u"w":
822
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
823
            else:
824
                raise ValueError
825
        except (ValueError, IndexError):
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
826
            raise ValueError
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
827
        timevalue += delta
828
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
829
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
830
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
831
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
832
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
833
    if state == avahi.SERVER_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
834
        logger.error(u"Zeroconf server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
835
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
836
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
837
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
838
839
840
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
841
    """Derived from the Avahi example code"""
109 by Teddy Hogeborn
* .bzrignore: New.
842
    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
843
    
844
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
109 by Teddy Hogeborn
* .bzrignore: New.
845
        logger.debug(u"Zeroconf service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
846
    elif state == avahi.ENTRY_GROUP_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
847
        logger.warning(u"Zeroconf service name collision.")
28 by Teddy Hogeborn
* server.conf: New file.
848
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
849
    elif state == avahi.ENTRY_GROUP_FAILURE:
109 by Teddy Hogeborn
* .bzrignore: New.
850
        logger.critical(u"Avahi: Error in group state changed %s",
28 by Teddy Hogeborn
* server.conf: New file.
851
                        unicode(error))
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
852
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
28 by Teddy Hogeborn
* server.conf: New file.
853
24.1.13 by Björn Påhlsson
mandosclient
854
def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
855
    """Call the C function if_nametoindex(), or equivalent"""
24.1.13 by Björn Påhlsson
mandosclient
856
    global if_nametoindex
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
857
    try:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
858
        if_nametoindex = (ctypes.cdll.LoadLibrary
859
                          (ctypes.util.find_library("c"))
860
                          .if_nametoindex)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
861
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
862
        if "struct" not in sys.modules:
863
            import struct
864
        if "fcntl" not in sys.modules:
865
            import fcntl
24.1.13 by Björn Påhlsson
mandosclient
866
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
867
            "Get an interface index the hard way, i.e. using fcntl()"
868
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
869
            with closing(socket.socket()) as s:
870
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
871
                                    struct.pack("16s16x", interface))
28 by Teddy Hogeborn
* server.conf: New file.
872
            interface_index = struct.unpack("I", ifreq[16:20])[0]
873
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
874
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
875
876
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
877
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
878
    """See daemon(3).  Standard BSD Unix function.
879
    This should really exist as os.daemon, but it doesn't (yet)."""
880
    if os.fork():
881
        sys.exit()
882
    os.setsid()
883
    if not nochdir:
884
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
885
    if os.fork():
886
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
887
    if not noclose:
888
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
889
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
890
        if not stat.S_ISCHR(os.fstat(null).st_mode):
891
            raise OSError(errno.ENODEV,
892
                          "/dev/null not a character device")
893
        os.dup2(null, sys.stdin.fileno())
894
        os.dup2(null, sys.stdout.fileno())
895
        os.dup2(null, sys.stderr.fileno())
896
        if null > 2:
897
            os.close(null)
898
899
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
900
def main():
237.2.1 by Teddy Hogeborn
Merge from trunk, but disable the unfinished D-Bus feature:
901
    parser = optparse.OptionParser(version = "%%prog %s" % version)
3 by Björn Påhlsson
Python based server
902
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
903
                      metavar="IF", help="Bind to interface IF")
904
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
905
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
906
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
907
                      help="Port number to receive requests on")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
908
    parser.add_option("--check", action="store_true",
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
909
                      help="Run self-test")
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
910
    parser.add_option("--debug", action="store_true",
28 by Teddy Hogeborn
* server.conf: New file.
911
                      help="Debug mode; run in foreground and log to"
912
                      " terminal")
913
    parser.add_option("--priority", type="string", help="GnuTLS"
914
                      " priority string (see GnuTLS documentation)")
915
    parser.add_option("--servicename", type="string", metavar="NAME",
916
                      help="Zeroconf service name")
917
    parser.add_option("--configdir", type="string",
918
                      default="/etc/mandos", metavar="DIR",
919
                      help="Directory to search for configuration"
920
                      " files")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
921
    parser.add_option("--no-dbus", action="store_false",
922
                      dest="use_dbus",
271 by Teddy Hogeborn
Merge from release branch.
923
                      help="Do not provide D-Bus system bus"
924
                      " interface")
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
925
    options = parser.parse_args()[0]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
926
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
927
    if options.check:
928
        import doctest
929
        doctest.testmod()
930
        sys.exit()
3 by Björn Påhlsson
Python based server
931
    
28 by Teddy Hogeborn
* server.conf: New file.
932
    # Default values for config file for server-global settings
933
    server_defaults = { "interface": "",
934
                        "address": "",
935
                        "port": "",
936
                        "debug": "False",
937
                        "priority":
938
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
939
                        "servicename": "Mandos",
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
940
                        "use_dbus": "True",
28 by Teddy Hogeborn
* server.conf: New file.
941
                        }
942
    
943
    # Parse config file for server-global settings
944
    server_config = ConfigParser.SafeConfigParser(server_defaults)
945
    del server_defaults
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
946
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
947
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
948
    server_settings = server_config.defaults()
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
949
    # Use the appropriate methods on the non-string config options
950
    server_settings["debug"] = server_config.getboolean("DEFAULT",
951
                                                        "debug")
952
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
953
                                                           "use_dbus")
954
    if server_settings["port"]:
955
        server_settings["port"] = server_config.getint("DEFAULT",
956
                                                       "port")
28 by Teddy Hogeborn
* server.conf: New file.
957
    del server_config
958
    
959
    # Override the settings from the config file with command line
960
    # options, if set.
961
    for option in ("interface", "address", "port", "debug",
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
962
                   "priority", "servicename", "configdir",
963
                   "use_dbus"):
28 by Teddy Hogeborn
* server.conf: New file.
964
        value = getattr(options, option)
965
        if value is not None:
966
            server_settings[option] = value
967
    del options
968
    # Now we have our good server settings in "server_settings"
969
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
970
    # For convenience
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
971
    debug = server_settings["debug"]
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
972
    use_dbus = server_settings["use_dbus"]
289 by Björn Påhlsson
* mandos (main): Catch SIGSEGV and raise RuntimeError.
973
974
    def sigsegvhandler(signum, frame):
975
        raise RuntimeError('Segmentation fault')
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
976
    
977
    if not debug:
978
        syslogger.setLevel(logging.WARNING)
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
979
        console.setLevel(logging.WARNING)
289 by Björn Påhlsson
* mandos (main): Catch SIGSEGV and raise RuntimeError.
980
    else:
981
        signal.signal(signal.SIGSEGV, sigsegvhandler)
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
982
    
983
    if server_settings["servicename"] != "Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
984
        syslogger.setFormatter(logging.Formatter
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
985
                               ('Mandos (%s): %%(levelname)s:'
986
                                ' %%(message)s'
987
                                % server_settings["servicename"]))
988
    
28 by Teddy Hogeborn
* server.conf: New file.
989
    # Parse config file with clients
990
    client_defaults = { "timeout": "1h",
991
                        "interval": "5m",
24.1.121 by Björn Påhlsson
mandos-ctl: Added support for all client calls
992
                        "checker": "fping -q -- %%(host)s",
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
993
                        "host": "",
28 by Teddy Hogeborn
* server.conf: New file.
994
                        }
995
    client_config = ConfigParser.SafeConfigParser(client_defaults)
996
    client_config.read(os.path.join(server_settings["configdir"],
997
                                    "clients.conf"))
998
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
999
    clients = Set()
1000
    tcp_server = IPv6_TCPServer((server_settings["address"],
1001
                                 server_settings["port"]),
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1002
                                TCP_handler,
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1003
                                settings=server_settings,
1004
                                clients=clients)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1005
    pidfilename = "/var/run/mandos.pid"
1006
    try:
1007
        pidfile = open(pidfilename, "w")
1008
    except IOError, error:
1009
        logger.error("Could not open file %r", pidfilename)
1010
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1011
    try:
1012
        uid = pwd.getpwnam("_mandos").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1013
        gid = pwd.getpwnam("_mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1014
    except KeyError:
1015
        try:
1016
            uid = pwd.getpwnam("mandos").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1017
            gid = pwd.getpwnam("mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1018
        except KeyError:
1019
            try:
1020
                uid = pwd.getpwnam("nobody").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1021
                gid = pwd.getpwnam("nogroup").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1022
            except KeyError:
1023
                uid = 65534
1024
                gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1025
    try:
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1026
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1027
        os.setuid(uid)
1028
    except OSError, error:
1029
        if error[0] != errno.EPERM:
1030
            raise error
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1031
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1032
    # Enable all possible GnuTLS debugging
1033
    if debug:
1034
        # "Use a log level over 10 to enable all debugging options."
1035
        # - GnuTLS manual
1036
        gnutls.library.functions.gnutls_global_set_log_level(11)
1037
        
1038
        @gnutls.library.types.gnutls_log_func
1039
        def debug_gnutls(level, string):
1040
            logger.debug("GnuTLS: %s", string[:-1])
1041
        
1042
        (gnutls.library.functions
1043
         .gnutls_global_set_log_function(debug_gnutls))
1044
    
28 by Teddy Hogeborn
* server.conf: New file.
1045
    global service
1046
    service = AvahiService(name = server_settings["servicename"],
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1047
                           servicetype = "_mandos._tcp", )
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1048
    if server_settings["interface"]:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1049
        service.interface = (if_nametoindex
1050
                             (server_settings["interface"]))
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
1051
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1052
    global main_loop
1053
    global bus
1054
    global server
1055
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1056
    DBusGMainLoop(set_as_default=True )
1057
    main_loop = gobject.MainLoop()
1058
    bus = dbus.SystemBus()
109 by Teddy Hogeborn
* .bzrignore: New.
1059
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1060
                                           avahi.DBUS_PATH_SERVER),
1061
                            avahi.DBUS_INTERFACE_SERVER)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1062
    # End of Avahi example code
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1063
    if use_dbus:
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1064
        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
1065
    
44 by Teddy Hogeborn
* ca.pem: Removed.
1066
    clients.update(Set(Client(name = section,
1067
                              config
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1068
                              = dict(client_config.items(section)),
1069
                              use_dbus = use_dbus)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1070
                       for section in client_config.sections()))
51 by Teddy Hogeborn
* clients.conf: Better comments.
1071
    if not clients:
244 by Teddy Hogeborn
* debian/control (Build-Depends): Bug fix: Added "docbook-xml".
1072
        logger.warning(u"No clients defined")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1073
    
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1074
    if debug:
1075
        # Redirect stdin so all checkers get /dev/null
1076
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1077
        os.dup2(null, sys.stdin.fileno())
1078
        if null > 2:
1079
            os.close(null)
1080
    else:
1081
        # No console logging
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
1082
        logger.removeHandler(console)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1083
        # Close all input and output, do double fork, etc.
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1084
        daemon()
51 by Teddy Hogeborn
* clients.conf: Better comments.
1085
    
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1086
    try:
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1087
        pid = os.getpid()
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1088
        pidfile.write(str(pid) + "\n")
1089
        pidfile.close()
1090
        del pidfile
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1091
    except IOError:
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1092
        logger.error(u"Could not write to file %r with PID %d",
1093
                     pidfilename, pid)
1094
    except NameError:
1095
        # "pidfile" was never created
1096
        pass
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1097
    del pidfilename
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1098
    
1099
    def cleanup():
1100
        "Cleanup function; run on exit"
1101
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1102
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1103
        if not group is None:
1104
            group.Free()
1105
            group = None
1106
        # End of Avahi example code
1107
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1108
        while clients:
1109
            client = clients.pop()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1110
            client.disable_hook = None
1111
            client.disable()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1112
    
1113
    atexit.register(cleanup)
1114
    
1115
    if not debug:
1116
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
1117
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1118
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1119
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1120
    if use_dbus:
1121
        class MandosServer(dbus.service.Object):
1122
            """A D-Bus proxy object"""
1123
            def __init__(self):
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1124
                dbus.service.Object.__init__(self, bus, "/")
1125
            _interface = u"se.bsnet.fukt.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1126
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1127
            @dbus.service.signal(_interface, signature="oa{sv}")
1128
            def ClientAdded(self, objpath, properties):
1129
                "D-Bus signal"
1130
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1131
            
1132
            @dbus.service.signal(_interface, signature="os")
1133
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1134
                "D-Bus signal"
1135
                pass
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
            @dbus.service.method(_interface, out_signature="ao")
1138
            def GetAllClients(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1139
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1140
                return dbus.Array(c.dbus_object_path for c in clients)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1141
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1142
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1143
            def GetAllClientsWithProperties(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1144
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1145
                return dbus.Dictionary(
1146
                    ((c.dbus_object_path, c.GetAllProperties())
1147
                     for c in clients),
1148
                    signature="oa{sv}")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1149
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1150
            @dbus.service.method(_interface, in_signature="o")
1151
            def RemoveClient(self, object_path):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1152
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1153
                for c in clients:
1154
                    if c.dbus_object_path == object_path:
1155
                        clients.remove(c)
1156
                        # Don't signal anything except ClientRemoved
1157
                        c.use_dbus = False
1158
                        c.disable()
1159
                        # Emit D-Bus signal
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1160
                        self.ClientRemoved(object_path, c.name)
1161
                        return
1162
                raise KeyError
1163
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1164
            del _interface
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1165
        
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1166
        mandos_server = MandosServer()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1167
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1168
    for client in clients:
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1169
        if use_dbus:
1170
            # Emit D-Bus signal
1171
            mandos_server.ClientAdded(client.dbus_object_path,
1172
                                      client.GetAllProperties())
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1173
        client.enable()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1174
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1175
    tcp_server.enable()
1176
    tcp_server.server_activate()
1177
    
28 by Teddy Hogeborn
* server.conf: New file.
1178
    # Find out what port we got
1179
    service.port = tcp_server.socket.getsockname()[1]
44 by Teddy Hogeborn
* ca.pem: Removed.
1180
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
1181
                u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
1182
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1183
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
1184
    
1185
    try:
1186
        # From the Avahi example code
1187
        server.connect_to_signal("StateChanged", server_state_changed)
1188
        try:
1189
            server_state_changed(server.GetState())
1190
        except dbus.exceptions.DBusException, error:
1191
            logger.critical(u"DBusException: %s", error)
1192
            sys.exit(1)
1193
        # End of Avahi example code
1194
        
1195
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
1196
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1197
                             (tcp_server.handle_request
1198
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
1199
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
1200
        logger.debug(u"Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1201
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
1202
    except AvahiError, error:
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
1203
        logger.critical(u"AvahiError: %s", error)
28 by Teddy Hogeborn
* server.conf: New file.
1204
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1205
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1206
        if debug:
1207
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1208
1209
if __name__ == '__main__':
1210
    main()