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