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