/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to server.py

  • Committer: Teddy Hogeborn
  • Date: 2008-07-19 18:43:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080719184324-iwhoa5in75xa0u2u
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
                       [braxen_client]/password): Removed.
  ([foo]/fingerprint, [braxen_client]/fingerprint): New.
  ([foo]/checker): New.
  ([foo]/secfile): New.
  ([braxen_client]/secret): New.

* server.py: New "--debug" option to set debug flag.  Removed "cert",
             "key", "ca", "crl", and "cred" variables.  Added default
             value for "checker" config file setting.  Do not pass
             credentials to IPv6_TCPServer constructor.
  (debug): New global debug flag.  Used by most debugging output code.
  (Client.__init__): Keyword argument "dn" replaced by "fingerprint",
                     "password" renamed to "secret", and "passfile"
                     renamed to "secfile".  New keyword argument
                     "checker". All callers changed.
  (Client.dn): Removed.
  (Client.fingerprint): New.
  (Client.password): Renamed to "secret"; all users changed.
  (Client.passfile): Renamed to "secfile"; all users changed.
  (Client.timeout, Client.interval): Changed to be properties; now
                                     automatically updates the
                                     "_timeout_milliseconds" and
                                     "_interval_milliseconds" values.
  (Client.timeout_milliseconds): Renamed to "_timeout_milliseconds".
  (Client.interval_milliseconds): Renamed to "_interval_milliseconds".
  (Client.check_command): New.
  (Client.start_checker): Use the new "check_command" attribute.
  (peer_certificate, fingerprint): New functions.

  (tcp_handler.handle): Use ClientSession with empty credentials
                        object instead of ServerSession.  Set gnutls
                        priority string.  Do not verify peer.  Use
                        fingerprint instead of DN when searching for
                        clients.  Bug fix: Loop sending data so even large
                        secret data strings are sent.
  (IPv6_TCPServer.credentials): Removed.
  (if_nametoindex): Do not import ctypes since that is now imported
                    globally.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from dbus.mainloop.glib import DBusGMainLoop
29
29
import ctypes
30
30
 
31
 
import logging
32
 
import logging.handlers
33
 
 
34
 
# logghandler.setFormatter(logging.Formatter('%(levelname)s %(message)s')
35
 
 
36
 
logger = logging.Logger('mandos')
37
 
logger.addHandler(logging.handlers.SysLogHandler(facility = logging.handlers.SysLogHandler.LOG_DAEMON))
38
 
 
39
31
# This variable is used to optionally bind to a specified interface.
40
32
# It is a global variable to fit in with the other variables from the
41
33
# Avahi server example code.
157
149
        """Stop this client.
158
150
        The possibility that this client might be restarted is left
159
151
        open, but not currently used."""
160
 
        logger.debug(u"Stopping client %s", self.name)
 
152
        if debug:
 
153
            sys.stderr.write(u"Stopping client %s\n" % self.name)
161
154
        self.secret = None
162
155
        if self.stop_initiator_tag:
163
156
            gobject.source_remove(self.stop_initiator_tag)
186
179
        now = datetime.datetime.now()
187
180
        if os.WIFEXITED(condition) \
188
181
               and (os.WEXITSTATUS(condition) == 0):
189
 
            logger.debug(u"Checker for %(name)s succeeded",
190
 
                         vars(self))
 
182
            if debug:
 
183
                sys.stderr.write(u"Checker for %(name)s succeeded\n"
 
184
                                 % vars(self))
191
185
            self.last_seen = now
192
186
            gobject.source_remove(self.stop_initiator_tag)
193
187
            self.stop_initiator_tag = gobject.timeout_add\
194
188
                                      (self._timeout_milliseconds,
195
189
                                       self.stop)
196
 
        if not os.WIFEXITED(condition):
197
 
            logger.warning(u"Checker for %(name)s crashed?",
198
 
                           vars(self))
199
 
        else:
200
 
            logger.debug(u"Checker for %(name)s failed",
201
 
                         vars(self))
202
 
            self.checker = None
 
190
        elif debug:
 
191
            if not os.WIFEXITED(condition):
 
192
                sys.stderr.write(u"Checker for %(name)s crashed?\n"
 
193
                                 % vars(self))
 
194
            else:
 
