/mandos/trunk

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