=== modified file 'DBUS-API' --- DBUS-API 2017-02-23 19:11:11 +0000 +++ DBUS-API 2016-02-28 14:22:10 +0000 @@ -130,8 +130,8 @@ * Copyright - Copyright © 2010-2017 Teddy Hogeborn - Copyright © 2010-2017 Björn Påhlsson + Copyright © 2010-2016 Teddy Hogeborn + Copyright © 2010-2016 Björn Påhlsson ** License: === modified file 'INSTALL' --- INSTALL 2016-07-03 03:32:28 +0000 +++ INSTALL 2016-03-13 00:37:02 +0000 @@ -38,12 +38,12 @@ "man -l mandos.8". *** Mandos Server - + GnuTLS 3.3 https://www.gnutls.org/ + + GnuTLS 3.3 http://www.gnutls.org/ + Avahi 0.6.16 http://www.avahi.org/ + Python 2.7 https://www.python.org/ - + dbus-python 0.82.4 https://dbus.freedesktop.org/doc/dbus-python/ + + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + PyGObject 3.7.1 https://wiki.gnome.org/Projects/PyGObject - + pkg-config https://www.freedesktop.org/wiki/Software/pkg-config/ + + pkg-config http://www.freedesktop.org/wiki/Software/pkg-config/ + Urwid 1.0.1 http://urwid.org/ (Only needed by the "mandos-monitor" tool.) @@ -59,11 +59,11 @@ + GNU C Library 2.16 https://gnu.org/software/libc/ + initramfs-tools 0.85i https://tracker.debian.org/pkg/initramfs-tools - + GnuTLS 3.3 https://www.gnutls.org/ + + GnuTLS 3.3 http://www.gnutls.org/ + Avahi 0.6.16 http://www.avahi.org/ + GnuPG 1.4.9 https://www.gnupg.org/ + GPGME 1.1.6 https://www.gnupg.org/related_software/gpgme/ - + pkg-config https://www.freedesktop.org/wiki/Software/pkg-config/ + + pkg-config http://www.freedesktop.org/wiki/Software/pkg-config/ Strongly recommended: + OpenSSH http://www.openssh.com/ === modified file 'Makefile' --- Makefile 2017-02-23 20:35:20 +0000 +++ Makefile 2016-03-19 22:00:38 +0000 @@ -10,12 +10,12 @@ -Wmissing-format-attribute -Wnormalized=nfc -Wpacked \ -Wredundant-decls -Wnested-externs -Winline -Wvla \ -Wvolatile-register-var -Woverlength-strings -#DEBUG=-ggdb3 -fsanitize=address +#DEBUG=-ggdb3 # For info about _FORTIFY_SOURCE, see feature_test_macros(7) -# and . +# and . FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC # -ALL_SANITIZE_OPTIONS:=-fsanitize=leak -fsanitize=undefined \ +ALL_SANITIZE_OPTIONS:=-fsanitize=address -fsanitize=undefined \ -fsanitize=shift -fsanitize=integer-divide-by-zero \ -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null \ -fsanitize=return -fsanitize=signed-integer-overflow \ @@ -40,7 +40,7 @@ OPTIMIZE=-Os -fno-strict-aliasing LANGUAGE=-std=gnu11 htmldir=man -version=1.7.15 +version=1.7.7 SED=sed USER=$(firstword $(subst :, ,$(shell getent passwd _mandos || getent passwd nobody || echo 65534))) @@ -283,17 +283,15 @@ run-client: all keydir/seckey.txt keydir/pubkey.txt @echo "###################################################################" @echo "# The following error messages are harmless and can be safely #" - @echo "# ignored: #" + @echo "# ignored. The messages are caused by not running as root, but #" + @echo "# you should NOT run \"make run-client\" as root unless you also #" + @echo "# unpacked and compiled Mandos as root, which is NOT recommended. #" @echo "# From plugin-runner: setgid: Operation not permitted #" @echo "# setuid: Operation not permitted #" @echo "# From askpass-fifo: mkfifo: Permission denied #" @echo "# From mandos-client: #" @echo "# Failed to raise privileges: Operation not permitted #" @echo "# Warning: network hook \"*\" exited with status * #" - @echo "# #" - @echo "# (The messages are caused by not running as root, but you should #" - @echo "# NOT run \"make run-client\" as root unless you also unpacked and #" - @echo "# compiled Mandos as root, which is also NOT recommended.) #" @echo "###################################################################" # We set GNOME_KEYRING_CONTROL to block pam_gnome_keyring ./plugin-runner --plugin-dir=plugins.d \ === modified file 'NEWS' --- NEWS 2017-02-23 20:35:20 +0000 +++ NEWS 2016-03-19 22:00:38 +0000 @@ -1,57 +1,6 @@ This NEWS file records noteworthy changes, very tersely. See the manual for detailed information. -Version 1.7.15 (2017-02-23) -* Server -** Bug fix: Respect the mandos.conf "zeroconf" and "restore" options -* Client -** Bug fix in mandos-keygen: Handle backslashes in passphrases - -Version 1.7.14 (2017-01-25) -* Server -** Use "Requisite" instead of "RequisiteOverridable" in systemd - service file. - -Version 1.7.13 (2016-10-08) -* Client -** Minor bug fix: Don't ask for passphrase or fail when generating - keys using GnuPG 2.1 in a chrooted environment. - -Version 1.7.12 (2016-10-05) -* Client -** Bug fix: Don't crash after exit() when using DH parameters file - -Version 1.7.11 (2016-10-01) -* Client -** Security fix: Don't compile with AddressSanitizer -* Server -** Bug fix: Find GnuTLS library when gnutls28-dev is not installed -** Bug fix: Include "Expires" and "Last Checker Status" in mandos-ctl - verbose output -** New option for mandos-ctl: --dump-json - -Version 1.7.10 (2016-06-23) -* Client -** Security fix: restrict permissions of /etc/mandos/plugin-helpers -* Server -** Bug fix: Make the --interface flag work with Python 2.7 when "cc" - is not installed - -Version 1.7.9 (2016-06-22) -* Client -** Do not include intro(8mandos) man page - -Version 1.7.8 (2016-06-21) -* Client -** Include intro(8mandos) man page -** mandos-keygen: Use ECDSA SSH keys by default -** Bug fix: Work with GnuPG 2 when booting (Debian bug #819982) - by copying /usr/bin/gpg-agent into initramfs -* Server -** Bug fix: Work with GnuPG 2 (don't use --no-use-agent option) -** Bug fix: Make the --interface option work when using Python 2.7 - by trying harder to find SO_BINDTODEVICE - Version 1.7.7 (2016-03-19) * Client ** Fix bug in Plymouth client, broken since 1.7.2 === modified file 'TODO' --- TODO 2017-02-22 21:45:35 +0000 +++ TODO 2016-03-19 03:51:23 +0000 @@ -13,26 +13,20 @@ ** TODO [#B] Use getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton() ** TODO [#C] Make start_mandos_communication() take "struct server". ** TODO [#C] --interfaces=regex,eth*,noregex (bridge-utils-interfaces(5)) -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * splashy ** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * usplash (Deprecated) ** TODO [#B] Make it work again ** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * askpass-fifo -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * password-prompt ** TODO [#B] lock stdin (with flock()?) -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * plymouth -** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL * TODO [#B] passdev @@ -41,14 +35,11 @@ *** Hook up stderr of plugins, buffer them, and prepend "Mandos Plugin [plugin name]" ** TODO [#C] use same file name rules as run-parts(8) ** kernel command line option for debug info -** TODO [#A] Restart plugins which exit with EX_TEMPFAIL * mandos (server) ** TODO [#B] --notify-command This would allow the mandos.service to use --notify-command="systemd-notify --pid --ready" -** TODO [#B] python-systemd -*** import systemd.daemon; systemd.daemon.notify() ** TODO [#B] Log level :BUGS: *** TODO /etc/mandos/clients.d/*.conf Watch this directory and add/remove/update clients? @@ -72,7 +63,7 @@ ** TODO Save state periodically to recover better from hard shutdowns ** TODO CheckerCompleted method, deprecate CheckedOK ** TODO Secret Service API? - https://standards.freedesktop.org/secret-service/ + http://standards.freedesktop.org/secret-service/ ** TODO Remove D-Bus interfaces with old domain name :2: ** TODO Remove old string_to_delta format :2: ** TODO http://0pointer.de/blog/projects/stateless.html @@ -85,6 +76,7 @@ * mandos-ctl *** Handle "no D-Bus server" and/or "no Mandos server found" better +*** [#B] --dump option ** TODO Remove old string_to_delta format :2: * TODO mandos-dispatch @@ -119,16 +111,5 @@ * [[http://www.undeadly.org/cgi?action=article&sid=20110530221728][OpenBSD]] -* TODO Use raw public keys (RFC 7250) for TLS communications :2: -** Support for this is planned for GnuTLS version 3.6 - https://gitlab.com/gnutls/gnutls/issues/26 -** Rationale -*** The client key is used both for communication and encryption - Using raw keys in GnuTLS instead uses separate keys for - communication and password decryption. -*** GnuTLS 3.5.9 has deprecated the OpenPGP functions - The functions are still available, but deprecated: - https://gitlab.com/gnutls/gnutls/issues/102 - #+STARTUP: showall === modified file 'common.ent' --- common.ent 2017-02-23 20:35:20 +0000 +++ common.ent 2016-03-19 22:00:38 +0000 @@ -1,3 +1,3 @@ - + === modified file 'debian/changelog' --- debian/changelog 2017-02-23 20:35:20 +0000 +++ debian/changelog 2016-03-19 22:00:38 +0000 @@ -1,77 +1,3 @@ -mandos (1.7.15-1) unstable; urgency=medium - - * New upstream release. - * Upstream release fixes "Seems not to be honoring zeroconf option at - mandos.conf" (Closes: #855589) - * debian/mandos.lintian-overrides (mandos): Add new line - "init.d-script-needs-depends-on-lsb-base etc/init.d/mandos (line 49)". - * debian/copyright: Update copyright year to 2017. - - -- Teddy Hogeborn Thu, 23 Feb 2017 21:29:36 +0100 - -mandos (1.7.14-1) unstable; urgency=medium - - * New upstream release. - * debian/mandos-client.postinst (create_key): Stop GPG agent after - running mandos-keygen. - * debian/control (Package: mandos/Depends): Add "systemd-sysv | lsb-base - (>= 3.0-6)", change "gnupg" to "gnupg2 | gnupg", and change - "libgpgme11-dev" to "libgpgme-dev | libgpgme11-dev". - - -- Teddy Hogeborn Wed, 25 Jan 2017 20:36:03 +0100 - -mandos (1.7.13-1) unstable; urgency=medium - - * New upstream release. - * Fix "fails to install noninteractively" by using the "%no-protection" - statement in the GnuPG batch parameter file. (Closes: #840001) - - -- Teddy Hogeborn Sat, 08 Oct 2016 06:31:07 +0200 - -mandos (1.7.12-1) unstable; urgency=medium - - * New upstream release. - - -- Teddy Hogeborn Wed, 05 Oct 2016 22:06:55 +0200 - -mandos (1.7.11-1) unstable; urgency=high - - * New upstream release. - * debian/control (Source: mandos/Vcs-Bzr): Change to use HTTPS. - (Vcs-Browser): - '' - - - -- Teddy Hogeborn Sat, 01 Oct 2016 16:20:48 +0200 - -mandos (1.7.10-1) unstable; urgency=high - - * New upstream release. - * debian/rules (override_dh_fixperms-arch): Also exclude - "etc/mandos/plugin-helpers" from changes by dh_fixperms. - * debian/mandos-client.postinst: Fix the permissions of - "/etc/mandos/plugin-helpers" for those systems which had a fresh - install of an older version. - - -- Teddy Hogeborn Thu, 23 Jun 2016 22:00:29 +0200 - -mandos (1.7.9-1) unstable; urgency=medium - - * New upstream release. - - -- Teddy Hogeborn Wed, 22 Jun 2016 07:30:12 +0200 - -mandos (1.7.8-1) unstable; urgency=medium - - * New upstream release. - * Fix "bad gpgme_op_decrypt: GPGME: Decryption failed." by copying - /usr/bin/gpg-agent into initramfs (Closes: #819982) - * debian/control (Homepage): Change URL to use HTTPS. - (Standards-Version): Update to 3.9.8. - * debian/copyright (Source): Change URL to HTTPS. - * debian/mandos-client.README.Debian: Change wording to match updated - capabilities. - - -- Teddy Hogeborn Tue, 21 Jun 2016 21:36:10 +0200 - mandos (1.7.7-1) unstable; urgency=medium * New upstream release. === modified file 'debian/control' --- debian/control 2017-01-21 22:22:44 +0000 +++ debian/control 2016-03-23 07:11:22 +0000 @@ -5,14 +5,14 @@ Uploaders: Teddy Hogeborn , Björn Påhlsson Build-Depends: debhelper (>= 9), docbook-xml, docbook-xsl, - libavahi-core-dev, libgpgme-dev | libgpgme11-dev, - libgnutls28-dev (>= 3.3.0) | gnutls-dev (>= 3.3.0), - xsltproc, pkg-config, libnl-route-3-dev + libavahi-core-dev, libgpgme11-dev, libgnutls28-dev (>= 3.3.0) + | gnutls-dev (>= 3.3.0), xsltproc, pkg-config, + libnl-route-3-dev Build-Depends-Indep: systemd, python (>= 2.7), python (<< 3), python-dbus, python-gi -Standards-Version: 3.9.8 -Vcs-Bzr: https://ftp.recompile.se/pub/mandos/trunk -Vcs-Browser: https://bzr.recompile.se/loggerhead/mandos/trunk/files +Standards-Version: 3.9.7 +Vcs-Bzr: http://ftp.recompile.se/pub/mandos/trunk +Vcs-Browser: http://bzr.recompile.se/loggerhead/mandos/trunk/files Homepage: https://www.recompile.se/mandos Package: mandos @@ -20,7 +20,7 @@ Depends: ${misc:Depends}, python (>= 2.7), python (<< 3), libgnutls28-dev (>= 3.3.0) | libgnutls30 (>= 3.3.0), python-dbus, python-gi, avahi-daemon, adduser, python-urwid, - gnupg2 | gnupg, systemd-sysv | lsb-base (>= 3.0-6) + gnupg Recommends: ssh-client | fping Description: server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows === modified file 'debian/copyright' --- debian/copyright 2017-02-23 19:11:11 +0000 +++ debian/copyright 2016-03-23 07:11:22 +0000 @@ -4,8 +4,8 @@ Source: Files: * -Copyright: Copyright © 2008-2017 Teddy Hogeborn - Copyright © 2008-2017 Björn Påhlsson +Copyright: Copyright © 2008-2016 Teddy Hogeborn + Copyright © 2008-2016 Björn Påhlsson License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as === modified file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2016-06-21 19:47:08 +0000 +++ debian/mandos-client.README.Debian 2015-07-12 01:25:14 +0000 @@ -43,9 +43,9 @@ automatically detected. If this should result in incorrect interfaces, edit the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf" file. (The default setting is - empty, meaning it will autodetect the interfaces.) *If* the DEVICE + empty, meaning it will autodetect the interface.) *If* the DEVICE setting is changed, it will be necessary to update the initrd image - by running this command: + by running the command update-initramfs -k all -u @@ -90,7 +90,7 @@ "mandos=connect::" on the kernel command line. - For very advanced users, it is possible to specify simply + For very advanced users, it it possible to specify simply "mandos=connect" on the kernel command line to make the system only set up the network (using the data in the "ip=" option) and not pass any extra "--connect" options to mandos-client at boot. For this to @@ -106,4 +106,4 @@ policy or other reasons, simply replace the existing dhparams.pem file and update the initital RAM disk image. - -- Teddy Hogeborn , Tue, 21 Jun 2016 21:43:49 +0200 + -- Teddy Hogeborn , Sun, 12 Jul 2015 03:24:24 +0200 === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2016-10-09 22:35:41 +0000 +++ debian/mandos-client.postinst 2016-03-19 04:21:00 +0000 @@ -57,7 +57,6 @@ return 0 fi mandos-keygen - gpg-connect-agent KILLAGENT /bye || : } create_dh_params(){ @@ -91,7 +90,7 @@ create_key "$@" create_dh_params "$@" || : update_initramfs "$@" - if dpkg --compare-versions "$2" lt-nl "1.7.10-1"; then + if dpkg --compare-versions "$2" lt-nl "1.7.7-1"; then PLUGINHELPERDIR=/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)/mandos/plugin-helpers if ! dpkg-statoverride --list "$PLUGINHELPERDIR" \ >/dev/null 2>&1; then === modified file 'debian/mandos.lintian-overrides' --- debian/mandos.lintian-overrides 2017-02-21 18:23:54 +0000 +++ debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000 @@ -2,4 +2,3 @@ # it, so it must be kept unreadable for non-root users. # mandos binary: non-standard-file-perm etc/mandos/clients.conf 0600 != 0644 -mandos: init.d-script-needs-depends-on-lsb-base etc/init.d/mandos (line 49) === modified file 'debian/rules' --- debian/rules 2016-06-23 19:46:41 +0000 +++ debian/rules 2016-03-19 03:19:04 +0000 @@ -22,7 +22,6 @@ override_dh_fixperms-arch: dh_fixperms --exclude etc/keys/mandos \ --exclude etc/mandos/plugins.d \ - --exclude etc/mandos/plugin-helpers \ --exclude usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)/mandos/plugins.d \ --exclude usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)/mandos/plugin-helpers \ --exclude usr/share/doc/mandos-client/examples/network-hooks.d === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2017-02-21 22:15:43 +0000 +++ initramfs-tools-hook 2016-03-19 04:26:32 +0000 @@ -142,7 +142,7 @@ for conf in /etc/initramfs-tools/conf.d/*; do if [ -n `basename \"$conf\" | grep '^[[:alnum:]][[:alnum:]\._-]*$' \ | grep -v '\.dpkg-.*$'` ]; then - [ -f "${conf}" ] && . "${conf}" + [ -f ${conf} ] && . ${conf} fi done export DEVICE @@ -157,7 +157,7 @@ if [ -x "$hook" ]; then # Copy any files needed by the network hook MANDOSNETHOOKDIR=/etc/mandos/network-hooks.d MODE=files \ - VERBOSITY=0 "$hook" files | while read -r file target; do + VERBOSITY=0 "$hook" files | while read file target; do if [ ! -e "${file}" ]; then echo "WARNING: file ${file} not found, requested by Mandos network hook '${hook##*/}'" >&2 fi @@ -169,8 +169,10 @@ done # Copy and load any modules needed by the network hook MANDOSNETHOOKDIR=/etc/mandos/network-hooks.d MODE=modules \ - VERBOSITY=0 "$hook" modules | while read -r module; do - force_load "$module" + VERBOSITY=0 "$hook" modules | while read module; do + if [ -z "${target}" ]; then + force_load "$module" + fi done fi done @@ -184,11 +186,6 @@ copy_exec /usr/bin/gpgconf fi gpg="`/usr/bin/gpgconf|sed --quiet --expression='s/^gpg:[^:]*://p'`" - gpgagent="`/usr/bin/gpgconf|sed --quiet --expression='s/^gpg-agent:[^:]*://p'`" - # Newer versions of GnuPG 2 requires the gpg-agent binary - if [ -e "$gpgagent" ] && [ ! -e "${DESTDIR}$gpgagent" ]; then - copy_exec "$gpgagent" - fi fi elif dpkg --compare-versions "$libgpgme11_version" ge 1.4.1-0.1; then gpg=/usr/bin/gpg2 === modified file 'initramfs-tools-script' --- initramfs-tools-script 2017-02-21 21:42:08 +0000 +++ initramfs-tools-script 2016-03-02 16:45:38 +0000 @@ -57,7 +57,7 @@ # Get DEVICE from /conf/initramfs.conf and other files . /conf/initramfs.conf for conf in /conf/conf.d/*; do - [ -f "${conf}" ] && . "${conf}" + [ -f ${conf} ] && . ${conf} done if [ -e /conf/param.conf ]; then . /conf/param.conf @@ -115,7 +115,7 @@ # parse /conf/conf.d/cryptroot. Format: # target=sda2_crypt,source=/dev/sda2,key=none,keyscript=/foo/bar/baz exec 3>/conf/conf.d/cryptroot.mandos -while read -r options; do +while read options; do newopts="" # Split option line on commas old_ifs="$IFS" === modified file 'intro.xml' --- intro.xml 2017-02-23 19:11:11 +0000 +++ intro.xml 2016-03-23 07:11:22 +0000 @@ -1,7 +1,7 @@ + %common; ]> @@ -36,7 +36,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -78,8 +77,6 @@ INTRODUCTION - You know how it is. You’ve heard of it happening. The Man comes and takes away your servers, your friends’ servers, the servers of everybody in the same hosting facility. The servers === modified file 'mandos' --- mandos 2017-02-23 20:35:20 +0000 +++ mandos 2016-03-19 22:00:38 +0000 @@ -1,19 +1,19 @@ #!/usr/bin/python # -*- mode: python; coding: utf-8 -*- -# +# # Mandos server - give out binary blobs to connecting clients. -# +# # This program is partly derived from an example program for an Avahi # service publisher, downloaded from # . This includes the # methods "add", "remove", "server_state_changed", # "entry_group_state_changed", "cleanup", and "activate" in the # "AvahiService" class, and some lines in "main". -# +# # Everything else is -# Copyright © 2008-2017 Teddy Hogeborn -# Copyright © 2008-2017 Björn Påhlsson -# +# Copyright © 2008-2016 Teddy Hogeborn +# Copyright © 2008-2016 Björn Påhlsson +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -23,13 +23,13 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see # . -# +# # Contact the authors at . -# +# from __future__ import (division, absolute_import, print_function, unicode_literals) @@ -86,35 +86,18 @@ import xml.dom.minidom import inspect -# Try to find the value of SO_BINDTODEVICE: try: - # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and - # newer, and it is also the most natural place for it: SO_BINDTODEVICE = socket.SO_BINDTODEVICE except AttributeError: try: - # This is where SO_BINDTODEVICE was up to and including Python - # 2.6, and also 3.2: from IN import SO_BINDTODEVICE except ImportError: - # In Python 2.7 it seems to have been removed entirely. - # Try running the C preprocessor: - try: - cc = subprocess.Popen(["cc", "--language=c", "-E", - "/dev/stdin"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - stdout = cc.communicate( - "#include \nSO_BINDTODEVICE\n")[0] - SO_BINDTODEVICE = int(stdout.splitlines()[-1]) - except (OSError, ValueError, IndexError): - # No value found - SO_BINDTODEVICE = None + SO_BINDTODEVICE = None if sys.version_info.major == 2: str = unicode -version = "1.7.15" +version = "1.7.7" stored_state_file = "clients.pickle" logger = logging.getLogger() @@ -124,7 +107,7 @@ if_nametoindex = ctypes.cdll.LoadLibrary( ctypes.util.find_library("c")).if_nametoindex except (OSError, AttributeError): - + def if_nametoindex(interface): "Get an interface index the hard way, i.e. using fcntl()" SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h @@ -153,16 +136,16 @@ def initlogger(debug, level=logging.WARNING): """init logger and add loglevel""" - + global syslogger syslogger = (logging.handlers.SysLogHandler( - facility=logging.handlers.SysLogHandler.LOG_DAEMON, - address="/dev/log")) + facility = logging.handlers.SysLogHandler.LOG_DAEMON, + address = "/dev/log")) syslogger.setFormatter(logging.Formatter ('Mandos [%(process)d]: %(levelname)s:' ' %(message)s')) logger.addHandler(syslogger) - + if debug: console = logging.StreamHandler() console.setFormatter(logging.Formatter('%(asctime)s %(name)s' @@ -180,7 +163,7 @@ class PGPEngine(object): """A simple class for OpenPGP symmetric encryption & decryption""" - + def __init__(self): self.tempdir = tempfile.mkdtemp(prefix="mandos-") self.gpg = "gpg" @@ -197,26 +180,24 @@ self.gnupgargs = ['--batch', '--homedir', self.tempdir, '--force-mdc', - '--quiet'] - # Only GPG version 1 has the --no-use-agent option. - if self.gpg == "gpg" or self.gpg.endswith("/gpg"): - self.gnupgargs.append("--no-use-agent") - + '--quiet', + '--no-use-agent'] + def __enter__(self): return self - + def __exit__(self, exc_type, exc_value, traceback): self._cleanup() return False - + def __del__(self): self._cleanup() - + def _cleanup(self): if self.tempdir is not None: # Delete contents of tempdir for root, dirs, files in os.walk(self.tempdir, - topdown=False): + topdown = False): for filename in files: os.remove(os.path.join(root, filename)) for dirname in dirs: @@ -224,7 +205,7 @@ # Remove tempdir os.rmdir(self.tempdir) self.tempdir = None - + def password_encode(self, password): # Passphrase can not be empty and can not contain newlines or # NUL bytes. So we prefix it and hex encode it. @@ -235,7 +216,7 @@ .replace(b"\n", b"\\n") .replace(b"\0", b"\\x00")) return encoded - + def encrypt(self, data, password): passphrase = self.password_encode(password) with tempfile.NamedTemporaryFile( @@ -246,60 +227,57 @@ '--passphrase-file', passfile.name] + self.gnupgargs, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - ciphertext, err = proc.communicate(input=data) + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE) + ciphertext, err = proc.communicate(input = data) if proc.returncode != 0: raise PGPError(err) return ciphertext - + def decrypt(self, data, password): passphrase = self.password_encode(password) with tempfile.NamedTemporaryFile( - dir=self.tempdir) as passfile: + dir = self.tempdir) as passfile: passfile.write(passphrase) passfile.flush() proc = subprocess.Popen([self.gpg, '--decrypt', '--passphrase-file', passfile.name] + self.gnupgargs, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - decrypted_plaintext, err = proc.communicate(input=data) + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE) + decrypted_plaintext, err = proc.communicate(input = data) if proc.returncode != 0: raise PGPError(err) return decrypted_plaintext - # Pretend that we have an Avahi module class Avahi(object): """This isn't so much a class as it is a module-like namespace. It is instantiated once, and simulates having an Avahi module.""" - IF_UNSPEC = -1 # avahi-common/address.h - PROTO_UNSPEC = -1 # avahi-common/address.h - PROTO_INET = 0 # avahi-common/address.h - PROTO_INET6 = 1 # avahi-common/address.h + IF_UNSPEC = -1 # avahi-common/address.h + PROTO_UNSPEC = -1 # avahi-common/address.h + PROTO_INET = 0 # avahi-common/address.h + PROTO_INET6 = 1 # avahi-common/address.h DBUS_NAME = "org.freedesktop.Avahi" DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup" DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server" DBUS_PATH_SERVER = "/" - def string_array_to_txt_array(self, t): return dbus.Array((dbus.ByteArray(s.encode("utf-8")) for s in t), signature="ay") - ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h - ENTRY_GROUP_COLLISION = 3 # avahi-common/defs.h - ENTRY_GROUP_FAILURE = 4 # avahi-common/defs.h - SERVER_INVALID = 0 # avahi-common/defs.h - SERVER_REGISTERING = 1 # avahi-common/defs.h - SERVER_RUNNING = 2 # avahi-common/defs.h - SERVER_COLLISION = 3 # avahi-common/defs.h - SERVER_FAILURE = 4 # avahi-common/defs.h + ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h + ENTRY_GROUP_COLLISION = 3 # avahi-common/defs.h + ENTRY_GROUP_FAILURE = 4 # avahi-common/defs.h + SERVER_INVALID = 0 # avahi-common/defs.h + SERVER_REGISTERING = 1 # avahi-common/defs.h + SERVER_RUNNING = 2 # avahi-common/defs.h + SERVER_COLLISION = 3 # avahi-common/defs.h + SERVER_FAILURE = 4 # avahi-common/defs.h avahi = Avahi() - class AvahiError(Exception): def __init__(self, value, *args, **kwargs): self.value = value @@ -317,7 +295,7 @@ class AvahiService(object): """An Avahi (Zeroconf) service. - + Attributes: interface: integer; avahi.IF_UNSPEC or an interface index. Used to optionally bind to the specified interface. @@ -335,18 +313,18 @@ server: D-Bus Server bus: dbus.SystemBus() """ - + def __init__(self, - interface=avahi.IF_UNSPEC, - name=None, - servicetype=None, - port=None, - TXT=None, - domain="", - host="", - max_renames=32768, - protocol=avahi.PROTO_UNSPEC, - bus=None): + interface = avahi.IF_UNSPEC, + name = None, + servicetype = None, + port = None, + TXT = None, + domain = "", + host = "", + max_renames = 32768, + protocol = avahi.PROTO_UNSPEC, + bus = None): self.interface = interface self.name = name self.type = servicetype @@ -361,7 +339,7 @@ self.server = None self.bus = bus self.entry_group_state_changed_match = None - + def rename(self, remove=True): """Derived from the Avahi example code""" if self.rename_count >= self.max_renames: @@ -387,7 +365,7 @@ logger.critical("D-Bus Exception", exc_info=error) self.cleanup() os._exit(1) - + def remove(self): """Derived from the Avahi example code""" if self.entry_group_state_changed_match is not None: @@ -395,7 +373,7 @@ self.entry_group_state_changed_match = None if self.group is not None: self.group.Reset() - + def add(self): """Derived from the Avahi example code""" self.remove() @@ -418,11 +396,11 @@ dbus.UInt16(self.port), avahi.string_array_to_txt_array(self.TXT)) self.group.Commit() - + def entry_group_state_changed(self, state, error): """Derived from the Avahi example code""" logger.debug("Avahi entry group state change: %i", state) - + if state == avahi.ENTRY_GROUP_ESTABLISHED: logger.debug("Zeroconf service established.") elif state == avahi.ENTRY_GROUP_COLLISION: @@ -432,7 +410,7 @@ logger.critical("Avahi: Error in group state changed %s", str(error)) raise AvahiGroupError("State changed: {!s}".format(error)) - + def cleanup(self): """Derived from the Avahi example code""" if self.group is not None: @@ -443,7 +421,7 @@ pass self.group = None self.remove() - + def server_state_changed(self, state, error=None): """Derived from the Avahi example code""" logger.debug("Avahi server state change: %i", state) @@ -478,7 +456,7 @@ logger.debug("Unknown state: %r", state) else: logger.debug("Unknown state: %r: %r", state, error) - + def activate(self): """Derived from the Avahi example code""" if self.server is None: @@ -501,29 +479,25 @@ .format(self.name))) return ret - # Pretend that we have a GnuTLS module class GnuTLS(object): """This isn't so much a class as it is a module-like namespace. It is instantiated once, and simulates having a GnuTLS module.""" - - library = ctypes.util.find_library("gnutls") - if library is None: - library = ctypes.util.find_library("gnutls-deb0") - _library = ctypes.cdll.LoadLibrary(library) - del library + + _library = ctypes.cdll.LoadLibrary( + ctypes.util.find_library("gnutls")) _need_version = b"3.3.0" - def __init__(self): - # Need to use "self" here, since this method is called before - # the assignment to the "gnutls" global variable happens. - if self.check_version(self._need_version) is None: - raise self.Error("Needs GnuTLS {} or later" - .format(self._need_version)) - + # Need to use class name "GnuTLS" here, since this method is + # called before the assignment to the "gnutls" global variable + # happens. + if GnuTLS.check_version(self._need_version) is None: + raise GnuTLS.Error("Needs GnuTLS {} or later" + .format(self._need_version)) + # Unless otherwise indicated, the constants and types below are # all from the gnutls/gnutls.h C header file. - + # Constants E_SUCCESS = 0 E_INTERRUPTED = -52 @@ -534,38 +508,35 @@ CRD_CERTIFICATE = 1 E_NO_CERTIFICATE_FOUND = -49 OPENPGP_FMT_RAW = 0 # gnutls/openpgp.h - + # Types class session_int(ctypes.Structure): _fields_ = [] session_t = ctypes.POINTER(session_int) - class certificate_credentials_st(ctypes.Structure): _fields_ = [] certificate_credentials_t = ctypes.POINTER( certificate_credentials_st) certificate_type_t = ctypes.c_int - class datum_t(ctypes.Structure): _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)), ('size', ctypes.c_uint)] - class openpgp_crt_int(ctypes.Structure): _fields_ = [] openpgp_crt_t = ctypes.POINTER(openpgp_crt_int) - openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h + openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) credentials_type_t = ctypes.c_int transport_ptr_t = ctypes.c_void_p close_request_t = ctypes.c_int - + # Exceptions class Error(Exception): # We need to use the class name "GnuTLS" here, since this # exception might be raised from within GnuTLS.__init__, # which is called before the assignment to the "gnutls" # global variable has happened. - def __init__(self, message=None, code=None, args=()): + def __init__(self, message = None, code = None, args=()): # Default usage is by a message string, but if a return # code is passed, convert it to a string with # gnutls.strerror() @@ -574,10 +545,10 @@ message = GnuTLS.strerror(code) return super(GnuTLS.Error, self).__init__( message, *args) - + class CertificateSecurityError(Error): pass - + # Classes class Credentials(object): def __init__(self): @@ -585,12 +556,12 @@ gnutls.certificate_allocate_credentials( ctypes.byref(self._c_object)) self.type = gnutls.CRD_CERTIFICATE - + def __del__(self): gnutls.certificate_free_credentials(self._c_object) - + class ClientSession(object): - def __init__(self, socket, credentials=None): + def __init__(self, socket, credentials = None): self._c_object = gnutls.session_t() gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT) gnutls.set_default_priority(self._c_object) @@ -604,13 +575,13 @@ ctypes.cast(credentials._c_object, ctypes.c_void_p)) self.credentials = credentials - + def __del__(self): gnutls.deinit(self._c_object) - + def handshake(self): return gnutls.handshake(self._c_object) - + def send(self, data): data = bytes(data) data_len = len(data) @@ -618,10 +589,10 @@ data_len -= gnutls.record_send(self._c_object, data[-data_len:], data_len) - + def bye(self): return gnutls.bye(self._c_object, gnutls.SHUT_RDWR) - + # Error handling functions def _error_code(result): """A function to raise exceptions on errors, suitable @@ -629,9 +600,9 @@ if result >= 0: return result if result == gnutls.E_NO_CERTIFICATE_FOUND: - raise gnutls.CertificateSecurityError(code=result) - raise gnutls.Error(code=result) - + raise gnutls.CertificateSecurityError(code = result) + raise gnutls.Error(code = result) + def _retry_on_error(result, func, arguments): """A function to retry on some errors, suitable for the 'errcheck' attribute on ctypes functions""" @@ -640,117 +611,116 @@ return _error_code(result) result = func(*arguments) return result - + # Unless otherwise indicated, the function declarations below are # all from the gnutls/gnutls.h C header file. - + # Functions priority_set_direct = _library.gnutls_priority_set_direct priority_set_direct.argtypes = [session_t, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] priority_set_direct.restype = _error_code - + init = _library.gnutls_init init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int] init.restype = _error_code - + set_default_priority = _library.gnutls_set_default_priority set_default_priority.argtypes = [session_t] set_default_priority.restype = _error_code - + record_send = _library.gnutls_record_send record_send.argtypes = [session_t, ctypes.c_void_p, ctypes.c_size_t] record_send.restype = ctypes.c_ssize_t record_send.errcheck = _retry_on_error - + certificate_allocate_credentials = ( _library.gnutls_certificate_allocate_credentials) certificate_allocate_credentials.argtypes = [ ctypes.POINTER(certificate_credentials_t)] certificate_allocate_credentials.restype = _error_code - + certificate_free_credentials = ( _library.gnutls_certificate_free_credentials) - certificate_free_credentials.argtypes = [ - certificate_credentials_t] + certificate_free_credentials.argtypes = [certificate_credentials_t] certificate_free_credentials.restype = None - + handshake_set_private_extensions = ( _library.gnutls_handshake_set_private_extensions) handshake_set_private_extensions.argtypes = [session_t, ctypes.c_int] handshake_set_private_extensions.restype = None - + credentials_set = _library.gnutls_credentials_set credentials_set.argtypes = [session_t, credentials_type_t, ctypes.c_void_p] credentials_set.restype = _error_code - + strerror = _library.gnutls_strerror strerror.argtypes = [ctypes.c_int] strerror.restype = ctypes.c_char_p - + certificate_type_get = _library.gnutls_certificate_type_get certificate_type_get.argtypes = [session_t] certificate_type_get.restype = _error_code - + certificate_get_peers = _library.gnutls_certificate_get_peers certificate_get_peers.argtypes = [session_t, ctypes.POINTER(ctypes.c_uint)] certificate_get_peers.restype = ctypes.POINTER(datum_t) - + global_set_log_level = _library.gnutls_global_set_log_level global_set_log_level.argtypes = [ctypes.c_int] global_set_log_level.restype = None - + global_set_log_function = _library.gnutls_global_set_log_function global_set_log_function.argtypes = [log_func] global_set_log_function.restype = None - + deinit = _library.gnutls_deinit deinit.argtypes = [session_t] deinit.restype = None - + handshake = _library.gnutls_handshake handshake.argtypes = [session_t] handshake.restype = _error_code handshake.errcheck = _retry_on_error - + transport_set_ptr = _library.gnutls_transport_set_ptr transport_set_ptr.argtypes = [session_t, transport_ptr_t] transport_set_ptr.restype = None - + bye = _library.gnutls_bye bye.argtypes = [session_t, close_request_t] bye.restype = _error_code bye.errcheck = _retry_on_error - + check_version = _library.gnutls_check_version check_version.argtypes = [ctypes.c_char_p] check_version.restype = ctypes.c_char_p - + # All the function declarations below are from gnutls/openpgp.h - + openpgp_crt_init = _library.gnutls_openpgp_crt_init openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)] openpgp_crt_init.restype = _error_code - + openpgp_crt_import = _library.gnutls_openpgp_crt_import openpgp_crt_import.argtypes = [openpgp_crt_t, ctypes.POINTER(datum_t), openpgp_crt_fmt_t] openpgp_crt_import.restype = _error_code - + openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)] openpgp_crt_verify_self.restype = _error_code - + openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit openpgp_crt_deinit.argtypes = [openpgp_crt_t] openpgp_crt_deinit.restype = None - + openpgp_crt_get_fingerprint = ( _library.gnutls_openpgp_crt_get_fingerprint) openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t, @@ -758,27 +728,25 @@ ctypes.POINTER( ctypes.c_size_t)] openpgp_crt_get_fingerprint.restype = _error_code - + # Remove non-public functions del _error_code, _retry_on_error # Create the global "gnutls" object, simulating a module gnutls = GnuTLS() - def call_pipe(connection, # : multiprocessing.Connection func, *args, **kwargs): """This function is meant to be called by multiprocessing.Process - + This function runs func(*args, **kwargs), and writes the resulting return value on the provided multiprocessing.Connection. """ connection.send(func(*args, **kwargs)) connection.close() - class Client(object): """A representation of a client host served by this server. - + Attributes: approved: bool(); 'None' if not yet approved/disapproved approval_delay: datetime.timedelta(); Time to wait for approval @@ -821,7 +789,7 @@ disabled, or None server_settings: The server_settings dict from main() """ - + runtime_expansions = ("approval_delay", "approval_duration", "created", "enabled", "expires", "fingerprint", "host", "interval", @@ -838,7 +806,7 @@ "approved_by_default": "True", "enabled": "True", } - + @staticmethod def config_parser(config): """Construct a new dict of client settings of this form: @@ -851,14 +819,14 @@ for client_name in config.sections(): section = dict(config.items(client_name)) client = settings[client_name] = {} - + client["host"] = section["host"] # Reformat values from string types to Python types client["approved_by_default"] = config.getboolean( client_name, "approved_by_default") client["enabled"] = config.getboolean(client_name, "enabled") - + # Uppercase and remove spaces from fingerprint for later # comparison purposes with return value from the # fingerprint() function @@ -888,10 +856,10 @@ client["last_approval_request"] = None client["last_checked_ok"] = None client["last_checker_status"] = -2 - + return settings - - def __init__(self, settings, name=None, server_settings=None): + + def __init__(self, settings, name = None, server_settings=None): self.name = name if server_settings is None: server_settings = {} @@ -899,7 +867,7 @@ # adding all client settings for setting, value in settings.items(): setattr(self, setting, value) - + if self.enabled: if not hasattr(self, "last_enabled"): self.last_enabled = datetime.datetime.utcnow() @@ -909,12 +877,12 @@ else: self.last_enabled = None self.expires = None - + logger.debug("Creating client %r", self.name) logger.debug(" Fingerprint: %s", self.fingerprint) self.created = settings.get("created", datetime.datetime.utcnow()) - + # attributes specific for this server instance self.checker = None self.checker_initiator_tag = None @@ -929,17 +897,17 @@ for attr in self.__dict__.keys() if not attr.startswith("_")] self.client_structure.append("client_structure") - + for name, t in inspect.getmembers( type(self), lambda obj: isinstance(obj, property)): if not name.startswith("_"): self.client_structure.append(name) - + # Send notice to process children that client state has changed def send_changedstate(self): with self.changedstate: self.changedstate.notify_all() - + def enable(self): """Start this client's checker and timeout hooks""" if getattr(self, "enabled", False): @@ -950,7 +918,7 @@ self.last_enabled = datetime.datetime.utcnow() self.init_checker() self.send_changedstate() - + def disable(self, quiet=True): """Disable this client.""" if not getattr(self, "enabled", False): @@ -970,10 +938,10 @@ self.send_changedstate() # Do not run this again if called by a GLib.timeout_add return False - + def __del__(self): self.disable() - + def init_checker(self): # Schedule a new checker to be started an 'interval' from now, # and every interval from then on. @@ -989,7 +957,7 @@ int(self.timeout.total_seconds() * 1000), self.disable) # Also start a new checker *right now*. self.start_checker() - + def checker_callback(self, source, condition, connection, command): """The checker has completed, so take appropriate actions.""" @@ -998,7 +966,7 @@ # Read return code from connection (see call_pipe) returncode = connection.recv() connection.close() - + if returncode >= 0: self.last_checker_status = returncode self.last_checker_signal = None @@ -1014,14 +982,14 @@ logger.warning("Checker for %(name)s crashed?", vars(self)) return False - + def checked_ok(self): """Assert that the client has been seen, alive and well.""" self.last_checked_ok = datetime.datetime.utcnow() self.last_checker_status = 0 self.last_checker_signal = None self.bump_timeout() - + def bump_timeout(self, timeout=None): """Bump up the timeout for this client.""" if timeout is None: @@ -1033,13 +1001,13 @@ self.disable_initiator_tag = GLib.timeout_add( int(timeout.total_seconds() * 1000), self.disable) self.expires = datetime.datetime.utcnow() + timeout - + def need_approval(self): self.last_approval_request = datetime.datetime.utcnow() - + def start_checker(self): """Start a new checker subprocess if one is not running. - + If a checker already exists, leave it running and do nothing.""" # The reason for not killing a running checker is that if we @@ -1050,7 +1018,7 @@ # checkers alone, the checker would have to take more time # than 'timeout' for the client to be disabled, which is as it # should be. - + if self.checker is not None and not self.checker.is_alive(): logger.warning("Checker was not alive; joining") self.checker.join() @@ -1060,7 +1028,7 @@ # Escape attributes for the shell escaped_attrs = { attr: re.escape(str(getattr(self, attr))) - for attr in self.runtime_expansions} + for attr in self.runtime_expansions } try: command = self.checker_command % escaped_attrs except TypeError as error: @@ -1078,25 +1046,25 @@ # The exception is when not debugging but nevertheless # running in the foreground; use the previously # created wnull. - popen_args = {"close_fds": True, - "shell": True, - "cwd": "/"} + popen_args = { "close_fds": True, + "shell": True, + "cwd": "/" } if (not self.server_settings["debug"] and self.server_settings["foreground"]): popen_args.update({"stdout": wnull, - "stderr": wnull}) - pipe = multiprocessing.Pipe(duplex=False) + "stderr": wnull }) + pipe = multiprocessing.Pipe(duplex = False) self.checker = multiprocessing.Process( - target=call_pipe, - args=(pipe[1], subprocess.call, command), - kwargs=popen_args) + target = call_pipe, + args = (pipe[1], subprocess.call, command), + kwargs = popen_args) self.checker.start() self.checker_callback_tag = GLib.io_add_watch( pipe[0].fileno(), GLib.IO_IN, self.checker_callback, pipe[0], command) # Re-run this periodically if run by GLib.timeout_add return True - + def stop_checker(self): """Force the checker process, if any, to stop.""" if self.checker_callback_tag: @@ -1115,10 +1083,10 @@ byte_arrays=False): """Decorators for marking methods of a DBusObjectWithProperties to become properties on the D-Bus. - + The decorated method will be called with no arguments by "Get" and with one argument by "Set". - + The parameters, where they are supported, are the same as dbus.service.method, except there is only "signature", since the type from Get() and the type sent to Set() is the same. @@ -1128,7 +1096,7 @@ if byte_arrays and signature != "ay": raise ValueError("Byte arrays not supported for non-'ay'" " signature {!r}".format(signature)) - + def decorator(func): func._dbus_is_property = True func._dbus_interface = dbus_interface @@ -1137,37 +1105,37 @@ func._dbus_name = func.__name__ if func._dbus_name.endswith("_dbus_property"): func._dbus_name = func._dbus_name[:-14] - func._dbus_get_args_options = {'byte_arrays': byte_arrays} + func._dbus_get_args_options = {'byte_arrays': byte_arrays } return func - + return decorator def dbus_interface_annotations(dbus_interface): """Decorator for marking functions returning interface annotations - + Usage: - + @dbus_interface_annotations("org.example.Interface") def _foo(self): # Function name does not matter return {"org.freedesktop.DBus.Deprecated": "true", "org.freedesktop.DBus.Property.EmitsChangedSignal": "false"} """ - + def decorator(func): func._dbus_is_interface = True func._dbus_interface = dbus_interface func._dbus_name = dbus_interface return func - + return decorator def dbus_annotations(annotations): """Decorator to annotate D-Bus methods, signals or properties Usage: - + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true", "org.freedesktop.DBus.Property." "EmitsChangedSignal": "false"}) @@ -1175,14 +1143,14 @@ access="r") def Property_dbus_property(self): return dbus.Boolean(False) - + See also the DBusObjectWithAnnotations class. """ - + def decorator(func): func._dbus_annotations = annotations return func - + return decorator @@ -1206,21 +1174,21 @@ class DBusObjectWithAnnotations(dbus.service.Object): """A D-Bus object with annotations. - + Classes inheriting from this can use the dbus_annotations decorator to add annotations to methods or signals. """ - + @staticmethod def _is_dbus_thing(thing): """Returns a function testing if an attribute is a D-Bus thing - + If called like _is_dbus_thing("method") it returns a function suitable for use as predicate to inspect.getmembers(). """ return lambda obj: getattr(obj, "_dbus_is_{}".format(thing), False) - + def _get_all_dbus_things(self, thing): """Returns a generator of (name, attribute) pairs """ @@ -1229,21 +1197,21 @@ for cls in self.__class__.__mro__ for name, athing in inspect.getmembers(cls, self._is_dbus_thing(thing))) - + @dbus.service.method(dbus.INTROSPECTABLE_IFACE, - out_signature="s", - path_keyword='object_path', - connection_keyword='connection') + out_signature = "s", + path_keyword = 'object_path', + connection_keyword = 'connection') def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. - + Inserts annotation tags on methods and signals. """ xmlstring = dbus.service.Object.Introspect(self, object_path, connection) try: document = xml.dom.minidom.parseString(xmlstring) - + for if_tag in document.getElementsByTagName("interface"): # Add annotation tags for typ in ("method", "signal"): @@ -1276,7 +1244,7 @@ if_tag.appendChild(ann_tag) # Fix argument name for the Introspect method itself if (if_tag.getAttribute("name") - == dbus.INTROSPECTABLE_IFACE): + == dbus.INTROSPECTABLE_IFACE): for cn in if_tag.getElementsByTagName("method"): if cn.getAttribute("name") == "Introspect": for arg in cn.getElementsByTagName("arg"): @@ -1295,12 +1263,12 @@ class DBusObjectWithProperties(DBusObjectWithAnnotations): """A D-Bus object with properties. - + Classes inheriting from this can use the dbus_service_property decorator to expose methods as D-Bus properties. It exposes the standard Get(), Set(), and GetAll() methods on the D-Bus. """ - + def _get_dbus_property(self, interface_name, property_name): """Returns a bound method if one exists which is a D-Bus property with the specified name and interface. @@ -1311,11 +1279,11 @@ if (value._dbus_name == property_name and value._dbus_interface == interface_name): return value.__get__(self) - + # No such property raise DBusPropertyNotFound("{}:{}.{}".format( self.dbus_object_path, interface_name, property_name)) - + @classmethod def _get_all_interface_names(cls): """Get a sequence of all interfaces supported by an object""" @@ -1324,7 +1292,7 @@ for x in (inspect.getmro(cls)) for attr in dir(x)) if name is not None) - + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss", out_signature="v") @@ -1338,7 +1306,7 @@ if not hasattr(value, "variant_level"): return value return type(value)(value, variant_level=value.variant_level+1) - + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv") def Set(self, interface_name, property_name, value): """Standard D-Bus property Set() method, see D-Bus standard. @@ -1356,14 +1324,14 @@ value = dbus.ByteArray(b''.join(chr(byte) for byte in value)) prop(value) - + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s", out_signature="a{sv}") def GetAll(self, interface_name): """Standard D-Bus property GetAll() method, see D-Bus standard. - + Note: Will not include properties with access="write". """ properties = {} @@ -1380,9 +1348,9 @@ properties[name] = value continue properties[name] = type(value)( - value, variant_level=value.variant_level + 1) + value, variant_level = value.variant_level + 1) return dbus.Dictionary(properties, signature="sv") - + @dbus.service.signal(dbus.PROPERTIES_IFACE, signature="sa{sv}as") def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties): @@ -1390,14 +1358,14 @@ standard. """ pass - + @dbus.service.method(dbus.INTROSPECTABLE_IFACE, out_signature="s", path_keyword='object_path', connection_keyword='connection') def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. - + Inserts property tags and interface annotation tags. """ xmlstring = DBusObjectWithAnnotations.Introspect(self, @@ -1405,14 +1373,14 @@ connection) try: document = xml.dom.minidom.parseString(xmlstring) - + def make_tag(document, name, prop): e = document.createElement("property") e.setAttribute("name", name) e.setAttribute("type", prop._dbus_signature) e.setAttribute("access", prop._dbus_access) return e - + for if_tag in document.getElementsByTagName("interface"): # Add property tags for tag in (make_tag(document, name, prop) @@ -1460,46 +1428,44 @@ exc_info=error) return xmlstring - try: dbus.OBJECT_MANAGER_IFACE except AttributeError: dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager" - class DBusObjectWithObjectManager(DBusObjectWithAnnotations): """A D-Bus object with an ObjectManager. - + Classes inheriting from this exposes the standard GetManagedObjects call and the InterfacesAdded and InterfacesRemoved signals on the standard "org.freedesktop.DBus.ObjectManager" interface. - + Note: No signals are sent automatically; they must be sent manually. """ @dbus.service.method(dbus.OBJECT_MANAGER_IFACE, - out_signature="a{oa{sa{sv}}}") + out_signature = "a{oa{sa{sv}}}") def GetManagedObjects(self): """This function must be overridden""" raise NotImplementedError() - + @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, - signature="oa{sa{sv}}") + signature = "oa{sa{sv}}") def InterfacesAdded(self, object_path, interfaces_and_properties): pass - - @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature="oas") + + @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas") def InterfacesRemoved(self, object_path, interfaces): pass - + @dbus.service.method(dbus.INTROSPECTABLE_IFACE, - out_signature="s", - path_keyword='object_path', - connection_keyword='connection') + out_signature = "s", + path_keyword = 'object_path', + connection_keyword = 'connection') def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. - + Override return argument name of GetManagedObjects to be "objpath_interfaces_and_properties" """ @@ -1508,11 +1474,11 @@ connection) try: document = xml.dom.minidom.parseString(xmlstring) - + for if_tag in document.getElementsByTagName("interface"): # Fix argument name for the GetManagedObjects method if (if_tag.getAttribute("name") - == dbus.OBJECT_MANAGER_IFACE): + == dbus.OBJECT_MANAGER_IFACE): for cn in if_tag.getElementsByTagName("method"): if (cn.getAttribute("name") == "GetManagedObjects"): @@ -1528,14 +1494,13 @@ except (AttributeError, xml.dom.DOMException, xml.parsers.expat.ExpatError) as error: logger.error("Failed to override Introspection method", - exc_info=error) + exc_info = error) return xmlstring - def datetime_to_dbus(dt, variant_level=0): """Convert a UTC datetime.datetime() to a D-Bus type.""" if dt is None: - return dbus.String("", variant_level=variant_level) + return dbus.String("", variant_level = variant_level) return dbus.String(dt.isoformat(), variant_level=variant_level) @@ -1544,25 +1509,25 @@ dbus.service.Object, it will add alternate D-Bus attributes with interface names according to the "alt_interface_names" mapping. Usage: - + @alternate_dbus_interfaces({"org.example.Interface": "net.example.AlternateInterface"}) class SampleDBusObject(dbus.service.Object): @dbus.service.method("org.example.Interface") def SampleDBusMethod(): pass - + The above "SampleDBusMethod" on "SampleDBusObject" will be reachable via two interfaces: "org.example.Interface" and "net.example.AlternateInterface", the latter of which will have its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to "true", unless "deprecate" is passed with a False value. - + This works for methods and signals, and also for D-Bus properties (from DBusObjectWithProperties) and interfaces (from the dbus_interface_annotations decorator). """ - + def wrapper(cls): for orig_interface_name, alt_interface_name in ( alt_interface_names.items()): @@ -1608,7 +1573,6 @@ attribute._dbus_annotations) except AttributeError: pass - # Define a creator of a function to call both the # original and alternate functions, so both the # original and alternate signals gets sent when @@ -1617,19 +1581,18 @@ """This function is a scope container to pass func1 and func2 to the "call_both" function outside of its arguments""" - + @functools.wraps(func2) def call_both(*args, **kwargs): """This function will emit two D-Bus signals by calling func1 and func2""" func1(*args, **kwargs) func2(*args, **kwargs) - # Make wrapper function look like a D-Bus - # signal + # Make wrapper function look like a D-Bus signal for name, attr in inspect.getmembers(func2): if name.startswith("_dbus_"): setattr(call_both, name, attr) - + return call_both # Create the "call_both" function and add it to # the class @@ -1681,13 +1644,13 @@ (copy_function(attribute))) if deprecate: # Deprecate all alternate interfaces - iname = "_AlternateDBusNames_interface_annotation{}" + iname="_AlternateDBusNames_interface_annotation{}" for interface_name in interface_names: - + @dbus_interface_annotations(interface_name) def func(self): - return {"org.freedesktop.DBus.Deprecated": - "true"} + return { "org.freedesktop.DBus.Deprecated": + "true" } # Find an unused name for aname in (iname.format(i) for i in itertools.count()): @@ -1704,7 +1667,7 @@ cls = type("{}Alternate".format(cls.__name__), (cls, ), attr) return cls - + return wrapper @@ -1712,20 +1675,20 @@ "se.bsnet.fukt.Mandos"}) class ClientDBus(Client, DBusObjectWithProperties): """A Client class using D-Bus - + Attributes: dbus_object_path: dbus.ObjectPath bus: dbus.SystemBus() """ - + runtime_expansions = (Client.runtime_expansions + ("dbus_object_path", )) - + _interface = "se.recompile.Mandos.Client" - + # dbus.service.Object doesn't use super(), so we can't either. - - def __init__(self, bus=None, *args, **kwargs): + + def __init__(self, bus = None, *args, **kwargs): self.bus = bus Client.__init__(self, *args, **kwargs) # Only now, when this client is initialized, can it show up on @@ -1737,7 +1700,7 @@ "/clients/" + client_object_name) DBusObjectWithProperties.__init__(self, self.bus, self.dbus_object_path) - + def notifychangeproperty(transform_func, dbus_name, type_func=lambda x: x, variant_level=1, @@ -1745,7 +1708,7 @@ _interface=_interface): """ Modify a variable so that it's a property which announces its changes to DBus. - + transform_fun: Function that takes a value and a variant_level and transforms it to a D-Bus type. dbus_name: D-Bus name of the variable @@ -1754,7 +1717,7 @@ variant_level: D-Bus variant level. Default: 1 """ attrname = "_{}".format(dbus_name) - + def setter(self, value): if hasattr(self, "dbus_object_path"): if (not hasattr(self, attrname) or @@ -1767,28 +1730,28 @@ else: dbus_value = transform_func( type_func(value), - variant_level=variant_level) + variant_level = variant_level) self.PropertyChanged(dbus.String(dbus_name), dbus_value) self.PropertiesChanged( _interface, - dbus.Dictionary({dbus.String(dbus_name): - dbus_value}), + dbus.Dictionary({ dbus.String(dbus_name): + dbus_value }), dbus.Array()) setattr(self, attrname, value) - + return property(lambda self: getattr(self, attrname), setter) - + expires = notifychangeproperty(datetime_to_dbus, "Expires") approvals_pending = notifychangeproperty(dbus.Boolean, "ApprovalPending", - type_func=bool) + type_func = bool) enabled = notifychangeproperty(dbus.Boolean, "Enabled") last_enabled = notifychangeproperty(datetime_to_dbus, "LastEnabled") checker = notifychangeproperty( dbus.Boolean, "CheckerRunning", - type_func=lambda checker: checker is not None) + type_func = lambda checker: checker is not None) last_checked_ok = notifychangeproperty(datetime_to_dbus, "LastCheckedOK") last_checker_status = notifychangeproperty(dbus.Int16, @@ -1799,26 +1762,26 @@ "ApprovedByDefault") approval_delay = notifychangeproperty( dbus.UInt64, "ApprovalDelay", - type_func=lambda td: td.total_seconds() * 1000) + type_func = lambda td: td.total_seconds() * 1000) approval_duration = notifychangeproperty( dbus.UInt64, "ApprovalDuration", - type_func=lambda td: td.total_seconds() * 1000) + type_func = lambda td: td.total_seconds() * 1000) host = notifychangeproperty(dbus.String, "Host") timeout = notifychangeproperty( dbus.UInt64, "Timeout", - type_func=lambda td: td.total_seconds() * 1000) + type_func = lambda td: td.total_seconds() * 1000) extended_timeout = notifychangeproperty( dbus.UInt64, "ExtendedTimeout", - type_func=lambda td: td.total_seconds() * 1000) + type_func = lambda td: td.total_seconds() * 1000) interval = notifychangeproperty( dbus.UInt64, "Interval", - type_func=lambda td: td.total_seconds() * 1000) + type_func = lambda td: td.total_seconds() * 1000) checker_command = notifychangeproperty(dbus.String, "Checker") secret = notifychangeproperty(dbus.ByteArray, "Secret", invalidate_only=True) - + del notifychangeproperty - + def __del__(self, *args, **kwargs): try: self.remove_from_connection() @@ -1827,7 +1790,7 @@ if hasattr(DBusObjectWithProperties, "__del__"): DBusObjectWithProperties.__del__(self, *args, **kwargs) Client.__del__(self, *args, **kwargs) - + def checker_callback(self, source, condition, connection, command, *args, **kwargs): ret = Client.checker_callback(self, source, condition, @@ -1849,7 +1812,7 @@ | self.last_checker_signal), dbus.String(command)) return ret - + def start_checker(self, *args, **kwargs): old_checker_pid = getattr(self.checker, "pid", None) r = Client.start_checker(self, *args, **kwargs) @@ -1859,42 +1822,42 @@ # Emit D-Bus signal self.CheckerStarted(self.current_checker_command) return r - + def _reset_approved(self): self.approved = None return False - + def approve(self, value=True): self.approved = value GLib.timeout_add(int(self.approval_duration.total_seconds() * 1000), self._reset_approved) self.send_changedstate() - - # D-Bus methods, signals & properties - - # Interfaces - - # Signals - + + ## D-Bus methods, signals & properties + + ## Interfaces + + ## Signals + # CheckerCompleted - signal @dbus.service.signal(_interface, signature="nxs") def CheckerCompleted(self, exitcode, waitstatus, command): "D-Bus signal" pass - + # CheckerStarted - signal @dbus.service.signal(_interface, signature="s") def CheckerStarted(self, command): "D-Bus signal" pass - + # PropertyChanged - signal @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.signal(_interface, signature="sv") def PropertyChanged(self, property, value): "D-Bus signal" pass - + # GotSecret - signal @dbus.service.signal(_interface) def GotSecret(self): @@ -1903,65 +1866,65 @@ server to mandos-client """ pass - + # Rejected - signal @dbus.service.signal(_interface, signature="s") def Rejected(self, reason): "D-Bus signal" pass - + # NeedApproval - signal @dbus.service.signal(_interface, signature="tb") def NeedApproval(self, timeout, default): "D-Bus signal" return self.need_approval() - - # Methods - + + ## Methods + # Approve - method @dbus.service.method(_interface, in_signature="b") def Approve(self, value): self.approve(value) - + # CheckedOK - method @dbus.service.method(_interface) def CheckedOK(self): self.checked_ok() - + # Enable - method @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface) def Enable(self): "D-Bus method" self.enable() - + # StartChecker - method @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface) def StartChecker(self): "D-Bus method" self.start_checker() - + # Disable - method @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface) def Disable(self): "D-Bus method" self.disable() - + # StopChecker - method @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface) def StopChecker(self): self.stop_checker() - - # Properties - + + ## Properties + # ApprovalPending - property @dbus_service_property(_interface, signature="b", access="read") def ApprovalPending_dbus_property(self): return dbus.Boolean(bool(self.approvals_pending)) - + # ApprovedByDefault - property @dbus_service_property(_interface, signature="b", @@ -1970,7 +1933,7 @@ if value is None: # get return dbus.Boolean(self.approved_by_default) self.approved_by_default = bool(value) - + # ApprovalDelay - property @dbus_service_property(_interface, signature="t", @@ -1980,7 +1943,7 @@ return dbus.UInt64(self.approval_delay.total_seconds() * 1000) self.approval_delay = datetime.timedelta(0, 0, 0, value) - + # ApprovalDuration - property @dbus_service_property(_interface, signature="t", @@ -1990,21 +1953,21 @@ return dbus.UInt64(self.approval_duration.total_seconds() * 1000) self.approval_duration = datetime.timedelta(0, 0, 0, value) - + # Name - property @dbus_annotations( {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"}) @dbus_service_property(_interface, signature="s", access="read") def Name_dbus_property(self): return dbus.String(self.name) - + # Fingerprint - property @dbus_annotations( {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"}) @dbus_service_property(_interface, signature="s", access="read") def Fingerprint_dbus_property(self): return dbus.String(self.fingerprint) - + # Host - property @dbus_service_property(_interface, signature="s", @@ -2013,19 +1976,19 @@ if value is None: # get return dbus.String(self.host) self.host = str(value) - + # Created - property @dbus_annotations( {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"}) @dbus_service_property(_interface, signature="s", access="read") def Created_dbus_property(self): return datetime_to_dbus(self.created) - + # LastEnabled - property @dbus_service_property(_interface, signature="s", access="read") def LastEnabled_dbus_property(self): return datetime_to_dbus(self.last_enabled) - + # Enabled - property @dbus_service_property(_interface, signature="b", @@ -2037,7 +2000,7 @@ self.enable() else: self.disable() - + # LastCheckedOK - property @dbus_service_property(_interface, signature="s", @@ -2047,22 +2010,22 @@ self.checked_ok() return return datetime_to_dbus(self.last_checked_ok) - + # LastCheckerStatus - property @dbus_service_property(_interface, signature="n", access="read") def LastCheckerStatus_dbus_property(self): return dbus.Int16(self.last_checker_status) - + # Expires - property @dbus_service_property(_interface, signature="s", access="read") def Expires_dbus_property(self): return datetime_to_dbus(self.expires) - + # LastApprovalRequest - property @dbus_service_property(_interface, signature="s", access="read") def LastApprovalRequest_dbus_property(self): return datetime_to_dbus(self.last_approval_request) - + # Timeout - property @dbus_service_property(_interface, signature="t", @@ -2087,7 +2050,7 @@ self.disable_initiator_tag = GLib.timeout_add( int((self.expires - now).total_seconds() * 1000), self.disable) - + # ExtendedTimeout - property @dbus_service_property(_interface, signature="t", @@ -2097,7 +2060,7 @@ return dbus.UInt64(self.extended_timeout.total_seconds() * 1000) self.extended_timeout = datetime.timedelta(0, 0, 0, value) - + # Interval - property @dbus_service_property(_interface, signature="t", @@ -2113,8 +2076,8 @@ GLib.source_remove(self.checker_initiator_tag) self.checker_initiator_tag = GLib.timeout_add( value, self.start_checker) - self.start_checker() # Start one now, too - + self.start_checker() # Start one now, too + # Checker - property @dbus_service_property(_interface, signature="s", @@ -2123,7 +2086,7 @@ if value is None: # get return dbus.String(self.checker_command) self.checker_command = str(value) - + # CheckerRunning - property @dbus_service_property(_interface, signature="b", @@ -2135,15 +2098,15 @@ self.start_checker() else: self.stop_checker() - + # ObjectPath - property @dbus_annotations( {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const", "org.freedesktop.DBus.Deprecated": "true"}) @dbus_service_property(_interface, signature="o", access="read") def ObjectPath_dbus_property(self): - return self.dbus_object_path # is already a dbus.ObjectPath - + return self.dbus_object_path # is already a dbus.ObjectPath + # Secret = property @dbus_annotations( {"org.freedesktop.DBus.Property.EmitsChangedSignal": @@ -2154,7 +2117,7 @@ byte_arrays=True) def Secret_dbus_property(self, value): self.secret = bytes(value) - + del _interface @@ -2164,7 +2127,7 @@ self._pipe.send(('init', fpr, address)) if not self._pipe.recv(): raise KeyError(fpr) - + def __getattribute__(self, name): if name == '_pipe': return super(ProxyClient, self).__getattribute__(name) @@ -2173,13 +2136,13 @@ if data[0] == 'data': return data[1] if data[0] == 'function': - + def func(*args, **kwargs): self._pipe.send(('funcall', name, args, kwargs)) return self._pipe.recv()[1] - + return func - + def __setattr__(self, name, value): if name == '_pipe': return super(ProxyClient, self).__setattr__(name, value) @@ -2188,31 +2151,30 @@ class ClientHandler(socketserver.BaseRequestHandler, object): """A class to handle client connections. - + Instantiated once for each connection to handle it. Note: This will run in its own forked process.""" - + def handle(self): with contextlib.closing(self.server.child_pipe) as child_pipe: logger.info("TCP connection from: %s", str(self.client_address)) logger.debug("Pipe FD: %d", self.server.child_pipe.fileno()) - + session = gnutls.ClientSession(self.request) - - # priority = ':'.join(("NONE", "+VERS-TLS1.1", - # "+AES-256-CBC", "+SHA1", - # "+COMP-NULL", "+CTYPE-OPENPGP", - # "+DHE-DSS")) + + #priority = ':'.join(("NONE", "+VERS-TLS1.1", + # "+AES-256-CBC", "+SHA1", + # "+COMP-NULL", "+CTYPE-OPENPGP", + # "+DHE-DSS")) # Use a fallback default, since this MUST be set. priority = self.server.gnutls_priority if priority is None: priority = "NORMAL" - gnutls.priority_set_direct(session._c_object, - priority.encode("utf-8"), + gnutls.priority_set_direct(session._c_object, priority, None) - + # Start communication using the Mandos protocol # Get protocol number line = self.request.makefile().readline() @@ -2223,7 +2185,7 @@ except (ValueError, IndexError, RuntimeError) as error: logger.error("Unknown protocol version: %s", error) return - + # Start GnuTLS connection try: session.handshake() @@ -2233,7 +2195,7 @@ # established. Just abandon the request. return logger.debug("Handshake succeeded") - + approval_required = False try: try: @@ -2243,18 +2205,18 @@ logger.warning("Bad certificate: %s", error) return logger.debug("Fingerprint: %s", fpr) - + try: client = ProxyClient(child_pipe, fpr, self.client_address) except KeyError: return - + if client.approval_delay: delay = client.approval_delay client.approvals_pending += 1 approval_required = True - + while True: if not client.enabled: logger.info("Client %s is disabled", @@ -2263,9 +2225,9 @@ # Emit D-Bus signal client.Rejected("Disabled") return - + if client.approved or not client.approval_delay: - # We are approved or approval is disabled + #We are approved or approval is disabled break elif client.approved is None: logger.info("Client %s needs approval", @@ -2282,8 +2244,8 @@ # Emit D-Bus signal client.Rejected("Denied") return - - # wait until timeout or approved + + #wait until timeout or approved time = datetime.datetime.now() client.changedstate.acquire() client.changedstate.wait(delay.total_seconds()) @@ -2302,21 +2264,21 @@ break else: delay -= time2 - time - + try: session.send(client.secret) except gnutls.Error as error: logger.warning("gnutls send failed", - exc_info=error) + exc_info = error) return - + logger.info("Sending secret to %s", client.name) # bump the timeout using extended_timeout client.bump_timeout(client.extended_timeout) if self.server.use_dbus: # Emit D-Bus signal client.GotSecret() - + finally: if approval_required: client.approvals_pending -= 1 @@ -2325,7 +2287,7 @@ except gnutls.Error as error: logger.warning("GnuTLS bye failed", exc_info=error) - + @staticmethod def peer_certificate(session): "Return the peer's OpenPGP certificate as a bytestring" @@ -2343,7 +2305,7 @@ return None cert = cert_list[0] return ctypes.string_at(cert.data, cert.size) - + @staticmethod def fingerprint(openpgp): "Convert an OpenPGP bytestring to a hexdigit fingerprint" @@ -2382,37 +2344,37 @@ class MultiprocessingMixIn(object): """Like socketserver.ThreadingMixIn, but with multiprocessing""" - + def sub_process_main(self, request, address): try: self.finish_request(request, address) except Exception: self.handle_error(request, address) self.close_request(request) - + def process_request(self, request, address): """Start a new process to process the request.""" - proc = multiprocessing.Process(target=self.sub_process_main, - args=(request, address)) + proc = multiprocessing.Process(target = self.sub_process_main, + args = (request, address)) proc.start() return proc class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object): """ adds a pipe to the MixIn """ - + def process_request(self, request, client_address): """Overrides and wraps the original process_request(). - + This function creates a new pipe in self.pipe """ parent_pipe, self.child_pipe = multiprocessing.Pipe() - + proc = MultiprocessingMixIn.process_request(self, request, client_address) self.child_pipe.close() self.add_pipe(parent_pipe, proc) - + def add_pipe(self, parent_pipe, proc): """Dummy function; override as necessary""" raise NotImplementedError() @@ -2421,13 +2383,13 @@ class IPv6_TCPServer(MultiprocessingMixInWithPipe, socketserver.TCPServer, object): """IPv6-capable TCP server. Accepts 'None' as address and/or port - + Attributes: enabled: Boolean; whether this server is activated yet interface: None or a network interface name (string) use_ipv6: Boolean; to use IPv6 or not """ - + def __init__(self, server_address, RequestHandlerClass, interface=None, use_ipv6=True, @@ -2443,13 +2405,12 @@ self.socketfd = socketfd # Save the original socket.socket() function self.socket_socket = socket.socket - # To implement --socket, we monkey patch socket.socket. - # + # # (When socketserver.TCPServer is a new-style class, we # could make self.socket into a property instead of monkey # patching socket.socket.) - # + # # Create a one-time-only replacement for socket.socket() @functools.wraps(socket.socket) def socket_wrapper(*args, **kwargs): @@ -2467,42 +2428,41 @@ # socket_wrapper(), if socketfd was set. socketserver.TCPServer.__init__(self, server_address, RequestHandlerClass) - + def server_bind(self): """This overrides the normal server_bind() function to bind to an interface if one was specified, and also NOT to bind to an address or port if they were not specified.""" - global SO_BINDTODEVICE if self.interface is not None: if SO_BINDTODEVICE is None: - # Fall back to a hard-coded value which seems to be - # common enough. - logger.warning("SO_BINDTODEVICE not found, trying 25") - SO_BINDTODEVICE = 25 - try: - self.socket.setsockopt( - socket.SOL_SOCKET, SO_BINDTODEVICE, - (self.interface + "\0").encode("utf-8")) - except socket.error as error: - if error.errno == errno.EPERM: - logger.error("No permission to bind to" - " interface %s", self.interface) - elif error.errno == errno.ENOPROTOOPT: - logger.error("SO_BINDTODEVICE not available;" - " cannot bind to interface %s", - self.interface) - elif error.errno == errno.ENODEV: - logger.error("Interface %s does not exist," - " cannot bind", self.interface) - else: - raise + logger.error("SO_BINDTODEVICE does not exist;" + " cannot bind to interface %s", + self.interface) + else: + try: + self.socket.setsockopt( + socket.SOL_SOCKET, SO_BINDTODEVICE, + (self.interface + "\0").encode("utf-8")) + except socket.error as error: + if error.errno == errno.EPERM: + logger.error("No permission to bind to" + " interface %s", self.interface) + elif error.errno == errno.ENOPROTOOPT: + logger.error("SO_BINDTODEVICE not available;" + " cannot bind to interface %s", + self.interface) + elif error.errno == errno.ENODEV: + logger.error("Interface %s does not exist," + " cannot bind", self.interface) + else: + raise # Only bind(2) the socket if we really need to. if self.server_address[0] or self.server_address[1]: if not self.server_address[0]: if self.address_family == socket.AF_INET6: - any_address = "::" # in6addr_any + any_address = "::" # in6addr_any else: - any_address = "0.0.0.0" # INADDR_ANY + any_address = "0.0.0.0" # INADDR_ANY self.server_address = (any_address, self.server_address[1]) elif not self.server_address[1]: @@ -2518,15 +2478,15 @@ class MandosServer(IPv6_TCPServer): """Mandos server. - + Attributes: clients: set of Client objects gnutls_priority GnuTLS priority string use_dbus: Boolean; to emit D-Bus signals or not - + Assumes a GLib.MainLoop event loop. """ - + def __init__(self, server_address, RequestHandlerClass, interface=None, use_ipv6=True, @@ -2542,44 +2502,44 @@ self.gnutls_priority = gnutls_priority IPv6_TCPServer.__init__(self, server_address, RequestHandlerClass, - interface=interface, - use_ipv6=use_ipv6, - socketfd=socketfd) - + interface = interface, + use_ipv6 = use_ipv6, + socketfd = socketfd) + def server_activate(self): if self.enabled: return socketserver.TCPServer.server_activate(self) - + def enable(self): self.enabled = True - + def add_pipe(self, parent_pipe, proc): # Call "handle_ipc" for both data and EOF events GLib.io_add_watch( parent_pipe.fileno(), GLib.IO_IN | GLib.IO_HUP, functools.partial(self.handle_ipc, - parent_pipe=parent_pipe, - proc=proc)) - + parent_pipe = parent_pipe, + proc = proc)) + def handle_ipc(self, source, condition, parent_pipe=None, - proc=None, + proc = None, client_object=None): # error, or the other end of multiprocessing.Pipe has closed if condition & (GLib.IO_ERR | GLib.IO_HUP): # Wait for other process to exit proc.join() return False - + # Read a request from the child request = parent_pipe.recv() command = request[0] - + if command == 'init': fpr = request[1] address = request[2] - + for c in self.clients.values(): if c.fingerprint == fpr: client = c @@ -2593,14 +2553,14 @@ address[0]) parent_pipe.send(False) return False - + GLib.io_add_watch( parent_pipe.fileno(), GLib.IO_IN | GLib.IO_HUP, functools.partial(self.handle_ipc, - parent_pipe=parent_pipe, - proc=proc, - client_object=client)) + parent_pipe = parent_pipe, + proc = proc, + client_object = client)) parent_pipe.send(True) # remove the old hook in favor of the new above hook on # same fileno @@ -2609,11 +2569,11 @@ funcname = request[1] args = request[2] kwargs = request[3] - + parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs))) - + if command == 'getattr': attrname = request[1] if isinstance(client_object.__getattribute__(attrname), @@ -2622,18 +2582,18 @@ else: parent_pipe.send(( 'data', client_object.__getattribute__(attrname))) - + if command == 'setattr': attrname = request[1] value = request[2] setattr(client_object, attrname, value) - + return True def rfc3339_duration_to_delta(duration): """Parse an RFC 3339 "duration" and return a datetime.timedelta - + >>> rfc3339_duration_to_delta("P7D") datetime.timedelta(7) >>> rfc3339_duration_to_delta("PT60S") @@ -2649,14 +2609,14 @@ >>> rfc3339_duration_to_delta("P1DT3M20S") datetime.timedelta(1, 200) """ - + # Parsing an RFC 3339 duration with regular expressions is not # possible - there would have to be multiple places for the same # values, like seconds. The current code, while more esoteric, is # cleaner without depending on a parsing library. If Python had a # built-in library for parsing we would use it, but we'd like to # avoid excessive use of external libraries. - + # New type for defining tokens, syntax, and semantics all-in-one Token = collections.namedtuple("Token", ( "regexp", # To match token; if "value" is not None, must have @@ -2695,14 +2655,11 @@ frozenset((token_year, token_month, token_day, token_time, token_week))) - # Define starting values: - # Value so far - value = datetime.timedelta() + # Define starting values + value = datetime.timedelta() # Value so far found_token = None - # Following valid tokens - followers = frozenset((token_duration, )) - # String left to parse - s = duration + followers = frozenset((token_duration, )) # Following valid tokens + s = duration # String left to parse # Loop until end token is found while found_token is not token_end: # Search for any currently valid tokens @@ -2732,7 +2689,7 @@ def string_to_delta(interval): """Parse a string and return a datetime.timedelta - + >>> string_to_delta('7d') datetime.timedelta(7) >>> string_to_delta('60s') @@ -2746,12 +2703,12 @@ >>> string_to_delta('5m 30s') datetime.timedelta(0, 330) """ - + try: return rfc3339_duration_to_delta(interval) except ValueError: pass - + timevalue = datetime.timedelta(0) for s in interval.split(): try: @@ -2775,9 +2732,9 @@ return timevalue -def daemon(nochdir=False, noclose=False): +def daemon(nochdir = False, noclose = False): """See daemon(3). Standard BSD Unix function. - + This should really exist as os.daemon, but it doesn't (yet).""" if os.fork(): sys.exit() @@ -2801,13 +2758,13 @@ def main(): - + ################################################################## # Parsing of options, both command line and config file - + parser = argparse.ArgumentParser() parser.add_argument("-v", "--version", action="version", - version="%(prog)s {}".format(version), + version = "%(prog)s {}".format(version), help="show version number and exit") parser.add_argument("-i", "--interface", metavar="IF", help="Bind to interface IF") @@ -2849,33 +2806,33 @@ parser.add_argument("--no-zeroconf", action="store_false", dest="zeroconf", help="Do not use Zeroconf", default=None) - + options = parser.parse_args() - + if options.check: import doctest fail_count, test_count = doctest.testmod() sys.exit(os.EX_OK if fail_count == 0 else 1) - + # Default values for config file for server-global settings - server_defaults = {"interface": "", - "address": "", - "port": "", - "debug": "False", - "priority": - "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA" - ":+SIGN-DSA-SHA256", - "servicename": "Mandos", - "use_dbus": "True", - "use_ipv6": "True", - "debuglevel": "", - "restore": "True", - "socket": "", - "statedir": "/var/lib/mandos", - "foreground": "False", - "zeroconf": "True", - } - + server_defaults = { "interface": "", + "address": "", + "port": "", + "debug": "False", + "priority": + "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA" + ":+SIGN-DSA-SHA256", + "servicename": "Mandos", + "use_dbus": "True", + "use_ipv6": "True", + "debuglevel": "", + "restore": "True", + "socket": "", + "statedir": "/var/lib/mandos", + "foreground": "False", + "zeroconf": "True", + } + # Parse config file for server-global settings server_config = configparser.SafeConfigParser(server_defaults) del server_defaults @@ -2883,8 +2840,7 @@ # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() # Use the appropriate methods on the non-string config options - for option in ("debug", "use_dbus", "use_ipv6", "restore", - "foreground", "zeroconf"): + for option in ("debug", "use_dbus", "use_ipv6", "foreground"): server_settings[option] = server_config.getboolean("DEFAULT", option) if server_settings["port"]: @@ -2900,7 +2856,7 @@ server_settings["socket"] = os.dup(server_settings ["socket"]) del server_config - + # Override the settings from the config file with command line # options, if set. for option in ("interface", "address", "port", "debug", @@ -2924,14 +2880,14 @@ if server_settings["debug"]: server_settings["foreground"] = True # Now we have our good server settings in "server_settings" - + ################################################################## - + if (not server_settings["zeroconf"] and not (server_settings["port"] or server_settings["socket"] != "")): parser.error("Needs port or socket to work without Zeroconf") - + # For convenience debug = server_settings["debug"] debuglevel = server_settings["debuglevel"] @@ -2941,7 +2897,7 @@ stored_state_file) foreground = server_settings["foreground"] zeroconf = server_settings["zeroconf"] - + if debug: initlogger(debug, logging.DEBUG) else: @@ -2950,22 +2906,22 @@ else: level = getattr(logging, debuglevel.upper()) initlogger(debug, level) - + if server_settings["servicename"] != "Mandos": syslogger.setFormatter( logging.Formatter('Mandos ({}) [%(process)d]:' ' %(levelname)s: %(message)s'.format( server_settings["servicename"]))) - + # Parse config file with clients client_config = configparser.SafeConfigParser(Client .client_defaults) client_config.read(os.path.join(server_settings["configdir"], "clients.conf")) - + global mandos_dbus_service mandos_dbus_service = None - + socketfd = None if server_settings["socket"] != "": socketfd = server_settings["socket"] @@ -2987,7 +2943,7 @@ except IOError as e: logger.error("Could not open file %r", pidfilename, exc_info=e) - + for name, group in (("_mandos", "_mandos"), ("mandos", "mandos"), ("nobody", "nogroup")): @@ -3011,35 +2967,35 @@ .format(uid, gid, os.strerror(error.errno))) if error.errno != errno.EPERM: raise - + if debug: # Enable all possible GnuTLS debugging - + # "Use a log level over 10 to enable all debugging options." # - GnuTLS manual gnutls.global_set_log_level(11) - + @gnutls.log_func def debug_gnutls(level, string): logger.debug("GnuTLS: %s", string[:-1]) - + gnutls.global_set_log_function(debug_gnutls) - + # Redirect stdin so all checkers get /dev/null null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR) os.dup2(null, sys.stdin.fileno()) if null > 2: os.close(null) - + # Need to fork before connecting to D-Bus if not foreground: # Close all input and output, do double fork, etc. daemon() - + # multiprocessing will use threads, so before we use GLib we need # to inform GLib that threads will be used. GLib.threads_init() - + global main_loop # From the Avahi example code DBusGMainLoop(set_as_default=True) @@ -3062,76 +3018,76 @@ if zeroconf: protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET service = AvahiServiceToSyslog( - name=server_settings["servicename"], - servicetype="_mandos._tcp", - protocol=protocol, - bus=bus) + name = server_settings["servicename"], + servicetype = "_mandos._tcp", + protocol = protocol, + bus = bus) if server_settings["interface"]: service.interface = if_nametoindex( server_settings["interface"].encode("utf-8")) - + global multiprocessing_manager multiprocessing_manager = multiprocessing.Manager() - + client_class = Client if use_dbus: - client_class = functools.partial(ClientDBus, bus=bus) - + client_class = functools.partial(ClientDBus, bus = bus) + client_settings = Client.config_parser(client_config) old_client_settings = {} clients_data = {} - + # This is used to redirect stdout and stderr for checker processes global wnull - wnull = open(os.devnull, "w") # A writable /dev/null + wnull = open(os.devnull, "w") # A writable /dev/null # Only used if server is running in foreground but not in debug # mode if debug or not foreground: wnull.close() - + # Get client data and settings from last running state. if server_settings["restore"]: try: with open(stored_state_path, "rb") as stored_state: - if sys.version_info.major == 2: + if sys.version_info.major == 2: clients_data, old_client_settings = pickle.load( stored_state) else: bytes_clients_data, bytes_old_client_settings = ( - pickle.load(stored_state, encoding="bytes")) - # Fix bytes to strings - # clients_data + pickle.load(stored_state, encoding = "bytes")) + ### Fix bytes to strings + ## clients_data # .keys() - clients_data = {(key.decode("utf-8") - if isinstance(key, bytes) - else key): value - for key, value in - bytes_clients_data.items()} + clients_data = { (key.decode("utf-8") + if isinstance(key, bytes) + else key): value + for key, value in + bytes_clients_data.items() } del bytes_clients_data for key in clients_data: - value = {(k.decode("utf-8") - if isinstance(k, bytes) else k): v - for k, v in - clients_data[key].items()} + value = { (k.decode("utf-8") + if isinstance(k, bytes) else k): v + for k, v in + clients_data[key].items() } clients_data[key] = value # .client_structure value["client_structure"] = [ (s.decode("utf-8") if isinstance(s, bytes) else s) for s in - value["client_structure"]] + value["client_structure"] ] # .name & .host for k in ("name", "host"): if isinstance(value[k], bytes): value[k] = value[k].decode("utf-8") - # old_client_settings + ## old_client_settings # .keys() old_client_settings = { (key.decode("utf-8") if isinstance(key, bytes) else key): value for key, value in - bytes_old_client_settings.items()} + bytes_old_client_settings.items() } del bytes_old_client_settings # .host for value in old_client_settings.values(): @@ -3151,13 +3107,13 @@ logger.warning("Could not load persistent state: " "EOFError:", exc_info=e) - + with PGPEngine() as pgp: for client_name, client in clients_data.items(): # Skip removed clients if client_name not in client_settings: continue - + # Decide which value to use after restoring saved state. # We have three different values: Old config file, # new config file, and saved state. @@ -3174,7 +3130,7 @@ client[name] = value except KeyError: pass - + # Clients who has passed its expire date can still be # enabled if its last checker was successful. A Client # whose checker succeeded before we stored its state is @@ -3213,7 +3169,7 @@ client_name)) client["secret"] = (client_settings[client_name] ["secret"]) - + # Add/remove clients based on new changes made to config for client_name in (set(old_client_settings) - set(client_settings)): @@ -3221,17 +3177,17 @@ for client_name in (set(client_settings) - set(old_client_settings)): clients_data[client_name] = client_settings[client_name] - + # Create all client objects for client_name, client in clients_data.items(): tcp_server.clients[client_name] = client_class( - name=client_name, - settings=client, - server_settings=server_settings) - + name = client_name, + settings = client, + server_settings = server_settings) + if not tcp_server.clients: logger.warning("No clients defined") - + if not foreground: if pidfile is not None: pid = os.getpid() @@ -3243,40 +3199,40 @@ pidfilename, pid) del pidfile del pidfilename - + for termsig in (signal.SIGHUP, signal.SIGTERM): GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig, lambda: main_loop.quit() and False) - + if use_dbus: - + @alternate_dbus_interfaces( - {"se.recompile.Mandos": "se.bsnet.fukt.Mandos"}) + { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" }) class MandosDBusService(DBusObjectWithObjectManager): """A D-Bus proxy object""" - + def __init__(self): dbus.service.Object.__init__(self, bus, "/") - + _interface = "se.recompile.Mandos" - + @dbus.service.signal(_interface, signature="o") def ClientAdded(self, objpath): "D-Bus signal" pass - + @dbus.service.signal(_interface, signature="ss") def ClientNotFound(self, fingerprint, address): "D-Bus signal" pass - + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.signal(_interface, signature="os") def ClientRemoved(self, objpath, name): "D-Bus signal" pass - + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface, out_signature="ao") @@ -3284,7 +3240,7 @@ "D-Bus method" return dbus.Array(c.dbus_object_path for c in tcp_server.clients.values()) - + @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"}) @dbus.service.method(_interface, @@ -3292,11 +3248,11 @@ def GetAllClientsWithProperties(self): "D-Bus method" return dbus.Dictionary( - {c.dbus_object_path: c.GetAll( + { c.dbus_object_path: c.GetAll( "se.recompile.Mandos.Client") - for c in tcp_server.clients.values()}, + for c in tcp_server.clients.values() }, signature="oa{sv}") - + @dbus.service.method(_interface, in_signature="o") def RemoveClient(self, object_path): "D-Bus method" @@ -3310,21 +3266,21 @@ self.client_removed_signal(c) return raise KeyError(object_path) - + del _interface - + @dbus.service.method(dbus.OBJECT_MANAGER_IFACE, - out_signature="a{oa{sa{sv}}}") + out_signature = "a{oa{sa{sv}}}") def GetManagedObjects(self): """D-Bus method""" return dbus.Dictionary( - {client.dbus_object_path: - dbus.Dictionary( - {interface: client.GetAll(interface) - for interface in - client._get_all_interface_names()}) - for client in tcp_server.clients.values()}) - + { client.dbus_object_path: + dbus.Dictionary( + { interface: client.GetAll(interface) + for interface in + client._get_all_interface_names()}) + for client in tcp_server.clients.values()}) + def client_added_signal(self, client): """Send the new standard signal and the old signal""" if use_dbus: @@ -3332,12 +3288,12 @@ self.InterfacesAdded( client.dbus_object_path, dbus.Dictionary( - {interface: client.GetAll(interface) - for interface in - client._get_all_interface_names()})) + { interface: client.GetAll(interface) + for interface in + client._get_all_interface_names()})) # Old signal self.ClientAdded(client.dbus_object_path) - + def client_removed_signal(self, client): """Send the new standard signal and the old signal""" if use_dbus: @@ -3348,24 +3304,19 @@ # Old signal self.ClientRemoved(client.dbus_object_path, client.name) - + mandos_dbus_service = MandosDBusService() - - # Save modules to variables to exempt the modules from being - # unloaded before the function registered with atexit() is run. - mp = multiprocessing - wn = wnull - + def cleanup(): "Cleanup function; run on exit" if zeroconf: service.cleanup() - - mp.active_children() - wn.close() + + multiprocessing.active_children() + wnull.close() if not (tcp_server.clients or client_settings): return - + # Store client before exiting. Secrets are encrypted with key # based on what config file has. If config file is # removed/edited, old secret will thus be unrecovable. @@ -3376,24 +3327,24 @@ client.encrypted_secret = pgp.encrypt(client.secret, key) client_dict = {} - + # A list of attributes that can not be pickled # + secret. - exclude = {"bus", "changedstate", "secret", - "checker", "server_settings"} + exclude = { "bus", "changedstate", "secret", + "checker", "server_settings" } for name, typ in inspect.getmembers(dbus.service .Object): exclude.add(name) - + client_dict["encrypted_secret"] = (client .encrypted_secret) for attr in client.client_structure: if attr not in exclude: client_dict[attr] = getattr(client, attr) - + clients[client.name] = client_dict del client_settings[client.name]["secret"] - + try: with tempfile.NamedTemporaryFile( mode='wb', @@ -3402,7 +3353,7 @@ dir=os.path.dirname(stored_state_path), delete=False) as stored_state: pickle.dump((clients, client_settings), stored_state, - protocol=2) + protocol = 2) tempname = stored_state.name os.rename(tempname, stored_state_path) except (IOError, OSError) as e: @@ -3418,7 +3369,7 @@ logger.warning("Could not save persistent state:", exc_info=e) raise - + # Delete all clients, and settings from config while tcp_server.clients: name, client = tcp_server.clients.popitem() @@ -3430,9 +3381,9 @@ if use_dbus: mandos_dbus_service.client_removed_signal(client) client_settings.clear() - + atexit.register(cleanup) - + for client in tcp_server.clients.values(): if use_dbus: # Emit D-Bus signal for adding @@ -3440,10 +3391,10 @@ # Need to initiate checking of clients if client.enabled: client.init_checker() - + tcp_server.enable() tcp_server.server_activate() - + # Find out what port we got if zeroconf: service.port = tcp_server.socket.getsockname()[1] @@ -3454,9 +3405,9 @@ else: # IPv4 logger.info("Now listening on address %r, port %d", *tcp_server.socket.getsockname()) - - # service.interface = tcp_server.socket.getsockname()[3] - + + #service.interface = tcp_server.socket.getsockname()[3] + try: if zeroconf: # From the Avahi example code @@ -3467,12 +3418,12 @@ cleanup() sys.exit(1) # End of Avahi example code - + GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN, lambda *args, **kwargs: (tcp_server.handle_request (*args[2:], **kwargs) or True)) - + logger.debug("Starting main loop") main_loop.run() except AvahiError as error: === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2017-02-23 19:11:11 +0000 +++ mandos-clients.conf.xml 2016-03-05 21:42:56 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/clients.conf"> - + %common; ]> @@ -41,7 +41,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -231,7 +230,7 @@ This option sets the OpenPGP fingerprint that identifies the public key that clients authenticate themselves with - through TLS. The string needs to be in hexadecimal form, + through TLS. The string needs to be in hexidecimal form, but spaces or upper/lower case are not significant. === modified file 'mandos-ctl' --- mandos-ctl 2017-08-20 13:50:57 +0000 +++ mandos-ctl 2016-03-19 22:00:38 +0000 @@ -1,11 +1,11 @@ #!/usr/bin/python # -*- mode: python; coding: utf-8 -*- -# +# # Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2008-2017 Teddy Hogeborn -# Copyright © 2008-2017 Björn Påhlsson -# +# +# Copyright © 2008-2016 Teddy Hogeborn +# Copyright © 2008-2016 Björn Påhlsson +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -15,13 +15,13 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see # . -# +# # Contact the authors at . -# +# from __future__ import (division, absolute_import, print_function, unicode_literals) @@ -38,7 +38,7 @@ import re import os import collections -import json +import doctest import dbus @@ -64,9 +64,7 @@ "ApprovalDelay": "Approval Delay", "ApprovalDuration": "Approval Duration", "Checker": "Checker", - "ExtendedTimeout": "Extended Timeout", - "Expires": "Expires", - "LastCheckerStatus": "Last Checker Status", + "ExtendedTimeout": "Extended Timeout" } defaultkeywords = ("Name", "Enabled", "Timeout", "LastCheckedOK") domain = "se.recompile" @@ -74,7 +72,7 @@ server_path = "/" server_interface = domain + ".Mandos" client_interface = domain + ".Mandos.Client" -version = "1.7.15" +version = "1.7.7" try: @@ -82,19 +80,18 @@ except AttributeError: dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager" - def milliseconds_to_string(ms): td = datetime.timedelta(0, 0, 0, ms) - return ("{days}{hours:02}:{minutes:02}:{seconds:02}" - .format(days="{}T".format(td.days) if td.days else "", - hours=td.seconds // 3600, - minutes=(td.seconds % 3600) // 60, - seconds=td.seconds % 60)) + return ("{days}{hours:02}:{minutes:02}:{seconds:02}".format( + days = "{}T".format(td.days) if td.days else "", + hours = td.seconds // 3600, + minutes = (td.seconds % 3600) // 60, + seconds = td.seconds % 60)) def rfc3339_duration_to_delta(duration): """Parse an RFC 3339 "duration" and return a datetime.timedelta - + >>> rfc3339_duration_to_delta("P7D") datetime.timedelta(7) >>> rfc3339_duration_to_delta("PT60S") @@ -110,14 +107,14 @@ >>> rfc3339_duration_to_delta("P1DT3M20S") datetime.timedelta(1, 200) """ - + # Parsing an RFC 3339 duration with regular expressions is not # possible - there would have to be multiple places for the same # values, like seconds. The current code, while more esoteric, is # cleaner without depending on a parsing library. If Python had a # built-in library for parsing we would use it, but we'd like to # avoid excessive use of external libraries. - + # New type for defining tokens, syntax, and semantics all-in-one Token = collections.namedtuple("Token", ( "regexp", # To match token; if "value" is not None, must have @@ -156,14 +153,11 @@ frozenset((token_year, token_month, token_day, token_time, token_week))) - # Define starting values: - # Value so far - value = datetime.timedelta() + # Define starting values + value = datetime.timedelta() # Value so far found_token = None - # Following valid tokens - followers = frozenset((token_duration, )) - # String left to parse - s = duration + followers = frozenset((token_duration, )) # Following valid tokens + s = duration # String left to parse # Loop until end token is found while found_token is not token_end: # Search for any currently valid tokens @@ -193,7 +187,7 @@ def string_to_delta(interval): """Parse a string and return a datetime.timedelta - + >>> string_to_delta('7d') datetime.timedelta(7) >>> string_to_delta('60s') @@ -207,15 +201,15 @@ >>> string_to_delta('5m 30s') datetime.timedelta(0, 330) """ - + try: return rfc3339_duration_to_delta(interval) except ValueError: pass - + value = datetime.timedelta(0) regexp = re.compile(r"(\d+)([dsmhw]?)") - + for num, suffix in regexp.findall(interval): if suffix == "d": value += datetime.timedelta(int(num)) @@ -240,20 +234,20 @@ "ApprovalDuration", "ExtendedTimeout"): return milliseconds_to_string(value) return str(value) - + # Create format string to print table rows format_string = " ".join("{{{key}:{width}}}".format( - width=max(len(tablewords[key]), - max(len(valuetostring(client[key], key)) - for client in clients)), - key=key) + width = max(len(tablewords[key]), + max(len(valuetostring(client[key], key)) + for client in clients)), + key = key) for key in keywords) # Print header line print(format_string.format(**tablewords)) for client in clients: - print(format_string - .format(**{key: valuetostring(client[key], key) - for key in keywords})) + print(format_string.format(**{ + key: valuetostring(client[key], key) + for key in keywords })) def has_actions(options): @@ -280,14 +274,12 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument("--version", action="version", - version="%(prog)s {}".format(version), + version = "%(prog)s {}".format(version), help="show version number and exit") parser.add_argument("-a", "--all", action="store_true", help="Select all clients") parser.add_argument("-v", "--verbose", action="store_true", help="Print all fields") - parser.add_argument("-j", "--dump-json", action="store_true", - help="Dump client data in JSON format") parser.add_argument("-e", "--enable", action="store_true", help="Enable client") parser.add_argument("-d", "--disable", action="store_true", @@ -332,62 +324,58 @@ help="Run self-test") parser.add_argument("client", nargs="*", help="Client name") options = parser.parse_args() - + if has_actions(options) and not (options.client or options.all): parser.error("Options require clients names or --all.") if options.verbose and has_actions(options): - parser.error("--verbose can only be used alone.") - if options.dump_json and (options.verbose - or has_actions(options)): - parser.error("--dump-json can only be used alone.") + parser.error("--verbose can only be used alone or with" + " --all.") if options.all and not has_actions(options): parser.error("--all requires an action.") if options.check: - import doctest fail_count, test_count = doctest.testmod() sys.exit(os.EX_OK if fail_count == 0 else 1) - + try: bus = dbus.SystemBus() mandos_dbus_objc = bus.get_object(busname, server_path) except dbus.exceptions.DBusException: print("Could not connect to Mandos server", file=sys.stderr) sys.exit(1) - + mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface=server_interface) + dbus_interface = server_interface) mandos_serv_object_manager = dbus.Interface( - mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE) - - # block stderr since dbus library prints to stderr + mandos_dbus_objc, dbus_interface = dbus.OBJECT_MANAGER_IFACE) + + #block stderr since dbus library prints to stderr null = os.open(os.path.devnull, os.O_RDWR) stderrcopy = os.dup(sys.stderr.fileno()) os.dup2(null, sys.stderr.fileno()) os.close(null) try: try: - mandos_clients = {path: ifs_and_props[client_interface] - for path, ifs_and_props in - mandos_serv_object_manager - .GetManagedObjects().items() - if client_interface in ifs_and_props} + mandos_clients = { path: ifs_and_props[client_interface] + for path, ifs_and_props in + mandos_serv_object_manager + .GetManagedObjects().items() + if client_interface in ifs_and_props } finally: - # restore stderr + #restore stderr os.dup2(stderrcopy, sys.stderr.fileno()) os.close(stderrcopy) except dbus.exceptions.DBusException as e: - print("Access denied: " - "Accessing mandos server through D-Bus: {}".format(e), - file=sys.stderr) + print("Access denied: Accessing mandos server through D-Bus: {}" + .format(e), file=sys.stderr) sys.exit(1) - + # Compile dict of (clients: properties) to process - clients = {} - + clients={} + if options.all or not options.client: - clients = {bus.get_object(busname, path): properties - for path, properties in mandos_clients.items()} + clients = { bus.get_object(busname, path): properties + for path, properties in mandos_clients.items() } else: for name in options.client: for path, client in mandos_clients.items(): @@ -399,49 +387,36 @@ print("Client not found on server: {!r}" .format(name), file=sys.stderr) sys.exit(1) - + if not has_actions(options) and clients: - if options.verbose or options.dump_json: + if options.verbose: keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK", "Created", "Interval", "Host", "Fingerprint", "CheckerRunning", "LastEnabled", "ApprovalPending", "ApprovedByDefault", "LastApprovalRequest", "ApprovalDelay", "ApprovalDuration", "Checker", - "ExtendedTimeout", "Expires", - "LastCheckerStatus") + "ExtendedTimeout") else: keywords = defaultkeywords - - if options.dump_json: - json.dump({client["Name"]: {key: - bool(client[key]) - if isinstance(client[key], - dbus.Boolean) - else client[key] - for key in keywords} - for client in clients.values()}, - fp=sys.stdout, indent=4, - separators=(',', ': ')) - print() - else: - print_clients(clients.values(), keywords) + + print_clients(clients.values(), keywords) else: # Process each client in the list by all selected options for client in clients: - + def set_client_prop(prop, value): """Set a Client D-Bus property""" client.Set(client_interface, prop, value, dbus_interface=dbus.PROPERTIES_IFACE) - + def set_client_prop_ms(prop, value): """Set a Client D-Bus property, converted from a string to milliseconds.""" set_client_prop(prop, string_to_delta(value).total_seconds() * 1000) - + if options.remove: mandos_serv.RemoveClient(client.__dbus_object_path__) if options.enable: @@ -455,11 +430,11 @@ if options.stop_checker: set_client_prop("CheckerRunning", dbus.Boolean(False)) if options.is_enabled: - if client.Get(client_interface, "Enabled", - dbus_interface=dbus.PROPERTIES_IFACE): - sys.exit(0) - else: - sys.exit(1) + sys.exit(0 if client.Get(client_interface, + "Enabled", + dbus_interface= + dbus.PROPERTIES_IFACE) + else 1) if options.checker is not None: set_client_prop("Checker", options.checker) if options.host is not None: === modified file 'mandos-ctl.xml' --- mandos-ctl.xml 2017-02-23 19:11:11 +0000 +++ mandos-ctl.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -38,7 +38,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -53,111 +52,109 @@ &COMMANDNAME; - Control or query the operation of the Mandos server + Control the operation of the Mandos server &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -171,11 +168,8 @@ &COMMANDNAME; - - - - - + + @@ -214,10 +208,9 @@ DESCRIPTION - &COMMANDNAME; is a program to control or - query the operation of the Mandos server - mandos8. + &COMMANDNAME; is a program to control the + operation of the Mandos server mandos8. This program can be used to change client settings, approve or @@ -481,16 +474,6 @@ - - - - - Dump client settings as JSON to standard output. - - - - - === modified file 'mandos-keygen' --- mandos-keygen 2017-08-20 14:08:59 +0000 +++ mandos-keygen 2016-03-19 22:00:38 +0000 @@ -2,8 +2,8 @@ # # Mandos key generator - create a new OpenPGP key for a Mandos client # -# Copyright © 2008-2017 Teddy Hogeborn -# Copyright © 2008-2017 Björn Påhlsson +# Copyright © 2008-2016 Teddy Hogeborn +# Copyright © 2008-2016 Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ # Contact the authors at . # -VERSION="1.7.15" +VERSION="1.7.7" KEYDIR="/etc/keys/mandos" KEYTYPE=RSA @@ -161,8 +161,8 @@ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;; esac - if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ]; } \ - && [ "$FORCE" -eq 0 ]; then + if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \ + -a "$FORCE" -eq 0 ]; then echo "Refusing to overwrite old key files; use --force" >&2 exit 1 fi @@ -218,7 +218,6 @@ #Handle: #%pubring pubring.gpg #%secring secring.gpg - %no-protection %commit EOF @@ -286,12 +285,11 @@ esac if [ $SSH -eq 1 ]; then - for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do + for ssh_keytype in ed25519 rsa; do set +e ssh_fingerprint="`ssh-keyscan -t $ssh_keytype localhost 2>/dev/null`" - err=$? set -e - if [ $err -ne 0 ]; then + if [ $? -ne 0 ]; then ssh_fingerprint="" continue fi @@ -326,11 +324,11 @@ cat "$PASSFILE" else tty --quiet && stty -echo - echo -n "Enter passphrase: " >/dev/tty - read -r first + echo -n "Enter passphrase: " >&2 + read first tty --quiet && echo >&2 - echo -n "Repeat passphrase: " >/dev/tty - read -r second + echo -n "Repeat passphrase: " >&2 + read second if tty --quiet; then echo >&2 stty echo === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2017-02-23 19:11:11 +0000 +++ mandos-keygen.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'mandos-monitor' --- mandos-monitor 2017-02-23 20:35:20 +0000 +++ mandos-monitor 2016-03-19 22:00:38 +0000 @@ -1,11 +1,11 @@ #!/usr/bin/python # -*- mode: python; coding: utf-8 -*- -# +# # Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2009-2017 Teddy Hogeborn -# Copyright © 2009-2017 Björn Påhlsson -# +# +# Copyright © 2009-2016 Teddy Hogeborn +# Copyright © 2009-2016 Björn Påhlsson +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -15,13 +15,13 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see # . -# +# # Contact the authors at . -# +# from __future__ import (division, absolute_import, print_function, unicode_literals) @@ -45,27 +45,25 @@ import locale -import logging - if sys.version_info.major == 2: str = unicode locale.setlocale(locale.LC_ALL, '') +import logging logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL) # Some useful constants domain = 'se.recompile' server_interface = domain + '.Mandos' client_interface = domain + '.Mandos.Client' -version = "1.7.15" +version = "1.7.7" try: dbus.OBJECT_MANAGER_IFACE except AttributeError: dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager" - def isoformat_to_datetime(iso): "Parse an ISO 8601 date string to a datetime.datetime()" if not iso: @@ -79,9 +77,8 @@ int(day), int(hour), int(minute), - int(second), # Whole seconds - int(fraction*1000000)) # Microseconds - + int(second), # Whole seconds + int(fraction*1000000)) # Microseconds class MandosClientPropertyCache(object): """This wraps a Mandos Client D-Bus proxy object, caches the @@ -89,21 +86,22 @@ changed. """ def __init__(self, proxy_object=None, properties=None, **kwargs): - self.proxy = proxy_object # Mandos Client proxy object + self.proxy = proxy_object # Mandos Client proxy object self.properties = dict() if properties is None else properties self.property_changed_match = ( self.proxy.connect_to_signal("PropertiesChanged", self.properties_changed, dbus.PROPERTIES_IFACE, byte_arrays=True)) - + if properties is None: - self.properties.update(self.proxy.GetAll( - client_interface, - dbus_interface=dbus.PROPERTIES_IFACE)) - + self.properties.update( + self.proxy.GetAll(client_interface, + dbus_interface + = dbus.PROPERTIES_IFACE)) + super(MandosClientPropertyCache, self).__init__(**kwargs) - + def properties_changed(self, interface, properties, invalidated): """This is called whenever we get a PropertiesChanged signal It updates the changed properties in the "properties" dict. @@ -111,7 +109,7 @@ # Update properties dict with new value if interface == client_interface: self.properties.update(properties) - + def delete(self): self.property_changed_match.remove() @@ -119,7 +117,7 @@ class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache): """A Mandos Client which is visible on the screen. """ - + def __init__(self, server_proxy_object=None, update_hook=None, delete_hook=None, logger=None, **kwargs): # Called on update @@ -130,9 +128,9 @@ self.server_proxy_object = server_proxy_object # Logger self.logger = logger - + self._update_timer_callback_tag = None - + # The widget shown normally self._text_widget = urwid.Text("") # The widget shown when we have focus @@ -140,7 +138,7 @@ super(MandosClientWidget, self).__init__(**kwargs) self.update() self.opened = False - + self.match_objects = ( self.proxy.connect_to_signal("CheckerCompleted", self.checker_completed, @@ -164,7 +162,7 @@ byte_arrays=True)) self.logger('Created client {}' .format(self.properties["Name"]), level=0) - + def using_timer(self, flag): """Call this method with True or False when timer should be activated or deactivated. @@ -177,7 +175,7 @@ elif not (flag or self._update_timer_callback_tag is None): GLib.source_remove(self._update_timer_callback_tag) self._update_timer_callback_tag = None - + def checker_completed(self, exitstatus, condition, command): if exitstatus == 0: self.logger('Checker for client {} (command "{}")' @@ -197,17 +195,17 @@ .format(self.properties["Name"], command, os.WTERMSIG(condition))) self.update() - + def checker_started(self, command): """Server signals that a checker started.""" self.logger('Client {} started checker "{}"' .format(self.properties["Name"], command), level=0) - + def got_secret(self): self.logger('Client {} received its secret' .format(self.properties["Name"])) - + def need_approval(self, timeout, default): if not default: message = 'Client {} needs approval within {} seconds' @@ -215,48 +213,49 @@ message = 'Client {} will get its secret in {} seconds' self.logger(message.format(self.properties["Name"], timeout/1000)) - + def rejected(self, reason): self.logger('Client {} was rejected; reason: {}' .format(self.properties["Name"], reason)) - + def selectable(self): """Make this a "selectable" widget. This overrides the method from urwid.FlowWidget.""" return True - + def rows(self, maxcolrow, focus=False): """How many rows this widget will occupy might depend on whether we have focus or not. This overrides the method from urwid.FlowWidget""" return self.current_widget(focus).rows(maxcolrow, focus=focus) - + def current_widget(self, focus=False): if focus or self.opened: return self._focus_widget return self._widget - + def update(self): "Called when what is visible on the screen should be updated." # How to add standout mode to a style - with_standout = {"normal": "standout", - "bold": "bold-standout", - "underline-blink": - "underline-blink-standout", - "bold-underline-blink": - "bold-underline-blink-standout", - } - + with_standout = { "normal": "standout", + "bold": "bold-standout", + "underline-blink": + "underline-blink-standout", + "bold-underline-blink": + "bold-underline-blink-standout", + } + # Rebuild focus and non-focus widgets using current properties - + # Base part of a client. Name! base = '{name}: '.format(name=self.properties["Name"]) if not self.properties["Enabled"]: message = "DISABLED" self.using_timer(False) elif self.properties["ApprovalPending"]: - timeout = datetime.timedelta( - milliseconds=self.properties["ApprovalDelay"]) + timeout = datetime.timedelta(milliseconds + = self.properties + ["ApprovalDelay"]) last_approval_request = isoformat_to_datetime( self.properties["LastApprovalRequest"]) if last_approval_request is not None: @@ -289,7 +288,7 @@ message = "enabled" self.using_timer(False) self._text = "{}{}".format(base, message) - + if not urwid.supports_unicode(): self._text = self._text.encode("ascii", "replace") textlist = [("normal", self._text)] @@ -305,14 +304,14 @@ # Run update hook, if any if self.update_hook is not None: self.update_hook() - + def update_timer(self): """called by GLib. Will indefinitely loop until GLib.source_remove() on tag is called """ self.update() return True # Keep calling this - + def delete(self, **kwargs): if self._update_timer_callback_tag is not None: GLib.source_remove(self._update_timer_callback_tag) @@ -323,31 +322,31 @@ if self.delete_hook is not None: self.delete_hook(self) return super(MandosClientWidget, self).delete(**kwargs) - + def render(self, maxcolrow, focus=False): """Render differently if we have focus. This overrides the method from urwid.FlowWidget""" return self.current_widget(focus).render(maxcolrow, focus=focus) - + def keypress(self, maxcolrow, key): """Handle keys. This overrides the method from urwid.FlowWidget""" if key == "+": self.proxy.Set(client_interface, "Enabled", - dbus.Boolean(True), ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + dbus.Boolean(True), ignore_reply = True, + dbus_interface = dbus.PROPERTIES_IFACE) elif key == "-": self.proxy.Set(client_interface, "Enabled", False, - ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + ignore_reply = True, + dbus_interface = dbus.PROPERTIES_IFACE) elif key == "a": self.proxy.Approve(dbus.Boolean(True, variant_level=1), - dbus_interface=client_interface, + dbus_interface = client_interface, ignore_reply=True) elif key == "d": self.proxy.Approve(dbus.Boolean(False, variant_level=1), - dbus_interface=client_interface, + dbus_interface = client_interface, ignore_reply=True) elif key == "R" or key == "_" or key == "ctrl k": self.server_proxy_object.RemoveClient(self.proxy @@ -355,14 +354,14 @@ ignore_reply=True) elif key == "s": self.proxy.Set(client_interface, "CheckerRunning", - dbus.Boolean(True), ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + dbus.Boolean(True), ignore_reply = True, + dbus_interface = dbus.PROPERTIES_IFACE) elif key == "S": self.proxy.Set(client_interface, "CheckerRunning", - dbus.Boolean(False), ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + dbus.Boolean(False), ignore_reply = True, + dbus_interface = dbus.PROPERTIES_IFACE) elif key == "C": - self.proxy.CheckedOK(dbus_interface=client_interface, + self.proxy.CheckedOK(dbus_interface = client_interface, ignore_reply=True) # xxx # elif key == "p" or key == "=": @@ -373,12 +372,12 @@ # self.open() else: return key - + def properties_changed(self, interface, properties, invalidated): """Call self.update() if any properties changed. This overrides the method from MandosClientPropertyCache""" - old_values = {key: self.properties.get(key) - for key in properties.keys()} + old_values = { key: self.properties.get(key) + for key in properties.keys() } super(MandosClientWidget, self).properties_changed( interface, properties, invalidated) if any(old_values[key] != self.properties.get(key) @@ -392,8 +391,7 @@ use them as an excuse to shift focus away from this widget. """ def keypress(self, *args, **kwargs): - ret = (super(ConstrainedListBox, self) - .keypress(*args, **kwargs)) + ret = super(ConstrainedListBox, self).keypress(*args, **kwargs) if ret in ("up", "down"): return return ret @@ -405,9 +403,9 @@ """ def __init__(self, max_log_length=1000, log_level=1): DBusGMainLoop(set_as_default=True) - + self.screen = urwid.curses_display.Screen() - + self.screen.register_palette(( ("normal", "default", "default", None), @@ -418,8 +416,7 @@ ("standout", "standout", "default", "standout"), ("bold-underline-blink", - "bold,underline,blink", "default", - "bold,underline,blink"), + "bold,underline,blink", "default", "bold,underline,blink"), ("bold-standout", "bold,standout", "default", "bold,standout"), ("underline-blink-standout", @@ -429,63 +426,66 @@ "bold,underline,blink,standout", "default", "bold,underline,blink,standout"), )) - + if urwid.supports_unicode(): - self.divider = "─" # \u2500 + self.divider = "─" # \u2500 + #self.divider = "━" # \u2501 else: - self.divider = "_" # \u005f - + #self.divider = "-" # \u002d + self.divider = "_" # \u005f + self.screen.start() - + self.size = self.screen.get_cols_rows() - + self.clients = urwid.SimpleListWalker([]) self.clients_dict = {} - + # We will add Text widgets to this list self.log = [] self.max_log_length = max_log_length - + self.log_level = log_level - + # We keep a reference to the log widget so we can remove it # from the ListWalker without it getting destroyed self.logbox = ConstrainedListBox(self.log) - + # This keeps track of whether self.uilist currently has # self.logbox in it or not self.log_visible = True self.log_wrap = "any" - + self.rebuild() self.log_message_raw(("bold", "Mandos Monitor version " + version)) self.log_message_raw(("bold", "q: Quit ?: Help")) - + self.busname = domain + '.Mandos' self.main_loop = GLib.MainLoop() - + def client_not_found(self, fingerprint, address): self.log_message("Client with address {} and fingerprint {}" " could not be found" .format(address, fingerprint)) - + def rebuild(self): """This rebuilds the User Interface. Call this when the widget layout needs to change""" self.uilist = [] - # self.uilist.append(urwid.ListBox(self.clients)) + #self.uilist.append(urwid.ListBox(self.clients)) self.uilist.append(urwid.Frame(ConstrainedListBox(self. clients), - # header=urwid.Divider(), + #header=urwid.Divider(), header=None, - footer=urwid.Divider( - div_char=self.divider))) + footer= + urwid.Divider(div_char= + self.divider))) if self.log_visible: self.uilist.append(self.logbox) self.topwidget = urwid.Pile(self.uilist) - + def log_message(self, message, level=1): """Log message formatted with timestamp""" if level < self.log_level: @@ -493,26 +493,26 @@ timestamp = datetime.datetime.now().isoformat() self.log_message_raw("{}: {}".format(timestamp, message), level=level) - + def log_message_raw(self, markup, level=1): """Add a log message to the log buffer.""" if level < self.log_level: return self.log.append(urwid.Text(markup, wrap=self.log_wrap)) - if self.max_log_length: - if len(self.log) > self.max_log_length: - del self.log[0:len(self.log)-self.max_log_length-1] + if (self.max_log_length + and len(self.log) > self.max_log_length): + del self.log[0:len(self.log)-self.max_log_length-1] self.logbox.set_focus(len(self.logbox.body.contents), coming_from="above") self.refresh() - + def toggle_log_display(self): """Toggle visibility of the log buffer.""" self.log_visible = not self.log_visible self.rebuild() self.log_message("Log visibility changed to: {}" .format(self.log_visible), level=0) - + def change_log_display(self): """Change type of log display. Currently, this toggles wrapping of text lines.""" @@ -524,10 +524,10 @@ textwidget.set_wrap_mode(self.log_wrap) self.log_message("Wrap mode: {}".format(self.log_wrap), level=0) - + def find_and_remove_client(self, path, interfaces): """Find a client by its object path and remove it. - + This is connected to the InterfacesRemoved signal from the Mandos server object.""" if client_interface not in interfaces: @@ -541,10 +541,10 @@ .format(path)) return client.delete() - + def add_new_client(self, path, ifs_and_props): """Find a client by its object path and remove it. - + This is connected to the InterfacesAdded signal from the Mandos server object. """ @@ -552,15 +552,21 @@ # Not a Mandos client object; ignore return client_proxy_object = self.bus.get_object(self.busname, path) - self.add_client(MandosClientWidget( - server_proxy_object=self.mandos_serv, - proxy_object=client_proxy_object, - update_hook=self.refresh, - delete_hook=self.remove_client, - logger=self.log_message, - properties=dict(ifs_and_props[client_interface])), + self.add_client(MandosClientWidget(server_proxy_object + =self.mandos_serv, + proxy_object + =client_proxy_object, + update_hook + =self.refresh, + delete_hook + =self.remove_client, + logger + =self.log_message, + properties + = dict(ifs_and_props[ + client_interface])), path=path) - + def add_client(self, client, path=None): self.clients.append(client) if path is None: @@ -568,46 +574,47 @@ self.clients_dict[path] = client self.clients.sort(key=lambda c: c.properties["Name"]) self.refresh() - + def remove_client(self, client, path=None): self.clients.remove(client) if path is None: path = client.proxy.object_path del self.clients_dict[path] self.refresh() - + def refresh(self): """Redraw the screen""" canvas = self.topwidget.render(self.size, focus=True) self.screen.draw_screen(self.size, canvas) - + def run(self): """Start the main loop and exit when it's done.""" self.bus = dbus.SystemBus() mandos_dbus_objc = self.bus.get_object( self.busname, "/", follow_name_owner_changes=True) - self.mandos_serv = dbus.Interface( - mandos_dbus_objc, dbus_interface=server_interface) + self.mandos_serv = dbus.Interface(mandos_dbus_objc, + dbus_interface + = server_interface) try: mandos_clients = (self.mandos_serv .GetAllClientsWithProperties()) if not mandos_clients: - self.log_message_raw(("bold", - "Note: Server has no clients.")) + self.log_message_raw(("bold", "Note: Server has no clients.")) except dbus.exceptions.DBusException: - self.log_message_raw(("bold", - "Note: No Mandos server running.")) + self.log_message_raw(("bold", "Note: No Mandos server running.")) mandos_clients = dbus.Dictionary() - + (self.mandos_serv .connect_to_signal("InterfacesRemoved", self.find_and_remove_client, - dbus_interface=dbus.OBJECT_MANAGER_IFACE, + dbus_interface + = dbus.OBJECT_MANAGER_IFACE, byte_arrays=True)) (self.mandos_serv .connect_to_signal("InterfacesAdded", self.add_new_client, - dbus_interface=dbus.OBJECT_MANAGER_IFACE, + dbus_interface + = dbus.OBJECT_MANAGER_IFACE, byte_arrays=True)) (self.mandos_serv .connect_to_signal("ClientNotFound", @@ -617,15 +624,19 @@ for path, client in mandos_clients.items(): client_proxy_object = self.bus.get_object(self.busname, path) - self.add_client(MandosClientWidget( - server_proxy_object=self.mandos_serv, - proxy_object=client_proxy_object, - properties=client, - update_hook=self.refresh, - delete_hook=self.remove_client, - logger=self.log_message), + self.add_client(MandosClientWidget(server_proxy_object + =self.mandos_serv, + proxy_object + =client_proxy_object, + properties=client, + update_hook + =self.refresh, + delete_hook + =self.remove_client, + logger + =self.log_message), path=path) - + self.refresh() self._input_callback_tag = (GLib.io_add_watch (sys.stdin.fileno(), @@ -635,28 +646,28 @@ # Main loop has finished, we should close everything now GLib.source_remove(self._input_callback_tag) self.screen.stop() - + def stop(self): self.main_loop.quit() - + def process_input(self, source, condition): keys = self.screen.get_input() - translations = {"ctrl n": "down", # Emacs - "ctrl p": "up", # Emacs - "ctrl v": "page down", # Emacs - "meta v": "page up", # Emacs - " ": "page down", # less - "f": "page down", # less - "b": "page up", # less - "j": "down", # vi - "k": "up", # vi - } + translations = { "ctrl n": "down", # Emacs + "ctrl p": "up", # Emacs + "ctrl v": "page down", # Emacs + "meta v": "page up", # Emacs + " ": "page down", # less + "f": "page down", # less + "b": "page up", # less + "j": "down", # vi + "k": "up", # vi + } for key in keys: try: key = translations[key] except KeyError: # :-) pass - + if key == "q" or key == "Q": self.stop() break @@ -710,30 +721,29 @@ else: self.log_level = 0 self.log_message("Verbose mode: On") - # elif (key == "end" or key == "meta >" or key == "G" - # or key == ">"): - # pass # xxx end-of-buffer - # elif (key == "home" or key == "meta <" or key == "g" - # or key == "<"): - # pass # xxx beginning-of-buffer - # elif key == "ctrl e" or key == "$": - # pass # xxx move-end-of-line - # elif key == "ctrl a" or key == "^": - # pass # xxx move-beginning-of-line - # elif key == "ctrl b" or key == "meta (" or key == "h": - # pass # xxx left - # elif key == "ctrl f" or key == "meta )" or key == "l": - # pass # xxx right - # elif key == "a": - # pass # scroll up log - # elif key == "z": - # pass # scroll down log + #elif (key == "end" or key == "meta >" or key == "G" + # or key == ">"): + # pass # xxx end-of-buffer + #elif (key == "home" or key == "meta <" or key == "g" + # or key == "<"): + # pass # xxx beginning-of-buffer + #elif key == "ctrl e" or key == "$": + # pass # xxx move-end-of-line + #elif key == "ctrl a" or key == "^": + # pass # xxx move-beginning-of-line + #elif key == "ctrl b" or key == "meta (" or key == "h": + # pass # xxx left + #elif key == "ctrl f" or key == "meta )" or key == "l": + # pass # xxx right + #elif key == "a": + # pass # scroll up log + #elif key == "z": + # pass # scroll down log elif self.topwidget.selectable(): self.topwidget.keypress(self.size, key) self.refresh() return True - ui = UserInterface() try: ui.run() === modified file 'mandos-monitor.xml' --- mandos-monitor.xml 2017-02-23 19:11:11 +0000 +++ mandos-monitor.xml 2016-03-05 21:46:00 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -38,7 +38,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'mandos.conf.xml' --- mandos.conf.xml 2017-02-23 19:11:11 +0000 +++ mandos.conf.xml 2016-03-05 21:42:56 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/mandos.conf"> - + %common; ]> @@ -41,7 +41,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'mandos.lsm' --- mandos.lsm 2017-06-24 10:08:21 +0000 +++ mandos.lsm 2016-03-23 07:11:22 +0000 @@ -1,7 +1,7 @@ Begin4 Title: Mandos -Version: 1.7.15 -Entered-date: 2017-06-24 +Version: 1.7.7 +Entered-date: 2016-03-19 Description: The Mandos system allows computers to have encrypted root file systems and at the same time be capable of remote and/or unattended reboots. @@ -12,9 +12,9 @@ Maintained-by: teddy@recompile.se (Teddy Hogeborn), belorn@recompile.se (Björn Påhlsson) Primary-site: https://www.recompile.se/mandos - 173K mandos_1.7.15.orig.tar.gz + 172K mandos_1.7.7.orig.tar.gz Alternate-site: ftp://ftp.recompile.se/pub/mandos - 173K mandos_1.7.15.orig.tar.gz + 172K mandos_1.7.7.orig.tar.gz Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.7, and various other libraries. While made for Debian GNU/Linux, it is probably portable to other === modified file 'mandos.service' --- mandos.service 2017-08-20 14:14:14 +0000 +++ mandos.service 2016-03-13 00:37:02 +0000 @@ -8,7 +8,7 @@ ## If the server is configured to not use ZeroConf, these two lines ## become unnecessary and should be removed or commented out. After=avahi-daemon.service -Requisite=avahi-daemon.service +RequisiteOverridable=avahi-daemon.service [Service] ## If the server's D-Bus interface is disabled, the "BusName" setting @@ -28,8 +28,6 @@ ProtectSystem=full ProtectHome=yes CapabilityBoundingSet=CAP_KILL CAP_SETGID CAP_SETUID CAP_DAC_OVERRIDE CAP_NET_RAW -ProtectKernelTunables=yes -ProtectControlGroups=yes [Install] WantedBy=multi-user.target === modified file 'mandos.xml' --- mandos.xml 2017-02-23 19:11:11 +0000 +++ mandos.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -708,7 +707,7 @@ - GnuTLS + GnuTLS === modified file 'network-hooks.d/bridge' --- network-hooks.d/bridge 2017-08-20 14:08:59 +0000 +++ network-hooks.d/bridge 2012-06-13 22:06:57 +0000 @@ -29,7 +29,7 @@ . "$CONFIG" fi -if [ -z "$BRIDGE" ] || [ -z "$PORT_ADDRESSES" ]; then +if [ -z "$BRIDGE" -o -z "$PORT_ADDRESSES" ]; then exit fi === modified file 'plugin-helpers/mandos-client-iprouteadddel.c' --- plugin-helpers/mandos-client-iprouteadddel.c 2017-02-23 19:11:11 +0000 +++ plugin-helpers/mandos-client-iprouteadddel.c 2016-02-28 14:22:10 +0000 @@ -2,8 +2,8 @@ /* * iprouteadddel - Add or delete direct route to a local IP address * - * Copyright © 2015-2017 Teddy Hogeborn - * Copyright © 2015-2017 Björn Påhlsson + * Copyright © 2015-2016 Teddy Hogeborn + * Copyright © 2015-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugin-runner.c' --- plugin-runner.c 2017-02-23 19:11:11 +0000 +++ plugin-runner.c 2016-03-17 21:14:12 +0000 @@ -2,8 +2,8 @@ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -792,7 +792,7 @@ } if(debug){ - for(plugin *p = plugin_list; p != NULL; p = p->next){ + for(plugin *p = plugin_list; p != NULL; p=p->next){ fprintf(stderr, "Plugin: %s has %d arguments\n", p->name ? p->name : "Global", p->argc - 1); for(char **a = p->argv; *a != NULL; a++){ @@ -807,7 +807,7 @@ if(getuid() == 0){ /* Work around Debian bug #633582: - */ + */ int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY); if(plugindir_fd == -1){ if(errno != ENOENT){ === modified file 'plugin-runner.xml' --- plugin-runner.xml 2017-02-23 19:11:11 +0000 +++ plugin-runner.xml 2016-03-17 21:18:37 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2017-02-23 19:11:11 +0000 +++ plugins.d/askpass-fifo.c 2016-02-28 14:22:10 +0000 @@ -2,8 +2,8 @@ /* * Askpass-FIFO - Read a password from a FIFO and output it * - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/askpass-fifo.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2017-02-23 19:11:11 +0000 +++ plugins.d/mandos-client.c 2016-03-05 20:11:10 +0000 @@ -9,8 +9,8 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -47,8 +47,7 @@ strtof(), abort() */ #include /* bool, false, true */ #include /* strcmp(), strlen(), strerror(), - asprintf(), strncpy(), strsignal() - */ + asprintf(), strncpy() */ #include /* ioctl */ #include /* socket(), inet_pton(), sockaddr, sockaddr_in6, PF_INET6, @@ -626,7 +625,6 @@ safer_gnutls_strerror(ret)); dhparamsfilename = NULL; } - free(params.data); } while(false); } if(dhparamsfilename == NULL){ @@ -1079,9 +1077,8 @@ bool match = false; { char *interface = NULL; - while((interface = argz_next(mc->interfaces, - mc->interfaces_size, - interface))){ + while((interface=argz_next(mc->interfaces, mc->interfaces_size, + interface))){ if(if_nametoindex(interface) == (unsigned int)if_index){ match = true; break; @@ -1240,7 +1237,7 @@ with an explicit route added with the server's address. Avahi bug reference: - https://lists.freedesktop.org/archives/avahi/2010-February/001833.html + http://lists.freedesktop.org/archives/avahi/2010-February/001833.html https://bugs.debian.org/587961 */ if(debug){ @@ -1426,7 +1423,6 @@ &decrypted_buffer, mc); if(decrypted_buffer_size >= 0){ - clearerr(stdout); written = 0; while(written < (size_t) decrypted_buffer_size){ if(quit_now){ @@ -1448,16 +1444,6 @@ } written += (size_t)ret; } - ret = fflush(stdout); - if(ret != 0){ - int e = errno; - if(debug){ - fprintf_plus(stderr, "Error writing encrypted data: %s\n", - strerror(errno)); - } - errno = e; - goto mandos_end; - } retval = 0; } } @@ -1494,6 +1480,7 @@ return retval; } +__attribute__((nonnull)) static void resolve_callback(AvahiSServiceResolver *r, AvahiIfIndex interface, AvahiProtocol proto, @@ -2196,7 +2183,7 @@ /* Sleep checking until interface is running. Check every 0.25s, up to total time of delay */ - for(int i = 0; i < delay * 4; i++){ + for(int i=0; i < delay * 4; i++){ if(interface_is_running(interface)){ break; } @@ -2498,7 +2485,7 @@ { /* Work around Debian bug #633582: - */ + */ /* Re-raise privileges */ ret = raise_privileges(); @@ -2959,13 +2946,7 @@ end: if(debug){ - if(signal_received){ - fprintf_plus(stderr, "%s exiting due to signal %d: %s\n", - argv[0], signal_received, - strsignal(signal_received)); - } else { - fprintf_plus(stderr, "%s exiting\n", argv[0]); - } + fprintf_plus(stderr, "%s exiting\n", argv[0]); } /* Cleanup things */ @@ -3023,9 +3004,9 @@ /* Take down the network interfaces which were brought up */ { char *interface = NULL; - while((interface = argz_next(interfaces_to_take_down, - interfaces_to_take_down_size, - interface))){ + while((interface=argz_next(interfaces_to_take_down, + interfaces_to_take_down_size, + interface))){ ret = take_down_interface(interface); if(ret != 0){ errno = ret; === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/mandos-client.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -843,7 +842,8 @@ - GnuTLS + GnuTLS @@ -855,7 +855,7 @@ - GPGME @@ -899,12 +899,12 @@ - RFC 5246: The Transport Layer Security (TLS) - Protocol Version 1.2 + RFC 4346: The Transport Layer Security (TLS) + Protocol Version 1.1 - TLS 1.2 is the protocol implemented by GnuTLS. + TLS 1.1 is the protocol implemented by GnuTLS. @@ -921,7 +921,7 @@ - RFC 6091: Using OpenPGP Keys for Transport Layer + RFC 5081: Using OpenPGP Keys for Transport Layer Security === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2017-06-24 10:08:21 +0000 +++ plugins.d/password-prompt.c 2016-02-28 14:22:10 +0000 @@ -2,8 +2,8 @@ /* * Password-prompt - Read a password from the terminal and print it * - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -215,12 +215,6 @@ if(ret == -1){ error_plus(1, errno, "scandir"); } - { - int i = ret; - while(i--){ - free(direntries[i]); - } - } free(direntries); return ret > 0; } === modified file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/password-prompt.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2017-02-23 19:11:11 +0000 +++ plugins.d/plymouth.c 2016-03-17 20:40:55 +0000 @@ -2,8 +2,8 @@ /* * Plymouth - Read a password from Plymouth and output it * - * Copyright © 2010-2017 Teddy Hogeborn - * Copyright © 2010-2017 Björn Påhlsson + * Copyright © 2010-2016 Teddy Hogeborn + * Copyright © 2010-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -181,7 +181,7 @@ } char **tmp; int i = 0; - for (; argv[i] != NULL; i++){ + for (; argv[i]!=NULL; i++){ tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2)); if(tmp == NULL){ error_plus(0, errno, "realloc"); === modified file 'plugins.d/plymouth.xml' --- plugins.d/plymouth.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/plymouth.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -38,7 +38,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2017-02-23 19:11:11 +0000 +++ plugins.d/splashy.c 2016-02-28 14:22:10 +0000 @@ -2,8 +2,8 @@ /* * Splashy - Read a password from splashy and output it * - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/splashy.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2017-02-23 19:11:11 +0000 +++ plugins.d/usplash.c 2016-02-28 14:22:10 +0000 @@ -2,8 +2,8 @@ /* * Usplash - Read a password from usplash and output it * - * Copyright © 2008-2017 Teddy Hogeborn - * Copyright © 2008-2017 Björn Påhlsson + * Copyright © 2008-2016 Teddy Hogeborn + * Copyright © 2008-2016 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as === modified file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/usplash.xml 2016-03-05 21:42:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -40,7 +40,6 @@ 2014 2015 2016 - 2017 Teddy Hogeborn Björn Påhlsson