/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
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
676
def peer_certificate(session):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
677
    "Return the peer's OpenPGP certificate as a bytestring"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
678
    # If not an OpenPGP certificate...
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
679
    if (gnutls.library.functions
680
        .gnutls_certificate_type_get(session._c_object)
681
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
682
        # ...do the normal thing
683
        return session.peer_certificate
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
684
    list_size = ctypes.c_uint(1)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
685
    cert_list = (gnutls.library.functions
686
                 .gnutls_certificate_get_peers
687
                 (session._c_object, ctypes.byref(list_size)))
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
688
    if not bool(cert_list) and list_size.value != 0:
689
        raise gnutls.errors.GNUTLSError("error getting peer"
690
                                        " certificate")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
691
    if list_size.value == 0:
692
        return None
693
    cert = cert_list[0]
694
    return ctypes.string_at(cert.data, cert.size)
695
696
697
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
698
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
699
    # New GnuTLS "datum" with the OpenPGP public key
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
700
    datum = (gnutls.library.types
701
             .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
702
                                         ctypes.POINTER
703
                                         (ctypes.c_ubyte)),
704
                             ctypes.c_uint(len(openpgp))))
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
705
    # New empty GnuTLS certificate
706
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
707
    (gnutls.library.functions
708
     .gnutls_openpgp_crt_init(ctypes.byref(crt)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
709
    # Import the OpenPGP public key into the certificate
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
710
    (gnutls.library.functions
711
     .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
712
                                gnutls.library.constants
713
                                .GNUTLS_OPENPGP_FMT_RAW))
24.1.62 by Björn Påhlsson
merge + small bugfix
714
    # Verify the self signature in the key
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
715
    crtverify = ctypes.c_uint()
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
716
    (gnutls.library.functions
717
     .gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
99 by Teddy Hogeborn
* mandos (fingerprint): Bug fix: Check crtverify.value, not crtverify.
718
    if crtverify.value != 0:
24.1.62 by Björn Påhlsson
merge + small bugfix
719
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
720
        raise gnutls.errors.CertificateSecurityError("Verify failed")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
721
    # New buffer for the fingerprint
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
722
    buf = ctypes.create_string_buffer(20)
723
    buf_len = ctypes.c_size_t()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
724
    # Get the fingerprint from the certificate into the buffer
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
725
    (gnutls.library.functions
726
     .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
727
                                         ctypes.byref(buf_len)))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
728
    # Deinit the certificate
729
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
730
    # Convert the buffer to a Python bytestring
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
731
    fpr = ctypes.string_at(buf, buf_len.value)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
732
    # Convert the bytestring to hexadecimal notation
733
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
734
    return hex_fpr
735
736
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
737
class TCP_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
738
    """A TCP request handler class.
739
    Instantiated by IPv6_TCPServer for each request to handle it.
740
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
741
    
3 by Björn Påhlsson
Python based server
742
    def handle(self):
44 by Teddy Hogeborn
* ca.pem: Removed.
743
        logger.info(u"TCP connection from: %s",
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
744
                    unicode(self.client_address))
327 by Teddy Hogeborn
Merge from pipe IPC branch.
745
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
746
        # Open IPC pipe to parent process
747
        with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
748
            session = (gnutls.connection
749
                       .ClientSession(self.request,
750
                                      gnutls.connection
751
                                      .X509Credentials()))
752
            
753
            line = self.request.makefile().readline()
754
            logger.debug(u"Protocol version: %r", line)
755
            try:
756
                if int(line.strip().split()[0]) > 1:
757
                    raise RuntimeError
758
            except (ValueError, IndexError, RuntimeError), error:
759
                logger.error(u"Unknown protocol version: %s", error)
760
                return
761
            
762
            # Note: gnutls.connection.X509Credentials is really a
763
            # generic GnuTLS certificate credentials object so long as
764
            # no X.509 keys are added to it.  Therefore, we can use it
765
            # here despite using OpenPGP certificates.
766
            
767
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
768
            #                     "+AES-256-CBC", "+SHA1",
769
            #                     "+COMP-NULL", "+CTYPE-OPENPGP",
770
            #                     "+DHE-DSS"))
771
            # Use a fallback default, since this MUST be set.
772
            priority = self.server.settings.get("priority", "NORMAL")
773
            (gnutls.library.functions
774
             .gnutls_priority_set_direct(session._c_object,
775
                                         priority, None))
288.1.2 by Teddy Hogeborn
Merge from trunk.
776
            
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
777
            try:
778
                session.handshake()
779
            except gnutls.errors.GNUTLSError, error:
780
                logger.warning(u"Handshake failed: %s", error)
781
                # Do not run session.bye() here: the session is not
782
                # established.  Just abandon the request.
783
                return
784
            logger.debug(u"Handshake succeeded")
785
            try:
786
                fpr = fingerprint(peer_certificate(session))
787
            except (TypeError, gnutls.errors.GNUTLSError), error:
788
                logger.warning(u"Bad certificate: %s", error)
789
                session.bye()
790
                return
791
            logger.debug(u"Fingerprint: %s", fpr)
288.1.2 by Teddy Hogeborn
Merge from trunk.
792
            
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
793
            for c in self.server.clients:
794
                if c.fingerprint == fpr:
795
                    client = c
796
                    break
797
            else:
798
                logger.warning(u"Client not found for fingerprint: %s",
799
                               fpr)
800
                ipc.write("NOTFOUND %s\n" % fpr)
801
                session.bye()
802
                return
803
            # Have to check if client.still_valid(), since it is
804
            # possible that the client timed out while establishing
805
            # the GnuTLS session.
806
            if not client.still_valid():
807
                logger.warning(u"Client %(name)s is invalid",
808
                               vars(client))
809
                ipc.write("INVALID %s\n" % client.name)
810
                session.bye()
811
                return
812
            ipc.write("SENDING %s\n" % client.name)
813
            sent_size = 0
814
            while sent_size < len(client.secret):
815
                sent = session.send(client.secret[sent_size:])
816
                logger.debug(u"Sent: %d, remaining: %d",
817
                             sent, len(client.secret)
818
                             - (sent_size + sent))
819
                sent_size += sent
820
            session.bye()
821
822
823
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
824
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
825
    Assumes a gobject.MainLoop event loop.
826
    """
827
    def process_request(self, request, client_address):
828
        """This overrides and wraps the original process_request().
829
        This function creates a new pipe in self.pipe 
830
        """
831
        self.pipe = os.pipe()
832
        super(ForkingMixInWithPipe,
833
              self).process_request(request, client_address)
834
        os.close(self.pipe[1])  # close write end
835
        # Call "handle_ipc" for both data and EOF events
836
        gobject.io_add_watch(self.pipe[0],
837
                             gobject.IO_IN | gobject.IO_HUP,
838
                             self.handle_ipc)
839
    def handle_ipc(source, condition):
840
        """Dummy function; override as necessary"""
841
        os.close(source)
842
        return False
843
844
845
class IPv6_TCPServer(ForkingMixInWithPipe,
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
846
                     SocketServer.TCPServer, object):
237.2.13 by Teddy Hogeborn
Merge from trunk. Notable changes:
847
    """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
848
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
849
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
850
        clients:        Set() of Client objects
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
851
        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
852
    """
853
    address_family = socket.AF_INET6
854
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
855
        if "settings" in kwargs:
856
            self.settings = kwargs["settings"]
857
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
858
        if "clients" in kwargs:
859
            self.clients = kwargs["clients"]
860
            del kwargs["clients"]
314 by Teddy Hogeborn
Support not using IPv6 in server:
861
        if "use_ipv6" in kwargs:
862
            if not kwargs["use_ipv6"]:
863
                self.address_family = socket.AF_INET
864
            del kwargs["use_ipv6"]
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
865
        self.enabled = False
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
866
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
867
    def server_bind(self):
868
        """This overrides the normal server_bind() function
869
        to bind to an interface if one was specified, and also NOT to
870
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
871
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
872
            # 25 is from /usr/include/asm-i486/socket.h
873
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
874
            try:
875
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
876
                                       SO_BINDTODEVICE,
877
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
878
            except socket.error, error:
879
                if error[0] == errno.EPERM:
44 by Teddy Hogeborn
* ca.pem: Removed.
880
                    logger.error(u"No permission to"
881
                                 u" bind to interface %s",
882
                                 self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
883
                else:
292 by Teddy Hogeborn
* Makefile (run-server): Use "--no-dbus" unconditionally.
884
                    raise
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
885
        # Only bind(2) the socket if we really need to.
886
        if self.server_address[0] or self.server_address[1]:
887
            if not self.server_address[0]:
314 by Teddy Hogeborn
Support not using IPv6 in server:
888
                if self.address_family == socket.AF_INET6:
889
                    any_address = "::" # in6addr_any
890
                else:
891
                    any_address = socket.INADDR_ANY
892
                self.server_address = (any_address,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
893
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
894
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
895
                self.server_address = (self.server_address[0],
896
                                       0)
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
897
#                 if self.settings["interface"]:
898
#                     self.server_address = (self.server_address[0],
899
#                                            0, # port
900
#                                            0, # flowinfo
901
#                                            if_nametoindex
902
#                                            (self.settings
903
#                                             ["interface"]))
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
904
            return super(IPv6_TCPServer, self).server_bind()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
905
    def server_activate(self):
906
        if self.enabled:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
907
            return super(IPv6_TCPServer, self).server_activate()
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
908
    def enable(self):
909
        self.enabled = True
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
910
    def handle_ipc(self, source, condition, file_objects={}):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
911
        condition_names = {
912
            gobject.IO_IN: "IN", # There is data to read.
913
            gobject.IO_OUT: "OUT", # Data can be written (without
914
                                   # blocking).
915
            gobject.IO_PRI: "PRI", # There is urgent data to read.
916
            gobject.IO_ERR: "ERR", # Error condition.
917
            gobject.IO_HUP: "HUP"  # Hung up (the connection has been
918
                                   # broken, usually for pipes and
919
                                   # sockets).
920
            }
921
        conditions_string = ' | '.join(name
922
                                       for cond, name in
923
                                       condition_names.iteritems()
924
                                       if cond & condition)
925
        logger.debug("Handling IPC: FD = %d, condition = %s", source,
926
                     conditions_string)
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
927
        
327 by Teddy Hogeborn
Merge from pipe IPC branch.
928
        # Turn the pipe file descriptor into a Python file object
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
929
        if source not in file_objects:
930
            file_objects[source] = os.fdopen(source, "r", 1)
931
        
932
        # Read a line from the file object
933
        cmdline = file_objects[source].readline()
934
        if not cmdline:             # Empty line means end of file
935
            # close the IPC pipe
936
            file_objects[source].close()
937
            del file_objects[source]
327 by Teddy Hogeborn
Merge from pipe IPC branch.
938
            
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
939
            # Stop calling this function
940
            return False
941
        
942
        logger.debug("IPC command: %r\n" % cmdline)
943
        
944
        # Parse and act on command
945
        cmd, args = cmdline.split(None, 1)
946
        if cmd == "NOTFOUND":
327 by Teddy Hogeborn
Merge from pipe IPC branch.
947
            if self.settings["use_dbus"]:
948
                # Emit D-Bus signal
949
                mandos_dbus_service.ClientNotFound(args)
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
950
        elif cmd == "INVALID":
327 by Teddy Hogeborn
Merge from pipe IPC branch.
951
            if self.settings["use_dbus"]:
952
                for client in self.clients:
953
                    if client.name == args:
954
                        # Emit D-Bus signal
955
                        client.Rejected()
956
                        break
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
957
        elif cmd == "SENDING":
327 by Teddy Hogeborn
Merge from pipe IPC branch.
958
            for client in self.clients:
959
                if client.name == args:
960
                    client.checked_ok()
961
                    if self.settings["use_dbus"]:
962
                        # Emit D-Bus signal
963
                        client.ReceivedSecret()
964
                    break
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
965
        else:
966
            logger.error("Unknown IPC command: %r", cmdline)
967
        
968
        # Keep calling this function
969
        return True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
970
3 by Björn Påhlsson
Python based server
971
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
972
def string_to_delta(interval):
973
    """Parse a string and return a datetime.timedelta
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
974
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
975
    >>> string_to_delta('7d')
976
    datetime.timedelta(7)
977
    >>> string_to_delta('60s')
978
    datetime.timedelta(0, 60)
979
    >>> string_to_delta('60m')
980
    datetime.timedelta(0, 3600)
981
    >>> string_to_delta('24h')
982
    datetime.timedelta(1)
983
    >>> string_to_delta(u'1w')
984
    datetime.timedelta(7)
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
985
    >>> string_to_delta('5m 30s')
986
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
987
    """
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
988
    timevalue = datetime.timedelta(0)
989
    for s in interval.split():
990
        try:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
991
            suffix = unicode(s[-1])
992
            value = int(s[:-1])
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
993
            if suffix == u"d":
994
                delta = datetime.timedelta(value)
995
            elif suffix == u"s":
996
                delta = datetime.timedelta(0, value)
997
            elif suffix == u"m":
998
                delta = datetime.timedelta(0, 0, 0, 0, value)
999
            elif suffix == u"h":
1000
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1001
            elif suffix == u"w":
1002
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1003
            else:
1004
                raise ValueError
1005
        except (ValueError, IndexError):
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1006
            raise ValueError
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1007
        timevalue += delta
1008
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1009
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
1010
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1011
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1012
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1013
    if state == avahi.SERVER_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
1014
        logger.error(u"Zeroconf server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
1015
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1016
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
1017
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1018
1019
1020
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1021
    """Derived from the Avahi example code"""
109 by Teddy Hogeborn
* .bzrignore: New.
1022
    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
1023
    
1024
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
109 by Teddy Hogeborn
* .bzrignore: New.
1025
        logger.debug(u"Zeroconf service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1026
    elif state == avahi.ENTRY_GROUP_COLLISION:
109 by Teddy Hogeborn
* .bzrignore: New.
1027
        logger.warning(u"Zeroconf service name collision.")
28 by Teddy Hogeborn
* server.conf: New file.
1028
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1029
    elif state == avahi.ENTRY_GROUP_FAILURE:
109 by Teddy Hogeborn
* .bzrignore: New.
1030
        logger.critical(u"Avahi: Error in group state changed %s",
28 by Teddy Hogeborn
* server.conf: New file.
1031
                        unicode(error))
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
1032
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
28 by Teddy Hogeborn
* server.conf: New file.
1033
24.1.13 by Björn Påhlsson
mandosclient
1034
def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
1035
    """Call the C function if_nametoindex(), or equivalent"""
24.1.13 by Björn Påhlsson
mandosclient
1036
    global if_nametoindex
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1037
    try:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1038
        if_nametoindex = (ctypes.cdll.LoadLibrary
1039
                          (ctypes.util.find_library("c"))
1040
                          .if_nametoindex)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
1041
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1042
        if "struct" not in sys.modules:
1043
            import struct
1044
        if "fcntl" not in sys.modules:
1045
            import fcntl
24.1.13 by Björn Påhlsson
mandosclient
1046
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
1047
            "Get an interface index the hard way, i.e. using fcntl()"
1048
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
1049
            with closing(socket.socket()) as s:
1050
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1051
                                    struct.pack("16s16x", interface))
28 by Teddy Hogeborn
* server.conf: New file.
1052
            interface_index = struct.unpack("I", ifreq[16:20])[0]
1053
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
1054
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1055
1056
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1057
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1058
    """See daemon(3).  Standard BSD Unix function.
1059
    This should really exist as os.daemon, but it doesn't (yet)."""
1060
    if os.fork():
1061
        sys.exit()
1062
    os.setsid()
1063
    if not nochdir:
1064
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
1065
    if os.fork():
1066
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1067
    if not noclose:
1068
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
1069
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1070
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1071
            raise OSError(errno.ENODEV,
1072
                          "/dev/null not a character device")
1073
        os.dup2(null, sys.stdin.fileno())
1074
        os.dup2(null, sys.stdout.fileno())
1075
        os.dup2(null, sys.stderr.fileno())
1076
        if null > 2:
1077
            os.close(null)
1078
1079
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1080
def main():
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1081
    
1082
    ######################################################################
1083
    # Parsing of options, both command line and config file
1084
    
237.2.1 by Teddy Hogeborn
Merge from trunk, but disable the unfinished D-Bus feature:
1085
    parser = optparse.OptionParser(version = "%%prog %s" % version)
3 by Björn Påhlsson
Python based server
1086
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
1087
                      metavar="IF", help="Bind to interface IF")
