/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

* mandos: Use all new builtins.
* mandos-ctl: - '' -
* mandos-monitor: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
    except ImportError:
89
89
        SO_BINDTODEVICE = None
90
90
 
91
 
version = "1.5.4"
 
91
version = "1.5.3"
92
92
stored_state_file = "clients.pickle"
93
93
 
94
94
logger = logging.getLogger()
379
379
                                 self.server_state_changed)
380
380
        self.server_state_changed(self.server.GetState())
381
381
 
382
 
 
383
382
class AvahiServiceToSyslog(AvahiService):
384
383
    def rename(self):
385
384
        """Add the new name to the syslog messages"""
390
389
                                .format(self.name)))
391
390
        return ret
392
391
 
393
 
 
394
392
def timedelta_to_milliseconds(td):
395
393
    "Convert a datetime.timedelta() to milliseconds"
396
394
    return ((td.days * 24 * 60 * 60 * 1000)
397
395
            + (td.seconds * 1000)
398
396
            + (td.microseconds // 1000))
399
397
 
400
 
 
401
398
class Client(object):
402
399
    """A representation of a client host served by this server.
403
400
    
442
439
    """
443
440
    
444
441
    runtime_expansions = ("approval_delay", "approval_duration",
445
 
                          "created", "enabled", "expires",
446
 
                          "fingerprint", "host", "interval",
447
 
                          "last_approval_request", "last_checked_ok",
 
442
                          "created", "enabled", "fingerprint",
 
443
                          "host", "interval", "last_checked_ok",
448
444
                          "last_enabled", "name", "timeout")
449
445
    client_defaults = { "timeout": "5m",
450
446
                        "extended_timeout": "15m",
576
572
        if getattr(self, "enabled", False):
577
573
            # Already enabled
578
574
            return
 
575
        self.send_changedstate()
579
576
        self.expires = datetime.datetime.utcnow() + self.timeout
580
577
        self.enabled = True
581
578
        self.last_enabled = datetime.datetime.utcnow()
582
579
        self.init_checker()
583
 
        self.send_changedstate()
584
580
    
585
581
    def disable(self, quiet=True):
586
582
        """Disable this client."""
587
583
        if not getattr(self, "enabled", False):
588
584
            return False
589
585
        if not quiet:
 
586
            self.send_changedstate()
 
587
        if not quiet:
590
588
            logger.info("Disabling client %s", self.name)
591
 
        if getattr(self, "disable_initiator_tag", None) is not None:
 
589
        if getattr(self, "disable_initiator_tag", False):
592
590
            gobject.source_remove(self.disable_initiator_tag)
593
591
            self.disable_initiator_tag = None
594
592
        self.expires = None
595
 
        if getattr(self, "checker_initiator_tag", None) is not None:
 
593
        if getattr(self, "checker_initiator_tag", False):
596
594
            gobject.source_remove(self.checker_initiator_tag)
597
595
            self.checker_initiator_tag = None
598
596
        self.stop_checker()
599
597
        self.enabled = False
600
 
        if not quiet:
601
 
            self.send_changedstate()
602
598
        # Do not run this again if called by a gobject.timeout_add
603
599
        return False
604
600
    
608
604
    def init_checker(self):
609
605
        # Schedule a new checker to be started an 'interval' from now,
610
606
        # and every interval from then on.
611
 
        if self.checker_initiator_tag is not None:
612
 
            gobject.source_remove(self.checker_initiator_tag)
613
607
        self.checker_initiator_tag = (gobject.timeout_add
614
608
                                      (self.interval_milliseconds(),
615
609
                                       self.start_checker))
616
610
        # Schedule a disable() when 'timeout' has passed
617
 
        if self.disable_initiator_tag is not None:
618
 
            gobject.source_remove(self.disable_initiator_tag)
619
611
        self.disable_initiator_tag = (gobject.timeout_add
620
612
                                   (self.timeout_milliseconds(),
621
613
                                    self.disable))
652
644
            timeout = self.timeout
653
645
        if self.disable_initiator_tag is not None:
654
646
            gobject.source_remove(self.disable_initiator_tag)
655
 
            self.disable_initiator_tag = None
656
647
        if getattr(self, "enabled", False):
657
648
            self.disable_initiator_tag = (gobject.timeout_add
658
649
                                          (timedelta_to_milliseconds
691
682
                                      self.current_checker_command)
692
683
        # Start a new checker if needed
693
684
        if self.checker is None:
694
 
            # Escape attributes for the shell
695
 
            escaped_attrs = dict(
696
 
                (attr, re.escape(unicode(getattr(self, attr))))
697
 
                for attr in
698
 
                self.runtime_expansions)
699
685
            try:
700
 
                command = self.checker_command % escaped_attrs
701
 
            except TypeError as error:
702
 
                logger.error('Could not format string "%s"',
703
 
                             self.checker_command, exc_info=error)
704
 
                return True # Try again later
 
686
                # In case checker_command has exactly one % operator
 
687
                command = self.checker_command % self.host
 
688
            except TypeError:
 
689
                # Escape attributes for the shell
 
690
                escaped_attrs = dict(
 
691
                    (attr,
 
692
                     re.escape(unicode(str(getattr(self, attr, "")),
 
693
                                       errors=
 
694
                                       'replace')))
 
695
                    for attr in
 
696
                    self.runtime_expansions)
 
697
                
 
698
                try:
 
699
                    command = self.checker_command % escaped_attrs
 
700
                except TypeError as error:
 
701
                    logger.error('Could not format string "%s"',
 
702
                                 self.checker_command, exc_info=error)
 
703
                    return True # Try again later
705
704
            self.current_checker_command = command
706
705
            try:
707
706
                logger.info("Starting checker %r for %s",
713
712
                self.checker = subprocess.Popen(command,
714
713
                                                close_fds=True,
715
714
                                                shell=True, cwd="/")
 
715
                self.checker_callback_tag = (gobject.child_watch_add
 
716
                                             (self.checker.pid,
 
717
                                              self.checker_callback,
 
718
                                              data=command))
 
719
                # The checker may have completed before the gobject
 
720
                # watch was added.  Check for this.
 
721
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
722
                if pid:
 
723
                    gobject.source_remove(self.checker_callback_tag)
 
724
                    self.checker_callback(pid, status, command)
716
725
            except OSError as error:
717
726
                logger.error("Failed to start subprocess",
718
727
                             exc_info=error)
719
 
            self.checker_callback_tag = (gobject.child_watch_add
720
 
                                         (self.checker.pid,
721
 
                                          self.checker_callback,
722
 
                                          data=command))
723
 
            # The checker may have completed before the gobject
724
 
            # watch was added.  Check for this.
725
 
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
726
 
            if pid:
727
 
                gobject.source_remove(self.checker_callback_tag)
728
 
                self.checker_callback(pid, status, command)
729
728
        # Re-run this periodically if run by gobject.timeout_add
730
729
        return True
731
730
    
1336
1335
        return False
1337
1336
    
1338
1337
    def approve(self, value=True):
 
1338
        self.send_changedstate()
1339
1339
        self.approved = value
1340
1340
        gobject.timeout_add(timedelta_to_milliseconds
1341
1341
                            (self.approval_duration),
1342
1342
                            self._reset_approved)
1343
 
        self.send_changedstate()
1344
1343
    
1345
1344
    ## D-Bus methods, signals & properties
1346
1345
    _interface = "se.recompile.Mandos.Client"
1530
1529
    def Timeout_dbus_property(self, value=None):
1531
1530
        if value is None:       # get
1532
1531
            return dbus.UInt64(self.timeout_milliseconds())
1533
 
        old_timeout = self.timeout
1534
1532
        self.timeout = datetime.timedelta(0, 0, 0, value)
1535
 
        # Reschedule disabling
 
1533
        # Reschedule timeout
1536
1534
        if self.enabled:
1537
1535
            now = datetime.datetime.utcnow()
1538
 
            self.expires += self.timeout - old_timeout
1539
 
            if self.expires <= now:
 
1536
            time_to_die = timedelta_to_milliseconds(
 
1537
                (self.last_checked_ok + self.timeout) - now)
 
1538
            if time_to_die <= 0:
1540
1539
                # The timeout has passed
1541
1540
                self.disable()
1542
1541
            else:
 
1542
                self.expires = (now +
 
1543
                                datetime.timedelta(milliseconds =
 
1544
                                                   time_to_die))
1543
1545
                if (getattr(self, "disable_initiator_tag", None)
1544
1546
                    is None):
1545
1547
                    return
1546
1548
                gobject.source_remove(self.disable_initiator_tag)
1547
 
                self.disable_initiator_tag = (
1548
 
                    gobject.timeout_add(
1549
 
                        timedelta_to_milliseconds(self.expires - now),
1550
 
                        self.disable))
 
1549
                self.disable_initiator_tag = (gobject.timeout_add
 
1550
                                              (time_to_die,
 
1551
                                               self.disable))
1551
1552
    
1552
1553
    # ExtendedTimeout - property
1553
1554
    @dbus_service_property(_interface, signature="t",
1741
1742
                    #wait until timeout or approved
1742
1743
                    time = datetime.datetime.now()
1743
1744
                    client.changedstate.acquire()
1744
 
                    client.changedstate.wait(
1745
 
                        float(timedelta_to_milliseconds(delay)
1746
 
                              / 1000))
 
1745
                    (client.changedstate.wait
 
1746
                     (float(client.timedelta_to_milliseconds(delay)
 
1747
                            / 1000)))
1747
1748
                    client.changedstate.release()
1748
1749
                    time2 = datetime.datetime.now()
1749
1750
                    if (time2 - time) >= delay:
1921
1922
                                           str(self.interface
1922
1923
                                               + '\0'))
1923
1924
                except socket.error as error:
1924
 
                    if error.errno == errno.EPERM:
 
1925
                    if error[0] == errno.EPERM:
1925
1926
                        logger.error("No permission to"
1926
1927
                                     " bind to interface %s",
1927
1928
                                     self.interface)
1928
 
                    elif error.errno == errno.ENOPROTOOPT:
 
1929
                    elif error[0] == errno.ENOPROTOOPT:
1929
1930
                        logger.error("SO_BINDTODEVICE not available;"
1930
1931
                                     " cannot bind to interface %s",
1931
1932
                                     self.interface)
1932
 
                    elif error.errno == errno.ENODEV:
1933
 
                        logger.error("Interface %s does not"
1934
 
                                     " exist, cannot bind",
1935
 
                                     self.interface)
1936
1933
                    else:
1937
1934
                        raise
1938
1935
        # Only bind(2) the socket if we really need to.
2288
2285
        os.setgid(gid)
2289
2286
        os.setuid(uid)
2290
2287
    except OSError as error:
2291
 
        if error.errno != errno.EPERM:
 
2288
        if error[0] != errno.EPERM:
2292
2289
            raise error
2293
2290
    
2294
2291
    if debug:
2462
2459
            # "pidfile" was never created
2463
2460
            pass
2464
2461
        del pidfilename
 
2462
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2465
2463
    
2466
2464
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2467
2465
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())