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