1088
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1089
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
1090
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
1091
                      help="Port number to receive requests on")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1092
    parser.add_option("--check", action="store_true",
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1093
                      help="Run self-test")
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1094
    parser.add_option("--debug", action="store_true",
28 by Teddy Hogeborn
* server.conf: New file.
1095
                      help="Debug mode; run in foreground and log to"
1096
                      " terminal")
1097
    parser.add_option("--priority", type="string", help="GnuTLS"
1098
                      " priority string (see GnuTLS documentation)")
1099
    parser.add_option("--servicename", type="string", metavar="NAME",
1100
                      help="Zeroconf service name")
1101
    parser.add_option("--configdir", type="string",
1102
                      default="/etc/mandos", metavar="DIR",
1103
                      help="Directory to search for configuration"
1104
                      " files")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1105
    parser.add_option("--no-dbus", action="store_false",
1106
                      dest="use_dbus",
324 by Teddy Hogeborn
Merge from release branch.
1107
                      help="Do not provide D-Bus system bus"
1108
                      " interface")
314 by Teddy Hogeborn
Support not using IPv6 in server:
1109
    parser.add_option("--no-ipv6", action="store_false",
1110
                      dest="use_ipv6", help="Do not use IPv6")
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1111
    options = parser.parse_args()[0]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1112
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1113
    if options.check:
