/mandos/trunk

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