195
                sys.stderr.write(u"Checker for %(name)s failed\n"
 
196
                                 % vars(self))
 
197
        self.checker = None
203
198
        self.checker_callback_tag = None
204
199
    def start_checker(self):
205
200
        """Start a new checker subprocess if one is not running.
206
201
        If a checker already exists, leave it running and do
207
202
        nothing."""
208
203
        if self.checker is None:
209
 
            logger.debug(u"Starting checker for %s",
210
 
                         self.name)
 
204
            if debug:
 
205
                sys.stderr.write(u"Starting checker for %s\n"
 
206
                                 % self.name)
211
207
            try:
212
208
                command = self.check_command % self.fqdn
213
209
            except TypeError:
214
210
                escaped_attrs = dict((key, re.escape(str(val)))
215
211
                                     for key, val in
216
212
                                     vars(self).iteritems())
217
 
                try:
218
 
                    command = self.check_command % escaped_attrs
219
 
                except TypeError, error:
220
 
                    logger.critical(u'Could not format string "%s": %s',
221
 
                                    self.check_command, error)
222
 
                    return True # Try again later
 
213
                command = self.check_command % escaped_attrs
223
214
            try:
224
215
                self.checker = subprocess.\
225
216
                               Popen(command,
231
222
                                                            self.\
232
223
                                                            checker_callback)
233
224
            except subprocess.OSError, error:
234
 
                logger.error(u"Failed to start subprocess: %s",
235
 
                             error)
 
225
                sys.stderr.write(u"Failed to start subprocess: %s\n"
 
226
                                 % error)
236
227
        # Re-run this periodically if run by gobject.timeout_add
237
228
        return True
238
229
    def stop_checker(self):