1114
        import doctest
1115
        doctest.testmod()
1116
        sys.exit()
3 by Björn Påhlsson
Python based server
1117
    
28 by Teddy Hogeborn
* server.conf: New file.
1118
    # Default values for config file for server-global settings
1119
    server_defaults = { "interface": "",
1120
                        "address": "",
1121
                        "port": "",
1122
                        "debug": "False",
1123
                        "priority":
1124
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1125
                        "servicename": "Mandos",
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1126
                        "use_dbus": "True",
314 by Teddy Hogeborn
Support not using IPv6 in server:
1127
                        "use_ipv6": "True",
28 by Teddy Hogeborn
* server.conf: New file.
1128
                        }
1129
    
1130
    # Parse config file for server-global settings
1131
    server_config = ConfigParser.SafeConfigParser(server_defaults)
1132
    del server_defaults
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1133
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
1134
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
1135
    server_settings = server_config.defaults()
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
1136
    # Use the appropriate methods on the non-string config options
1137
    server_settings["debug"] = server_config.getboolean("DEFAULT",
1138
                                                        "debug")
1139
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1140
                                                           "use_dbus")
314 by Teddy Hogeborn
Support not using IPv6 in server:
1141
    server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1142
                                                           "use_ipv6")
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
1143
    if server_settings["port"]:
