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