307
298
    Note: This will run in its own forked process."""
308
299
    
309
300
    def handle(self):
310
 
        logger.debug(u"TCP connection from: %s",
311
 
                     unicode(self.client_address))
 
301
        if debug:
 
302
            sys.stderr.write(u"TCP request came\n")
 
303
            sys.stderr.write(u"Request: %s\n" % self.request)
 
304
            sys.stderr.write(u"Client Address: %s\n"
 
305
                             % unicode(self.client_address))
 
306
            sys.stderr.write(u"Server: %s\n" % self.server)
312
307
        session = gnutls.connection.ClientSession(self.request,
313
308
                                                  gnutls.connection.\
314
309
                                                  X509Credentials())
324
319
        try:
325
320
            session.handshake()
326
321
        except gnutls.errors.GNUTLSError, error:
327
 
            logger.debug(u"Handshake failed: %s", error)
 
322
            if debug:
 
323
                sys.stderr.write(u"Handshake failed: %s\n" % error)
328
324
            # Do not run session.bye() here: the session is not
329
325
            # established.  Just abandon the request.
330
326
            return
331
327
        try:
332
328
            fpr = fingerprint(peer_certificate(session))
333
329
        except (TypeError, gnutls.errors.GNUTLSError), error:
334
 
            logger.debug(u"Bad certificate: %s", error)
 
330
            if debug:
 
331
                sys.stderr.write(u"Bad certificate: %s\n" % error)
335
332
            session.bye()
336
333
            return
337
 
        logger.debug(u"Fingerprint: %s", fpr)
 
334
        if debug:
 
335
            sys.stderr.write(u"Fingerprint: %s\n" % fpr)
338
336
        client = None
339
337
        for c in clients:
340
338
            if c.fingerprint == fpr:
344
342
        # that the client timed out while establishing the GnuTLS
345
343
        # session.
346
344
        if (not client) or (not client.still_valid()):
347
 
            if client:
348
 
                logger.debug(u"Client %(name)s is invalid",
349
 
                             vars(client))
350
 
            else:
351
 
                logger.debug(u"Client not found for fingerprint: %s",
352
 
                             fpr)
 
345
            if debug:
 
346
                if client:
 
347
                    sys.stderr.write(u"Client %(name)s is invalid\n"
 
348
                                     % vars(client))
 
349
                else:
 
350
                    sys.stderr.write(u"Client not found for "
 
351
                                     u"fingerprint: %s\n" % fpr)
353
352
            session.bye()
354
353
            return
355
354
        sent_size = 0
356
355
        while sent_size < len(client.secret):
357
356
            sent = session.send(client.secret[sent_size:])
358
 
            logger.debug(u"Sent: %d, remaining: %d",
359
 
                         sent, len(client.secret)
360
 
                         - (sent_size + sent))
 
357
            if debug:
 
358
                sys.stderr.write(u"Sent: %d, remaining: %d\n"
 
359
                                 % (sent, len(client.secret)
 
360
                                    - (sent_size + sent)))
361
361
            sent_size += sent
362
362
        session.bye()
363
363
 
391
391
                                       self.options.interface)
392
392
            except socket.error, error:
393
393
                if error[0] == errno.EPERM:
394
 
                    logger.warning(u"No permission to"
395
 
                                   u" bind to interface %s",
396
 
                                   self.options.interface)
 
394
                    sys.stderr.write(u"Warning: No permission to" \
 
395
                                     u" bind to interface %s\n"
 
396
                                     % self.options.interface)
397
397
                else:
398
398
                    raise error
399
399
        # Only bind(2) the socket if we really need to.
453
453
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
454
454
        group.connect_to_signal('StateChanged',
455
455
                                entry_group_state_changed)
456
 
    logger.debug(u"Adding service '%s' of type '%s' ...",
457
 
                 serviceName, serviceType)
 
456
    if debug:
 
457
        sys.stderr.write(u"Adding service '%s' of type '%s' ...\n"
 
458
                         % (serviceName, serviceType))
458
459
    
459
460
    group.AddService(
460
461
            serviceInterface,           # interface
478
479
def server_state_changed(state):
479
480
    """From the Avahi server example code"""
480
481
    if state == avahi.SERVER_COLLISION:
481
 
        logger.warning(u"Server name collision")
 
482
        sys.stderr.write(u"WARNING: Server name collision\n")
482
483
        remove_service()
483
484
    elif state == avahi.SERVER_RUNNING:
484
485
        add_service()
488
489
    """From the Avahi server example code"""
489
490
    global serviceName, server, rename_count
490
491
    
491
 
    logger.debug(u"state change: %i", state)
 
492
    if debug:
 
493
        sys.stderr.write(u"state change: %i\n" % state)
492
494
    
493
495
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
494
 
        logger.debug(u"Service established.")
 
496
        if debug:
 
497
            sys.stderr.write(u"Service established.\n")
495
498
    elif state == avahi.ENTRY_GROUP_COLLISION:
496
499
        
497
500
        rename_count = rename_count - 1
498
501
        if rename_count > 0:
499
502
            name = server.GetAlternativeServiceName(name)
500
 
            logger.warning(u"Service name collision, "
501
 
                           u"changing name to '%s' ...", name)
 
503
            sys.stderr.write(u"WARNING: Service name collision, "
 
504
                             u"changing name to '%s' ...\n" % name)
502
505
            remove_service()
503
506
            add_service()
504
507
            
505
508
        else:
506
 
            logger.error(u"No suitable service name found "
507
 
                         u"after %i retries, exiting.",
508
 
                         n_rename)
 
509
            sys.stderr.write(u"ERROR: No suitable service name found "
 
510
                             u"after %i retries, exiting.\n"
 
511
                             % n_rename)
509
512
            main_loop.quit()
510
513
    elif state == avahi.ENTRY_GROUP_FAILURE:
511
 
        logger.error(u"Error in group state changed %s",
512
 
                     unicode(error))
 
514
        sys.stderr.write(u"Error in group state changed %s\n"
 
515
                         % unicode(error))
513
516
        main_loop.quit()
514
517
        return
515
518
 
600
603
    def remove_from_clients(client):
601
604
        clients.remove(client)
602
605
        if not clients:
603
 
            logger.debug(u"No clients left, exiting")
 
606
            if debug:
 
607
                sys.stderr.write(u"No clients left, exiting\n")
604
608
            main_loop.quit()
605
609
    
606
610
    clients.update(Set(Client(name=section, options=options,
617
621
                                clients=clients)
618
622
    # Find out what random port we got
619
623
    servicePort = tcp_server.socket.getsockname()[1]
620
 
    logger.debug(u"Now listening on port %d", servicePort)
 
624
    if debug:
 
625
        sys.stderr.write(u"Now listening on port %d\n" % servicePort)
621
626
    
622
627
    if options.interface is not None:
623
628
        serviceInterface = if_nametoindex(options.interface)