1144
        server_settings["port"] = server_config.getint("DEFAULT",
1145
                                                       "port")
28 by Teddy Hogeborn
* server.conf: New file.
1146
    del server_config
1147
    
1148
    # Override the settings from the config file with command line
1149
    # options, if set.
1150
    for option in ("interface", "address", "port", "debug",
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1151
                   "priority", "servicename", "configdir",
314 by Teddy Hogeborn
Support not using IPv6 in server:
1152
                   "use_dbus", "use_ipv6"):
28 by Teddy Hogeborn
* server.conf: New file.
1153
        value = getattr(options, option)
1154
        if value is not None:
1155
            server_settings[option] = value
1156
    del options
1157
    # Now we have our good server settings in "server_settings"
1158
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1159
    ##################################################################
1160
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1161
    # For convenience
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
1162
    debug = server_settings["debug"]
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1163
    use_dbus = server_settings["use_dbus"]
314 by Teddy Hogeborn
Support not using IPv6 in server:
1164
    use_ipv6 = server_settings["use_ipv6"]
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
1165
    
1166
    if not debug:
1167
        syslogger.setLevel(logging.WARNING)
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
1168
        console.setLevel(logging.WARNING)
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
1169
    
1170
    if server_settings["servicename"] != "Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1171
        syslogger.setFormatter(logging.Formatter
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1172
                               ('Mandos (%s) [%%(process)d]:'
1173
                                ' %%(levelname)s: %%(message)s'
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
1174
                                % server_settings["servicename"]))
1175
    
28 by Teddy Hogeborn
* server.conf: New file.
1176
    # Parse config file with clients
1177
    client_defaults = { "timeout": "1h",
1178
                        "interval": "5m",
24.1.121 by Björn Påhlsson
mandos-ctl: Added support for all client calls
1179
                        "checker": "fping -q -- %%(host)s",
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1180
                        "host": "",
28 by Teddy Hogeborn
* server.conf: New file.
1181
                        }
1182
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1183
    client_config.read(os.path.join(server_settings["configdir"],
1184
                                    "clients.conf"))
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1185
1186
    global mandos_dbus_service
1187
    mandos_dbus_service = None
28 by Teddy Hogeborn
* server.conf: New file.
1188
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1189
    clients = Set()
1190
    tcp_server = IPv6_TCPServer((server_settings["address"],
1191
                                 server_settings["port"]),
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1192
                                TCP_handler,
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1193
                                settings=server_settings,
314 by Teddy Hogeborn
Support not using IPv6 in server:
1194
                                clients=clients, use_ipv6=use_ipv6)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1195
    pidfilename = "/var/run/mandos.pid"
1196
    try:
1197
        pidfile = open(pidfilename, "w")
292 by Teddy Hogeborn
* Makefile (run-server): Use "--no-dbus" unconditionally.
1198
    except IOError:
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1199
        logger.error("Could not open file %r", pidfilename)
1200
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1201
    try:
1202
        uid = pwd.getpwnam("_mandos").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1203
        gid = pwd.getpwnam("_mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1204
    except KeyError:
1205
        try:
1206
            uid = pwd.getpwnam("mandos").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1207
            gid = pwd.getpwnam("mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1208
        except KeyError:
1209
            try:
1210
                uid = pwd.getpwnam("nobody").pw_uid
256 by Teddy Hogeborn
* mandos (main): Try to find non-privileged user+group in pairs, so
1211
                gid = pwd.getpwnam("nogroup").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1212
            except KeyError:
1213
                uid = 65534
1214
                gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1215
    try:
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1216
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1217
        os.setuid(uid)
1218
    except OSError, error:
1219
        if error[0] != errno.EPERM:
1220
            raise error
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1221
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1222
    # Enable all possible GnuTLS debugging
1223
    if debug:
1224
        # "Use a log level over 10 to enable all debugging options."
1225
        # - GnuTLS manual
1226
        gnutls.library.functions.gnutls_global_set_log_level(11)
1227
        
1228
        @gnutls.library.types.gnutls_log_func
1229
        def debug_gnutls(level, string):
1230
            logger.debug("GnuTLS: %s", string[:-1])
1231
        
1232
        (gnutls.library.functions
1233
         .gnutls_global_set_log_function(debug_gnutls))
1234
    
28 by Teddy Hogeborn
* server.conf: New file.
1235
    global service
314 by Teddy Hogeborn
Support not using IPv6 in server:
1236
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
28 by Teddy Hogeborn
* server.conf: New file.
1237
    service = AvahiService(name = server_settings["servicename"],
314 by Teddy Hogeborn
Support not using IPv6 in server:
1238
                           servicetype = "_mandos._tcp",
1239
                           protocol = protocol)
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1240
    if server_settings["interface"]:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1241
        service.interface = (if_nametoindex
1242
                             (server_settings["interface"]))
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
1243
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1244
    global main_loop
1245
    global bus
1246
    global server
1247
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1248
    DBusGMainLoop(set_as_default=True )
1249
    main_loop = gobject.MainLoop()
1250
    bus = dbus.SystemBus()
109 by Teddy Hogeborn
* .bzrignore: New.
1251
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1252
                                           avahi.DBUS_PATH_SERVER),
1253
                            avahi.DBUS_INTERFACE_SERVER)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1254
    # End of Avahi example code
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1255
    if use_dbus:
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1256
        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
1257
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1258
    client_class = Client
1259
    if use_dbus:
1260
        client_class = ClientDBus
1261
    clients.update(Set(
1262
            client_class(name = section,
1263
                         config= dict(client_config.items(section)))
1264
            for section in client_config.sections()))
51 by Teddy Hogeborn
* clients.conf: Better comments.
1265
    if not clients:
244 by Teddy Hogeborn
* debian/control (Build-Depends): Bug fix: Added "docbook-xml".
1266
        logger.warning(u"No clients defined")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1267
    
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1268
    if debug:
1269
        # Redirect stdin so all checkers get /dev/null
1270
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1271
        os.dup2(null, sys.stdin.fileno())
1272
        if null > 2:
1273
            os.close(null)
1274
    else:
1275
        # No console logging
61 by Teddy Hogeborn
* mandos (console): Define handler globally.
1276
        logger.removeHandler(console)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
1277
        # Close all input and output, do double fork, etc.
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1278
        daemon()
51 by Teddy Hogeborn
* clients.conf: Better comments.
1279
    
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1280
    try:
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1281
        with closing(pidfile):
1282
            pid = os.getpid()
1283
            pidfile.write(str(pid) + "\n")
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1284
        del pidfile
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1285
    except IOError:
165 by Teddy Hogeborn
* mandos (main): Use EAFP with pidfile.
1286
        logger.error(u"Could not write to file %r with PID %d",
1287
                     pidfilename, pid)
1288
    except NameError:
1289
        # "pidfile" was never created
1290
        pass
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1291
    del pidfilename
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1292
    
1293
    def cleanup():
1294
        "Cleanup function; run on exit"
1295
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1296
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1297
        if not group is None:
1298
            group.Free()
1299
            group = None
1300
        # End of Avahi example code
1301
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1302
        while clients:
1303
            client = clients.pop()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1304
            client.disable_hook = None
1305
            client.disable()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1306
    
1307
    atexit.register(cleanup)
1308
    
1309
    if not debug:
1310
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
1311
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1312
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1313
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1314
    if use_dbus:
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1315
        class MandosDBusService(dbus.service.Object):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1316
            """A D-Bus proxy object"""
1317
            def __init__(self):
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1318
                dbus.service.Object.__init__(self, bus, "/")
1319
            _interface = u"se.bsnet.fukt.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1320
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1321
            @dbus.service.signal(_interface, signature="oa{sv}")
1322
            def ClientAdded(self, objpath, properties):
1323
                "D-Bus signal"
1324
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1325
            
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1326
            @dbus.service.signal(_interface, signature="s")
1327
            def ClientNotFound(self, fingerprint):
1328
                "D-Bus signal"
1329
                pass
1330
            
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1331
            @dbus.service.signal(_interface, signature="os")
1332
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1333
                "D-Bus signal"
1334
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1335
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1336
            @dbus.service.method(_interface, out_signature="ao")
1337
            def GetAllClients(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1338
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1339
                return dbus.Array(c.dbus_object_path for c in clients)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1340
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1341
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1342
            def GetAllClientsWithProperties(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1343
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1344
                return dbus.Dictionary(
1345
                    ((c.dbus_object_path, c.GetAllProperties())
1346
                     for c in clients),
1347
                    signature="oa{sv}")
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, in_signature="o")
1350
            def RemoveClient(self, object_path):
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
                for c in clients:
1353
                    if c.dbus_object_path == object_path:
1354
                        clients.remove(c)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1355
                        c.remove_from_connection()
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1356
                        # Don't signal anything except ClientRemoved
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1357
                        c.disable(signal=False)
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1358
                        # Emit D-Bus signal
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1359
                        self.ClientRemoved(object_path, c.name)
1360
                        return
1361
                raise KeyError
1362
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1363
            del _interface
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1364
        
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1365
        mandos_dbus_service = MandosDBusService()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1366
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1367
    for client in clients:
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1368
        if use_dbus:
1369
            # Emit D-Bus signal
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1370
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1371
                                            client.GetAllProperties())
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1372
        client.enable()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1373
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1374
    tcp_server.enable()
1375
    tcp_server.server_activate()
1376
    
28 by Teddy Hogeborn
* server.conf: New file.
1377
    # Find out what port we got
1378
    service.port = tcp_server.socket.getsockname()[1]
314 by Teddy Hogeborn
Support not using IPv6 in server:
1379
    if use_ipv6:
1380
        logger.info(u"Now listening on address %r, port %d,"
1381
                    " flowinfo %d, scope_id %d"
1382
                    % tcp_server.socket.getsockname())
1383
    else:                       # IPv4
1384
        logger.info(u"Now listening on address %r, port %d"
1385
                    % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
1386
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1387
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
1388
    
1389
    try:
1390
        # From the Avahi example code
1391
        server.connect_to_signal("StateChanged", server_state_changed)
1392
        try:
1393
            server_state_changed(server.GetState())
1394
        except dbus.exceptions.DBusException, error:
1395
            logger.critical(u"DBusException: %s", error)
1396
            sys.exit(1)
1397
        # End of Avahi example code
1398
        
1399
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
1400
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1401
                             (tcp_server.handle_request
1402
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
1403
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
1404
        logger.debug(u"Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1405
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
1406
    except AvahiError, error:
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
1407
        logger.critical(u"AvahiError: %s", error)
28 by Teddy Hogeborn
* server.conf: New file.
1408
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1409
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1410
        if debug:
292 by Teddy Hogeborn
* Makefile (run-server): Use "--no-dbus" unconditionally.
1411
            print >> sys.stderr
1412
        logger.debug("Server received KeyboardInterrupt")
1413
    logger.debug("Server exiting")
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1414
1415
if __name__ == '__main__':
1416
    main()