=== modified file 'DBUS-API' --- DBUS-API 2017-02-23 19:11:11 +0000 +++ DBUS-API 2012-01-15 20:27:28 +0000 @@ -13,25 +13,34 @@ | Path | Object | |-----------------------+-------------------| | "/" | The Mandos Server | - - (To get a list of paths to client objects, use the standard D-Bus - org.freedesktop.DBus.ObjectManager interface, which the server - object supports.) - - + | "/clients/CLIENTNAME" | Mandos Client | + + * Mandos Server Interface: Interface name: "se.recompile.Mandos" ** Methods: +*** GetAllClients() → (ao: Clients) + Returns an array of all client D-Bus object paths + +*** GetAllClientsWithProperties() → (a{oa{sv}}: ClientProperties) + Returns an array of all clients and all their properties + *** RemoveClient(o: ObjectPath) → nothing Removes a client ** Signals: +*** ClientAdded(o: ObjectPath) + A new client was added. + *** ClientNotFound(s: Fingerprint, s: Address) A client connected from Address using Fingerprint, but was rejected because it was not found in the server. The fingerprint is represented as a string of hexadecimal digits. The address is an IPv4 or IPv6 address in its normal string format. + +*** ClientRemoved(o: ObjectPath, s: Name) + A client named Name on ObjectPath was removed. * Mandos Client Interface: @@ -46,6 +55,20 @@ Assert that this client has been checked and found to be alive. This will restart the timeout before disabling this client. See also the "LastCheckedOK" property. + +*** Disable() → nothing + Disable this client. See also the "Enabled" property. + +*** Enable() → nothing + Enable this client. See also the "Enabled" property. + +*** StartChecker() → nothing + Start a new checker for this client, if none is currently + running. See also the "CheckerRunning" property. + +*** StopChecker() → nothing + Abort a running checker process for this client, if any. See also + the "CheckerRunning" property. ** Properties @@ -73,6 +96,7 @@ | LastCheckerStatus (i) | n | Read | N/A | | LastEnabled (j) | s | Read | N/A | | Name | s | Read | (Section name) | + | ObjectPath | o | Read | N/A | | Secret (k) | ay | Write | secret (or secfile) | | Timeout (a) | t | Read/Write | timeout | @@ -80,12 +104,13 @@ b) An approval is currently pending. - c) Changing this property can either start a new checker or abort a - running one. + c) Setting this property is equivalent to calling StartChecker() or + StopChecker(). d) The creation time of this client object, as an RFC 3339 string. - e) Changing this property enables or disables a client. + e) Setting this property is equivalent to calling Enable() or + Disable(). f) The date and time this client will be disabled, as an RFC 3339 string, or an empty string if this is not scheduled. @@ -125,13 +150,16 @@ milliseconds, depending on ApprovedByDefault. Approve() can now usefully be called on this client object. +*** PropertyChanged(s: Property, v: Value) + The Property on this client has changed to Value. + *** Rejected(s: Reason) This client was not given its secret for a specified Reason. * Copyright - Copyright © 2010-2017 Teddy Hogeborn - Copyright © 2010-2017 Björn Påhlsson + Copyright © 2010-2012 Teddy Hogeborn + Copyright © 2010-2012 Björn Påhlsson ** License: === modified file 'INSTALL' --- INSTALL 2016-07-03 03:32:28 +0000 +++ INSTALL 2013-06-23 15:13:06 +0000 @@ -4,15 +4,14 @@ ** Operating System - Debian 8.0 "jessie" or Ubuntu 15.10 "Wily Werewolf" (or later). + Debian 6.0 "squeeze" or Ubuntu 10.10 "Maverick Meerkat". This is mostly for the support scripts which make sure that the client is installed and started in the initial RAM disk environment - and that the initial RAM file system image file is automatically - made unreadable. The server and client programs themselves *could* - be run in other distributions, but they *are* specific to GNU/Linux - systems, and are not written with portabillity to other Unixes in - mind. + and that the initrd.img file is automatically made unreadable. The + server and client programs themselves *could* be run in other + distributions, but they *are* specific to GNU/Linux systems, and + are not written with portabillity to other Unixes in mind. ** Libraries @@ -28,7 +27,7 @@ + DocBook 4.5 http://www.docbook.org/ Note: DocBook 5.0 is not compatible. + DocBook XSL stylesheets 1.71.0 - http://wiki.docbook.org/DocBookXslStylesheets + http://wiki.docbook.org/topic/DocBookXslStylesheets Package names: docbook docbook-xsl @@ -38,39 +37,32 @@ "man -l mandos.8". *** Mandos Server - + GnuTLS 3.3 https://www.gnutls.org/ + + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ + 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/ - + PyGObject 3.7.1 https://wiki.gnome.org/Projects/PyGObject - + pkg-config https://www.freedesktop.org/wiki/Software/pkg-config/ - + Urwid 1.0.1 http://urwid.org/ - (Only needed by the "mandos-monitor" tool.) + + Python 2.6 http://www.python.org/ + + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/ + + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/ + + Urwid 0.9.8.3 http://excess.org/urwid/ Strongly recommended: - + fping 2.4b2-to-ipv6 http://www.fping.org/ - + ssh-keyscan from OpenSSH http://www.openssh.com/ + + fping 2.4b2-to-ipv6 http://www.fping.com/ Package names: - avahi-daemon python python-dbus python-gi python-urwid pkg-config - fping ssh-client + python-gnutls avahi-daemon python python-avahi python-dbus + python-gobject python-urwid *** Mandos Client - + 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/ - + 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/ - - Strongly recommended: - + OpenSSH http://www.openssh.com/ + http://packages.qa.debian.org/i/initramfs-tools.html + + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ + + Avahi 0.6.16 http://www.avahi.org/ + + GnuPG 1.4.9 http://www.gnupg.org/ + + GPGME 1.1.6 http://www.gnupg.org/related_software/gpgme/ Package names: initramfs-tools libgnutls-dev libavahi-core-dev gnupg - libgpgme11-dev pkg-config ssh + libgpgme11-dev * Installing the Mandos server @@ -106,11 +98,15 @@ and append this to the file "/etc/mandos/clients.conf" *on the server computer*. - 4. Configure the client to use any special configuration needed for - your local system. Note: This is not necessary if the server is - present on the same wired local network as the client. If you do - make changes to /etc/mandos/plugin-runner.conf, the initrd.img - file must be updated, possibly using the following command: + 4. Configure the client to use the correct network interface. The + interface to use is automatically chosen at boot, and if this + needs to be adjusted, it will be necessary to edit + /etc/initramfs-tools/initramfs.conf to change the DEVICE setting + there. Alternatively, the file /etc/mandos/plugin-runner.conf + can be edited to add a "--device" parameter for the + mandos-client(8) plugin. Please note: If any of those files are + changed, the initrd.img file must be updated, possibly using the + following command: # update-initramfs -k all -u @@ -130,14 +126,12 @@ After this, the client computer should be able to reboot without needing a password entered on the console, as long as it does not - take more than five minutes to reboot. + take more than an hour to reboot. * Further customizations You may want to tighten or loosen the timeouts in the server configuration files; see mandos.conf(5) and mandos-clients.conf(5). - If IPsec is not used and SSH is not installed, it is suggested that - a more cryptographically secure checker program is used and - configured, since, without IPsec, ping packets can be faked. - -#+STARTUP: showall + If IPsec is not used, it is suggested that a more cryptographically + secure checker program is used and configured, since without IPsec + ping packets can be faked. === modified file 'Makefile' --- Makefile 2017-02-23 20:35:20 +0000 +++ Makefile 2012-06-22 23:33:56 +0000 @@ -1,33 +1,16 @@ -WARN=-O -Wall -Wextra -Wdouble-promotion -Wformat=2 -Winit-self \ - -Wmissing-include-dirs -Wswitch-default -Wswitch-enum \ - -Wunused -Wuninitialized -Wstrict-overflow=5 \ - -Wsuggest-attribute=pure -Wsuggest-attribute=const \ - -Wsuggest-attribute=noreturn -Wfloat-equal -Wundef -Wshadow \ +WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs \ + -Wswitch-default -Wswitch-enum -Wunused-parameter \ + -Wstrict-aliasing=1 -Wextra -Wfloat-equal -Wundef -Wshadow \ -Wunsafe-loop-optimizations -Wpointer-arith \ -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \ - -Wconversion -Wlogical-op -Waggregate-return \ - -Wstrict-prototypes -Wold-style-definition \ - -Wmissing-format-attribute -Wnormalized=nfc -Wpacked \ - -Wredundant-decls -Wnested-externs -Winline -Wvla \ - -Wvolatile-register-var -Woverlength-strings -#DEBUG=-ggdb3 -fsanitize=address -# For info about _FORTIFY_SOURCE, see feature_test_macros(7) -# and . + -Wconversion -Wstrict-prototypes -Wold-style-definition \ + -Wpacked -Wnested-externs -Winline -Wvolatile-register-var +# -Wunreachable-code +#DEBUG=-ggdb3 +# For info about _FORTIFY_SOURCE, see +# +# and . FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC -# -ALL_SANITIZE_OPTIONS:=-fsanitize=leak -fsanitize=undefined \ - -fsanitize=shift -fsanitize=integer-divide-by-zero \ - -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null \ - -fsanitize=return -fsanitize=signed-integer-overflow \ - -fsanitize=bounds -fsanitize=alignment \ - -fsanitize=object-size -fsanitize=float-divide-by-zero \ - -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute \ - -fsanitize=returns-nonnull-attribute -fsanitize=bool \ - -fsanitize=enum -# Check which sanitizing options can be used -SANITIZE:=$(foreach option,$(ALL_SANITIZE_OPTIONS),$(shell \ - echo 'int main(){}' | $(CC) --language=c $(option) /dev/stdin \ - -o /dev/null >/dev/null 2>&1 && echo $(option))) LINK_FORTIFY_LD=-z relro -z now LINK_FORTIFY= @@ -37,14 +20,14 @@ LINK_FORTIFY += -pie endif #COVERAGE=--coverage -OPTIMIZE=-Os -fno-strict-aliasing -LANGUAGE=-std=gnu11 +OPTIMIZE=-Os +LANGUAGE=-std=gnu99 htmldir=man -version=1.7.15 +version=1.6.0 SED=sed USER=$(firstword $(subst :, ,$(shell getent passwd _mandos || getent passwd nobody || echo 65534))) -GROUP=$(firstword $(subst :, ,$(shell getent group _mandos || getent group nogroup || echo 65534))) +GROUP=$(firstword $(subst :, ,$(shell getent group _mandos || getent group nobody || echo 65534))) ## Use these settings for a traditional /usr/local install # PREFIX=$(DESTDIR)/usr/local @@ -53,7 +36,6 @@ # MANDIR=$(PREFIX)/man # INITRAMFSTOOLS=$(DESTDIR)/etc/initramfs-tools # STATEDIR=$(DESTDIR)/var/lib/mandos -# LIBDIR=$(PREFIX)/lib ## ## These settings are for a package-type install @@ -63,20 +45,8 @@ MANDIR=$(PREFIX)/share/man INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools STATEDIR=$(DESTDIR)/var/lib/mandos -LIBDIR=$(shell \ - for d in \ - "/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`" \ - "`rpm --eval='%{_libdir}' 2>/dev/null`" /usr/lib; do \ - if [ -d "$$d" -a "$$d" = "$${d%/}" ]; then \ - echo "$(DESTDIR)$$d"; \ - break; \ - fi; \ - done) ## -SYSTEMD=$(DESTDIR)$(shell pkg-config systemd --variable=systemdsystemunitdir) -TMPFILES=$(DESTDIR)$(shell pkg-config systemd --variable=tmpfilesdir) - GNUTLS_CFLAGS=$(shell pkg-config --cflags-only-I gnutls) GNUTLS_LIBS=$(shell pkg-config --libs gnutls) AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core) @@ -84,14 +54,12 @@ GPGME_CFLAGS=$(shell gpgme-config --cflags; getconf LFS_CFLAGS) GPGME_LIBS=$(shell gpgme-config --libs; getconf LFS_LIBS; \ getconf LFS_LDFLAGS) -LIBNL3_CFLAGS=$(shell pkg-config --cflags-only-I libnl-route-3.0) -LIBNL3_LIBS=$(shell pkg-config --libs libnl-route-3.0) # Do not change these two -CFLAGS+=$(WARN) $(DEBUG) $(FORTIFY) $(SANITIZE) $(COVERAGE) \ - $(OPTIMIZE) $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) \ - $(GPGME_CFLAGS) -DVERSION='"$(version)"' -LDFLAGS+=-Xlinker --as-needed $(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag)) +CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \ + $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \ + -DVERSION='"$(version)"' +LDFLAGS=-Xlinker --as-needed $(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag)) # Commands to format a DocBook document into a manual page DOCBOOKTOMAN=$(strip cd $(dir $<); xsltproc --nonet --xinclude \ @@ -102,10 +70,13 @@ --param man.authors.section.enabled 0 \ /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \ $(notdir $<); \ + $(MANPOST) $(notdir $@);\ if locale --all 2>/dev/null | grep --regexp='^en_US\.utf8$$' \ && type man 2>/dev/null; then LANG=en_US.UTF-8 MANWIDTH=80 \ man --warnings --encoding=UTF-8 --local-file $(notdir $@); \ fi >/dev/null) +# DocBook-to-man post-processing to fix a '\n' escape bug +MANPOST=$(SED) --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g' DOCBOOKTOHTML=$(strip xsltproc --nonet --xinclude \ --param make.year.ranges 1 \ @@ -123,8 +94,7 @@ PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \ plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo \ plugins.d/plymouth -PLUGIN_HELPERS=plugin-helpers/mandos-client-iprouteadddel -CPROGS=plugin-runner $(PLUGINS) $(PLUGIN_HELPERS) +CPROGS=plugin-runner $(PLUGINS) PROGS=mandos mandos-keygen mandos-ctl mandos-monitor $(CPROGS) DOCS=mandos.8 mandos-keygen.8 mandos-monitor.8 mandos-ctl.8 \ mandos.conf.5 mandos-clients.conf.5 plugin-runner.8mandos \ @@ -257,15 +227,9 @@ $(LINK.c) $^ -lrt $(GNUTLS_LIBS) $(AVAHI_LIBS) $(strip\ ) $(GPGME_LIBS) $(LOADLIBES) $(LDLIBS) -o $@ -plugin-helpers/mandos-client-iprouteadddel: plugin-helpers/mandos-client-iprouteadddel.c - $(LINK.c) $(LIBNL3_CFLAGS) $^ $(LIBNL3_LIBS) $(strip\ - ) $(LOADLIBES) $(LDLIBS) -o $@ - -.PHONY : all doc html clean distclean mostlyclean maintainer-clean \ - check run-client run-server install install-html \ - install-server install-client-nokey install-client uninstall \ - uninstall-server uninstall-client purge purge-server \ - purge-client +.PHONY : all doc html clean distclean run-client run-server install \ + install-server install-client uninstall uninstall-server \ + uninstall-client purge purge-server purge-client clean: -rm --force $(CPROGS) $(objects) $(htmldocs) $(DOCS) core @@ -283,24 +247,18 @@ run-client: all keydir/seckey.txt keydir/pubkey.txt @echo "###################################################################" @echo "# The following error messages are harmless and can be safely #" - @echo "# ignored: #" - @echo "# From plugin-runner: setgid: Operation not permitted #" - @echo "# setuid: Operation not permitted #" + @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: 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 "# From mandos-client: setuid: Operation not permitted #" + @echo "# seteuid: Operation not permitted #" + @echo "# klogctl: Operation not permitted #" @echo "###################################################################" -# We set GNOME_KEYRING_CONTROL to block pam_gnome_keyring ./plugin-runner --plugin-dir=plugins.d \ - --plugin-helper-dir=plugin-helpers \ --config-file=plugin-runner.conf \ --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt,--network-hook-dir=network-hooks.d \ - --env-for=mandos-client:GNOME_KEYRING_CONTROL= \ $(CLIENTARGS) # Used by run-client @@ -321,7 +279,7 @@ install --directory confdir install --mode=u=rw $< $@ # Add a client password - ./mandos-keygen --dir keydir --password --no-ssh >> $@ + ./mandos-keygen --dir keydir --password >> $@ statedir: install --directory statedir @@ -334,16 +292,8 @@ install-server: doc install --directory $(CONFDIR) - if install --directory --mode=u=rwx --owner=$(USER) \ - --group=$(GROUP) $(STATEDIR); then \ - :; \ - elif install --directory --mode=u=rwx $(STATEDIR); then \ - chown -- $(USER):$(GROUP) $(STATEDIR) || :; \ - fi - if [ "$(TMPFILES)" != "$(DESTDIR)" -a -d "$(TMPFILES)" ]; then \ - install --mode=u=rw,go=r tmpfiles.d-mandos.conf \ - $(TMPFILES)/mandos.conf; \ - fi + install --directory --mode=u=rwx --owner=$(USER) \ + --group=$(GROUP) $(STATEDIR) install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ mandos-ctl @@ -357,9 +307,6 @@ $(DESTDIR)/etc/dbus-1/system.d/mandos.conf install --mode=u=rwx,go=rx init.d-mandos \ $(DESTDIR)/etc/init.d/mandos - if [ "$(SYSTEMD)" != "$(DESTDIR)" -a -d "$(SYSTEMD)" ]; then \ - install --mode=u=rw,go=r mandos.service $(SYSTEMD); \ - fi install --mode=u=rw,go=r default-mandos \ $(DESTDIR)/etc/default/mandos if [ -z $(DESTDIR) ]; then \ @@ -379,42 +326,37 @@ > $(MANDIR)/man8/intro.8mandos.gz install-client-nokey: all doc - install --directory $(LIBDIR)/mandos $(CONFDIR) + install --directory $(PREFIX)/lib/mandos $(CONFDIR) install --directory --mode=u=rwx $(KEYDIR) \ - $(LIBDIR)/mandos/plugins.d \ - $(LIBDIR)/mandos/plugin-helpers - if [ "$(CONFDIR)" != "$(LIBDIR)/mandos" ]; then \ + $(PREFIX)/lib/mandos/plugins.d + if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ install --mode=u=rwx \ - --directory "$(CONFDIR)/plugins.d" \ - "$(CONFDIR)/plugin-helpers"; \ + --directory "$(CONFDIR)/plugins.d"; \ fi install --mode=u=rwx,go=rx --directory \ "$(CONFDIR)/network-hooks.d" install --mode=u=rwx,go=rx \ - --target-directory=$(LIBDIR)/mandos plugin-runner + --target-directory=$(PREFIX)/lib/mandos plugin-runner install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ mandos-keygen install --mode=u=rwx,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/password-prompt install --mode=u=rwxs,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/mandos-client install --mode=u=rwxs,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/usplash install --mode=u=rwxs,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/splashy install --mode=u=rwxs,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/askpass-fifo install --mode=u=rwxs,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugins.d \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/plymouth - install --mode=u=rwx,go=rx \ - --target-directory=$(LIBDIR)/mandos/plugin-helpers \ - plugin-helpers/mandos-client-iprouteadddel install initramfs-tools-hook \ $(INITRAMFSTOOLS)/hooks/mandos install --mode=u=rw,go=r initramfs-tools-hook-conf \ @@ -465,13 +407,13 @@ ! grep --regexp='^ *[^ #].*keyscript=[^,=]*/mandos/' \ $(DESTDIR)/etc/crypttab -rm --force $(PREFIX)/sbin/mandos-keygen \ - $(LIBDIR)/mandos/plugin-runner \ - $(LIBDIR)/mandos/plugins.d/password-prompt \ - $(LIBDIR)/mandos/plugins.d/mandos-client \ - $(LIBDIR)/mandos/plugins.d/usplash \ - $(LIBDIR)/mandos/plugins.d/splashy \ - $(LIBDIR)/mandos/plugins.d/askpass-fifo \ - $(LIBDIR)/mandos/plugins.d/plymouth \ + $(PREFIX)/lib/mandos/plugin-runner \ + $(PREFIX)/lib/mandos/plugins.d/password-prompt \ + $(PREFIX)/lib/mandos/plugins.d/mandos-client \ + $(PREFIX)/lib/mandos/plugins.d/usplash \ + $(PREFIX)/lib/mandos/plugins.d/splashy \ + $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \ + $(PREFIX)/lib/mandos/plugins.d/plymouth \ $(INITRAMFSTOOLS)/hooks/mandos \ $(INITRAMFSTOOLS)/conf-hooks.d/mandos \ $(INITRAMFSTOOLS)/scripts/init-premount/mandos \ @@ -483,8 +425,8 @@ $(MANDIR)/man8/splashy.8mandos.gz \ $(MANDIR)/man8/askpass-fifo.8mandos.gz \ $(MANDIR)/man8/plymouth.8mandos.gz \ - -rmdir $(LIBDIR)/mandos/plugins.d $(CONFDIR)/plugins.d \ - $(LIBDIR)/mandos $(CONFDIR) $(KEYDIR) + -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \ + $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR) update-initramfs -k all -u purge: purge-server purge-client @@ -494,8 +436,6 @@ $(DESTDIR)/etc/dbus-1/system.d/mandos.conf $(DESTDIR)/etc/default/mandos \ $(DESTDIR)/etc/init.d/mandos \ - $(SYSTEMD)/mandos.service \ - $(DESTDIR)/run/mandos.pid \ $(DESTDIR)/var/run/mandos.pid -rmdir $(CONFDIR) === modified file 'NEWS' --- NEWS 2017-02-23 20:35:20 +0000 +++ NEWS 2012-06-17 22:26:40 +0000 @@ -1,207 +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 - -Version 1.7.6 (2016-03-13) -* Server -** Fix bug where stopping server would time out -** Make server runnable with Python 3 - -Version 1.7.5 (2016-03-08) -* Server -** Fix security restrictions in systemd service file. -** Work around bug where stopping server would time out - -Version 1.7.4 (2016-03-05) -* Client -** Bug fix: Tolerate errors from configure_networking (Debian Bug - #816513) -** Compilation: Only use sanitizing options which work with the - compiler used when building. This should fix compilation with GCC - 4.9 on mips, mipsel, and s390x. -* Server -** Add extra security restrictions in systemd service file. - -Version 1.7.3 (2016-02-29) -* Client -** Bug fix: Remove new type of keyring directory user by GnuPG 2.1. -** Bug fix: Remove "nonnull" attribute from a function argument, which - would otherwise generate a spurious runtime warning. - -Version 1.7.2 (2016-02-28) -* Server -** Stop using python-gnutls library; it was not updated to GnuTLS 3.3. -** Bug fix: Only send D-Bus signal ClientRemoved if using D-Bus. -** Use GnuPG 2 if available. -* Client -** Compile with various sanitizing flags. - -Version 1.7.1 (2015-10-24) -* Client -** Bug fix: Can now really find Mandos server even if the server has - an IPv6 address on a network other than the one which the Mandos - server is on. - -Version 1.7.0 (2015-08-10) -* Server -** Bug fix: Handle local Zeroconf service name collisions better. -** Bug fix: Finally fix "ERROR: Child process vanished" bug. -** Bug fix: Fix systemd service file to start server correctly. -** Bug fix: Be compatible with old 2048-bit DSA keys. -** The D-Bus API now provides the standard D-Bus ObjectManager - interface, and deprecates older functionality. See the DBUS-API - file for the currently recommended API. Note: the original API - still works, but is deprecated. -* Client -** Can now find Mandos server even if the server has an IPv6 address - on a network without IPv6 Router Advertisment (like if the Mandos - client itself is the router, or there is an IPv6 router advertising - a network other than the one which the Mandos server is on.) -** Use a better value than 1024 for the default number of DH bits. - This better value is either provided by a DH parameters file (see - below) or an appropriate number of DH bits is determined based on - the PGP key. -** Bug fix: mandos-keygen now generates correct output for the - "Checker" variable even if the SSH server on the Mandos client has - multiple SSH key types. -** Can now use pre-generated Diffie-Hellman parameters from a file. - -Version 1.6.9 (2014-10-05) -* Server -** Changed to emit standard D-Bus signal when D-Bus properties change. - (The old signal is still emitted too, but marked as deprecated.) - -Version 1.6.8 (2014-08-06) -* Client -** Bug fix: mandos-keygen now generates working SSH checker commands. -* Server -** Bug fix: "mandos-monitor" now really redraws screen on Ctrl-L. -** Now requires Python 2.7. - -Version 1.6.7 (2014-07-17) -* Client -** Bug fix: Now compatible with GPGME 1.5.0. -** Bug fix: Fixed minor memory leaks. -* Server -** "mandos-monitor" now has verbose logging, toggleable with "v". - -Version 1.6.6 (2014-07-13) -* Client -** If client host has an SSH server, "mandos-keygen --password" now - outputs "checker" option which uses "ssh-keyscan"; this is more - secure than the default "fping" checker. -** Bug fix: allow "." in network hook names, to match documentation. -** Better error messages. -* Server -** New --no-zeroconf option. -** Bug fix: Fix --servicename option, broken since 1.6.4. -** Bug fix: Fix --socket option work for --socket=0. - -Version 1.6.5 (2014-05-11) -* Client -** Work around bug in GnuPG -** Give better error messages when run without sufficient privileges -** Only warn if workaround for Debian bug #633582 was necessary and - failed, not if it failed and was unnecessary. - -Version 1.6.4 (2014-02-16) -* Server -** Very minor fix to self-test code. - -Version 1.6.3 (2014-01-21) -* Server -** Add systemd support. -** For PID file, fall back to /var/run if /run does not exist. -* Client -** Moved files from /usr/lib/mandos to whatever the architecture - specifies, like /usr/lib/x86_64-linux-gnu/mandos or - /usr/lib64/mandos. - -Version 1.6.2 (2013-10-24) -* Server -** PID file moved from /var/run to /run. -** Bug fix: Handle long secrets when saving client state. -** Bug fix: Use more magic in the GnuTLS priority string to handle - both old DSA/ELG 2048-bit keys and new RSA/RSA 4096-bit keys. -* Client -** mandos-keygen: Bug fix: now generate RSA keys which GnuTLS can use. - Bug fix: Output passphrase prompts even when - redirecting standard output. - -Version 1.6.1 (2013-10-13) -* Server -** All client options for time intervals now also take an RFC 3339 - duration. The same for all options to mandos-ctl. -** Bug fix: Handle fast checkers (like ":") correctly. -** Bug fix: Don't print output from checkers when running in - foreground. -** Bug fix: Do not fail when client is removed from clients.conf but - saved settings remain. -** Bug fix: mandos-monitor now displays standout (reverse video) again - using new version of Urwid. -** Bug fix: Make boolean options work from the config file again. -** Bug fix: Make --no-ipv6 work again. -** New default priority string to be slightly more compatible with - older versions of GnuTLS. -* Client -** Bug fix: Fix bashism in mandos-keygen. -** Default key and subkey types are now RSA and RSA, respectively. - Also, new default key size is 4096 bits. - Version 1.6.0 (2012-06-18) * Server ** Takes new --foreground option === modified file 'README' --- README 2016-03-23 07:11:22 +0000 +++ README 2012-01-01 20:45:53 +0000 @@ -1,4 +1,4 @@ -Please see: https://www.recompile.se/mandos/man/intro.8mandos +Please see: http://www.recompile.se/mandos/man/intro.8mandos This information previously in this file has been moved to the intro(8mandos) manual page. Go to the above URL, or install the === modified file 'TODO' --- TODO 2017-02-22 21:45:35 +0000 +++ TODO 2013-06-23 15:30:34 +0000 @@ -1,54 +1,46 @@ -*- org -*- +* [[http://www.undeadly.org/cgi?action=article&sid=20110530221728][OpenBSD]] + * Testing ** python-nemu * mandos-applet * mandos-client -** TODO A --server option which only adds to the server list. - (Unlike --connect, which implicitly disables zeroconf.) ** TODO [#B] Use capabilities instead of seteuid(). - https://forums.grsecurity.net/viewtopic.php?f=7&t=2522 +** TODO [#B] Use struct sockaddr_storage instead of a union ** TODO [#B] Use getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton() +** TODO [#B] Use getnameinfo(serv=NULL, NI_NUMERICHOST) instead of inet_ntop() +** TODO [#B] Prefer /run/tmp over /tmp, if it exists ** 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 [#A] 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 +** TODO [#B] Drop privileges after opening FIFO. * 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 * plugin-runner ** TODO handle printing for errors for plugins *** Hook up stderr of plugins, buffer them, and prepend "Mandos Plugin [plugin name]" +** TODO [#B] use scandir(3) instead of readdir(3) ** 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 +** TODO [#B] Use openat() * 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? @@ -62,29 +54,37 @@ + SetPass(u"gazonk", True) -> Approval, persistent + Approve(False) -> Close client connection immediately ** TODO [#C] python-parsedatetime +** TODO [#C] systemd/launchd + http://0pointer.de/blog/projects/systemd.html + http://wiki.debian.org/systemd ** TODO Separate logging logic to own object -** TODO [#B] Limit approval_delay to max gnutls/tls timeout value +** TODO [#A] Limit approval_delay to max gnutls/tls timeout value ** TODO [#B] break the wait on approval_delay if connection dies ** TODO Generate Client.runtime_expansions from client options + extra ** TODO Allow %%(checker)s as a runtime expansion +** TODO Use python-tlslite? ** TODO D-Bus AddClient() method on server object ** TODO Use org.freedesktop.DBus.Method.NoReply annotation on async methods. :2: +** TODO Emit [[http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties][org.freedesktop.DBus.Properties.PropertiesChanged]] signal :2: + TODO Deprecate se.recompile.Mandos.Client.PropertyChanged - annotate! + TODO Can use "invalidates" annotation to also emit on changed secret. +** TODO Support [[http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager][org.freedesktop.DBus.ObjectManager]] interface on server object :2: + Deprecate methods GetAllClients(), GetAllClientsWithProperties() + and signals ClientAdded and ClientRemoved. ** 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 -*** File in /usr/lib/sysusers.d to create user+group "_mandos" -** TODO Error handling on error parsing config files -** TODO init.d script error handling -** TODO D-Bus server properties; address, port, interface, etc. :2: -** Python 3 :2: -*** TODO [#C] In Python 3.3, use shlex.quote() instead of re.escape() +** TODO --no-zeroconf (only valid if port or socket is set) + +* mandos.xml +** Add mandos contact info in manual pages * 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 @@ -92,12 +92,12 @@ arguments. * mandos-monitor -** TODO --servicename :BUGS: ** TODO help should be toggleable ** Urwid client data displayer Better view of client data in the listing *** Properties popup ** Print a nice "We are sorry" message, save stack trace to log. +** Rename module "gobject" to "GObject". * mandos-keygen ** TODO "--secfile" option @@ -105,30 +105,21 @@ ** TODO [#B] "--test" option For testing decryption before rebooting. +* Makefile +** TODO [#C] Implement DEB_BUILD_OPTIONS + http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options + * Package ** /usr/share/initramfs-tools/hooks/mandos *** TODO [#C] use same file name rules as run-parts(8) *** TODO [#C] Do not install in initrd.img if configured not to. Use "/etc/initramfs-tools/hooksconf.d/mandos"? -** TODO [#C] $(pkg-config --variable=completionsdir bash-completion) +** TODO [#C] /etc/bash_completion.d/mandos From XML sources directly? * Side Stuff ** TODO Locate which package moves the other bin/sh when busybox is deactivated ** TODO contact owner of package, and ask them to have that shell static in position regardless of busybox -* [[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 === removed file 'bugs.xml' --- bugs.xml 2016-03-05 21:42:56 +0000 +++ bugs.xml 1970-01-01 00:00:00 +0000 @@ -1,11 +0,0 @@ - - - - Please report bugs to the Mandos development mailing list: - mandos-dev@recompile.se (subscription required). - Note that this list is public. The developers can be reached - privately at mandos@recompile.se (OpenPGP key - fingerprint 153A 37F1 0BBA 0435 987F 2C4A 7223 2973 CA34 - C2C4 for encrypted mail). - === modified file 'common.ent' --- common.ent 2017-02-23 20:35:20 +0000 +++ common.ent 2012-06-17 22:26:40 +0000 @@ -1,3 +1,3 @@ - + === modified file 'debian/changelog' --- debian/changelog 2017-02-23 20:35:20 +0000 +++ debian/changelog 2012-06-17 22:26:40 +0000 @@ -1,322 +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. - * debian/mandos-client.postinst (configure): If older version, fix - permissions on plugin helper directory. Also fix permissions on - plugin helper local override directory (/etc/mandos/plugin-helpers), - but only if not listed by "dpkg-statoverride". - * debian/rules (override_dh_fixperms-arch): Exclude plugin helper - directory from dh_fixperms. - * debian/mandos.postinst (configure): Fix state directory permissions, - but only if not listed by "dpkg-statoverride". - * debian/mandos-client.lintian-overrides: Do not warn about permissions - on plugin helper directory. - * debian/mandos.dirs (usr/lib/tmpfiles.d): Added. - - -- Teddy Hogeborn Sat, 19 Mar 2016 22:58:49 +0100 - -mandos (1.7.6-1) unstable; urgency=medium - - * New upstream release. - * debian/control (Source: mandos/Build-Depends-Indep): Remove - "python-avahi". - (Source: mandos/Build-Depends-Indep): Change "python-gi | - python-gobject" to "python-gi"; i.e. remove "python-gobject". - - -- Teddy Hogeborn Sun, 13 Mar 2016 22:58:23 +0100 - -mandos (1.7.5-1) unstable; urgency=high - - * New upstream release. - * debian/mandos.postinst (configure): If old version was 1.7.4-1 or - 1.7.4-1~bpo8+1, fix situation where clients.pickle file is owned by - root. - - -- Teddy Hogeborn Tue, 08 Mar 2016 01:09:55 +0100 - -mandos (1.7.4-1) unstable; urgency=medium - - * New upstream release. - * initramfs-tools-script: Fix "Call to configure_network in initramfs - script broken due to set -e" by surrounding call by "set +x" and "set - -e" (Closes: #816513) - * debian/control: (Source: mandos/Build-Depends-Indep): Change - "python-gobject | python-gi" to "python-gi | python-gobject" - (Package: mandos/Depends): - '' - - - -- Teddy Hogeborn Sat, 05 Mar 2016 23:10:07 +0100 - -mandos (1.7.3-1) unstable; urgency=medium - - * New upstream release. - - -- Teddy Hogeborn Mon, 29 Feb 2016 22:26:38 +0100 - -mandos (1.7.2-1) unstable; urgency=medium - - * New upstream release. - * Fix "Uses unneeded and obsolete version specific python packages" - by removing version-specific dependencies (Closes: #811159) - * debian/control (Source: mandos/Build-Depends): Add (>= 3.3.0) to - "libgnutls28-dev" and "gnutls-dev". - (Source: mandos/Build-Depends-Indep): Remove "python2.7-gnutls", - "python2.7", "python2.7-dbus", "python2.7-avahi", and - "python2.7-gobject"; replace with "python (>= 2.7), python (<< 3)", - "python-dbus", "python-avahi", "python-gobject | python-gi". - (Package: mandos/Depends): Remove "python-gnutls" and - "python2.7-gnutls", add "libgnutls28-dev (>= 3.3.0) | libgnutls30 (>= - 3.3.0)". Add "python (<< 3)". Remove "python2.7-dbus", - "python2.7-avahi", "python2.7-gobject", and "python2.7-urwid". - Replace "python-gobject" with "python-gobject | python-gi" and "gnupg - (<< 2)" with "gnupg". - (Package: mandos-client/Depends): Replace - "gnupg (<< 2)" with "gnupg". - (Source: mandos/Standards-Version): Change to 3.9.7. - * debian/copyright (Copyright): Update copyright year. - - -- Teddy Hogeborn Sun, 28 Feb 2016 16:09:01 +0100 - -mandos (1.7.1-2) unstable; urgency=medium - - * debian/control (Package: mandos/Depends): Fix "Please drop versioned - dependency on initscripts package" by removing initscripts dependency - (Closes: #804967) - * debian/rules (override_dh_fixperms) Fix "FTBFS when built with - dpkg-buildpackage -A (No such file or directory)" by splitting into - "override_dh_fixperms-arch" and "override_dh_fixperms-indep". - (Closes: #806073) - - -- Teddy Hogeborn Sat, 05 Dec 2015 02:27:40 +0100 - -mandos (1.7.1-1) unstable; urgency=medium - - * New upstream release. - - -- Teddy Hogeborn Sat, 24 Oct 2015 19:43:40 +0200 - -mandos (1.7.0-1) unstable; urgency=medium - - * New upstream release. - * debian/control (Standards-Version): Updated to "3.9.6". - (Build-Depends): Add "libnl-route-3-dev". - (Package: mandos-client/Recommends): Added "gnutls-bin | openssl" for - the generating of DH parameters. - * debian/mandos-client.README.Debian: Update example command line to use - new MANDOSPLUGINHELPERDIR environment variable. Also document the new - dhparams.pem file. - * debian/mandos-client.postinst: Create DH parameters file. - * debian/mandos.prerm: Don't run init script, use only invoke-rc.d. - * debian/mandos-client.postinst: Don't use absolute paths to commands. - * debian/mandos-client.postrm: Don't use absolute paths to commands. - Also remove dhparams.pem file. - * debian/copyright (Copyright): Update copyright year. - * Upstream changed systemd service file to implicitly be of - "Type=dbus". (Closes: #786845) - - -- Teddy Hogeborn Mon, 10 Aug 2015 22:00:29 +0200 - -mandos (1.6.9-1) unstable; urgency=medium - - * New upstream release. - * debian/control (Build-Depends): Fix "still uses GnutLS 2.x" by - changing from "libgnutls-dev" to "libgnutls28-dev | gnutls-dev" - (Closes: #762349) - - -- Teddy Hogeborn Sun, 05 Oct 2014 22:05:06 +0200 - -mandos (1.6.8-1) unstable; urgency=medium - - * New upstream release. - * debian/control (Source: mandos/Build-Depends-Indep): Since upstream - now requires Python 2.7, depend on exactly the python2.7 package and - all the Python 2.7 versions of the python modules. - (Package: mandos/Depends): - '' - but still depend on python (>=2.7) - and the generic versions of the Python modules; this is for mandos-ctl - and mandos-monitor, both of which are compatible with Python 3, and - use #!/usr/bin/python. - - -- Teddy Hogeborn Wed, 06 Aug 2014 22:55:24 +0200 - -mandos (1.6.7-1) unstable; urgency=medium - - * New upstream release. - - -- Teddy Hogeborn Thu, 17 Jul 2014 05:22:45 +0200 - -mandos (1.6.6-1) unstable; urgency=medium - - * New upstream release. - * debian/mandos.postinst: Fix typo in comment. - * debian/control (mandos/Recommends): Changed to "ssh-client | fping". - (mandos-client/Recommends): New; set to "ssh". - - -- Teddy Hogeborn Sun, 13 Jul 2014 22:49:21 +0200 - -mandos (1.6.5-3) unstable; urgency=medium - - * debian/control (mandos-client/Depends): Add "dpkg-dev (>=1.16.0)"; - initramfs-tools-hook runs "dpkg-architecture -qDEB_HOST_MULTIARCH". - (Closes: #750221) - - -- Teddy Hogeborn Fri, 06 Jun 2014 04:27:15 +0200 - -mandos (1.6.5-2) unstable; urgency=medium - - * debian/rules (override_dh_auto_test-arch): New; does nothing. Fixes - FTBFS for build-indep. - - -- Teddy Hogeborn Tue, 13 May 2014 08:08:31 +0200 - -mandos (1.6.5-1) unstable; urgency=medium - - * New upstream release. - * debian/copyright: Change year to "2014". - * debian/control (Build-Depends, Build-Depends-Indep): Moved build - dependencies of "mandos" package to "Build-Depends-Indep". - * debian/upstream/signing-key.asc: New; upstream source public key. - * debian/control (Standards-Version): Updated to "3.9.5". - * debian/control (mandos/Depends): Remove the dependency on - "avahi-daemon (>= 0.6.31-3) | systemd-sysv". It is unnecessary - since we have a workaround in debian/mandos.postinst anyway. - - -- Teddy Hogeborn Sun, 11 May 2014 22:16:33 +0200 - -mandos (1.6.4-1) unstable; urgency=medium - - * New upstream release. - * debian/control (Build-Depends): Add Python dependencies to - successfully run self-tests. - * debian/copyright: GPLv3 now has its own license file - use it. - * debian/watch: Set PGP signature URL. - - -- Teddy Hogeborn Sun, 16 Feb 2014 14:09:25 +0100 - -mandos (1.6.3-1) unstable; urgency=low - - * New upstream release. - * debian/control (Build-Depends): Added "systemd". - * debian/mandos.dirs (lib/systemd/system): New. - * debian/mandos-client.README.Debian: Refer to architecture libdir. - * debian/control (mandos/Depends): Add "avahi-daemon (>= 0.6.31-3) | - systemd-sysv". - * debian/mandos.postinst: If avahi-daemon is version 0.6.31-2 or older, - edit /etc/init.d script headers Required-Start - and Required-Stop to have "avahi" instead of - "avahi-daemon", before insserv(8) sees it. - * debian/mandos-client.lintian-overrides: Libdir changes. - * debian/rules (override_dh_fixperms): - '' - - - -- Teddy Hogeborn Tue, 21 Jan 2014 22:01:30 +0100 - -mandos (1.6.2-1) unstable; urgency=low - - * New upstream release. - * debian/compat: Changed to "9". - * debian/control (Build-Depends): Changed debhelper version to (>= 9). - (Standards-Version): Updated to "3.9.4". - (DM-Upload-Allowed): Removed. - (mandos/Depends): Add "initscripts (>= 2.88dsf-13.3)" to be able to - use the "/run" directory (for mandos.pid). - * debian/copyright (Copyright): Update year. - * Fix "Mandos/gnutls fails to establish connection, "an algorithm that - is not enabled was negotiated"" fixed by upstream. (Closes: #702120) - - -- Teddy Hogeborn Thu, 24 Oct 2013 22:33:40 +0200 - -mandos (1.6.1-1) unstable; urgency=low - - * New upstream release. - * debian/control (mandos/Depends): No longer depends on - python-gnupginterface, but does - depend on gnupg (<< 2). - (Build-Depends): Depend on debhelper 8.9.7 for using "override-*-arch" - and "override-*-indep" targets in debian/rules. - * debian/mandos-client.README: Update Linux documentation link. - * debian/rules: Completely rewritten to use debhelper v7. - * initramfs-tools-hook: Bug fix: Make sure the right version of GnuPG is - copied into the initramfs image. Always assume that GPGME is used to - avoid searching for it since the path might not be /usr/lib. Thanks - to Félix Sipma for the initial bug report, - and also thanks to Dick Middleton for some more - debugging. (Closes: #721903) - * Fix "bashism in /bin/sh script" fixed by upstream. (Closes: #690639) - - -- Teddy Hogeborn Sun, 13 Oct 2013 19:03:23 +0200 - mandos (1.6.0-1) unstable; urgency=low * New upstream release. === modified file 'debian/compat' --- debian/compat 2013-10-20 15:25:09 +0000 +++ debian/compat 2008-09-17 00:34:09 +0000 @@ -1,1 +1,1 @@ -9 +7 === modified file 'debian/control' --- debian/control 2017-01-21 22:22:44 +0000 +++ debian/control 2013-10-05 19:34:40 +0000 @@ -4,24 +4,21 @@ Maintainer: Mandos Maintainers 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 -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 -Homepage: https://www.recompile.se/mandos +Build-Depends: debhelper (>= 8.9.7), docbook-xml, docbook-xsl, + libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc, + pkg-config +Standards-Version: 3.9.3 +Vcs-Bzr: http://ftp.recompile.se/pub/mandos/trunk +Vcs-Browser: http://bzr.recompile.se/loggerhead/mandos/trunk/files +Homepage: http://www.recompile.se/mandos +DM-Upload-Allowed: yes Package: mandos Architecture: all -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) -Recommends: ssh-client | fping +Depends: ${misc:Depends}, python (>=2.6), python-gnutls, python-dbus, + python-avahi, python-gobject, avahi-daemon, adduser, + python-urwid, python (>=2.7) | python-argparse, gnupg (<< 2) +Recommends: fping Description: server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows computers to have encrypted root file systems and at the @@ -40,8 +37,7 @@ Package: mandos-client Architecture: linux-any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup, - initramfs-tools, dpkg-dev (>=1.16.0) -Recommends: ssh, gnutls-bin | openssl + gnupg (<< 2), initramfs-tools Breaks: dropbear (<= 0.53.1-1) Enhances: cryptsetup Description: do unattended reboots with an encrypted root file system === modified file 'debian/copyright' --- debian/copyright 2017-02-23 19:11:11 +0000 +++ debian/copyright 2012-06-01 21:48:12 +0000 @@ -1,11 +1,11 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Mandos Upstream-Contact: Mandos -Source: +Source: Files: * -Copyright: Copyright © 2008-2017 Teddy Hogeborn - Copyright © 2008-2017 Björn Påhlsson +Copyright: Copyright © 2008-2012 Teddy Hogeborn + Copyright © 2008-2012 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 @@ -22,4 +22,4 @@ . . On Debian systems, the complete text of the GNU General Public - License can be found in "/usr/share/common-licenses/GPL-3". + License can be found in "/usr/share/common-licenses/GPL". === modified file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2016-06-21 19:47:08 +0000 +++ debian/mandos-client.README.Debian 2013-09-29 15:52:19 +0000 @@ -1,7 +1,3 @@ -This file documents the next steps to take after installation of the -Debian package, and also contain some notes specific to the Debian -packaging which are not also in the manual. - * Adding a Client Password to the Server The server must be given a password to give back to the client on @@ -20,10 +16,7 @@ is possible to verify that the correct password will be received by this client by running the command, on the client: - MANDOSPLUGINHELPERDIR=/usr/lib/$(dpkg-architecture \ - -qDEB_HOST_MULTIARCH)/mandos/plugin-helpers \ - /usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH \ - )/mandos/plugins.d/mandos-client \ + /usr/lib/mandos/plugins.d/mandos-client \ --pubkey=/etc/keys/mandos/pubkey.txt \ --seckey=/etc/keys/mandos/seckey.txt; echo @@ -43,9 +36,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,20 +83,11 @@ "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 work, "--options-for=mandos-client:--connect=
:" needs to be manually added to the file "/etc/mandos/plugin-runner.conf". -* Diffie-Hellman Parameters - - On installation, a file with Diffie-Hellman parameters, - /etc/keys/mandos/dhparams.pem, will be generated and automatically - installed into the initital RAM disk image and also used by the - Mandos Client on boot. If different parameters are needed for - 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, 23 Jun 2013 17:31:53 +0200 === modified file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2016-03-19 04:21:00 +0000 +++ debian/mandos-client.lintian-overrides 2010-10-13 06:12:52 +0000 @@ -2,14 +2,12 @@ # mandos-client binary: non-standard-dir-perm etc/keys/mandos/ 0700 != 0755 -# The directory /usr/lib//mandos/plugins.d contains setuid -# binaries which are not meant to be run outside an initial RAM disk +# The directory /usr/lib/mandos/plugins.d contains setuid binaries +# which are not meant to be run outside an initial RAM disk # environment (except for test purposes). It would be insecure to # allow anyone to run them. # -mandos-client binary: non-standard-dir-perm usr/lib/*/mandos/plugins.d/ 0700 != 0755 -# Likewise for helper executables for plugins -mandos-client binary: non-standard-dir-perm usr/lib/*/mandos/plugin-helpers/ 0700 != 0755 +mandos-client binary: non-standard-dir-perm usr/lib/mandos/plugins.d/ 0700 != 0755 # These binaries must be setuid root, since they need root powers, but # are started by plugin-runner(8mandos), which runs all plugins as @@ -17,16 +15,14 @@ # system, but in an initial RAM disk environment. Here they are # protected from non-root access by the directory permissions, above. # -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/mandos-client 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/askpass-fifo 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/splashy 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/usplash 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/plymouth 4755 root/root +mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root +mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/askpass-fifo 4755 root/root +mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/splashy 4755 root/root +mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/usplash 4755 root/root +mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/plymouth 4755 root/root # The directory /etc/mandos/plugins.d can be used by local system # administrators to place plugins in, overriding and complementing -# /usr/lib//mandos/plugins.d, and must be likewise protected. +# /usr/lib/mandos/plugins.d, and must be likewise protected. # mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755 -# Likewise for plugin-helpers directory -mandos-client binary: non-standard-dir-perm etc/mandos/plugin-helpers/ 0700 != 0755 === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2016-10-09 22:35:41 +0000 +++ debian/mandos-client.postinst 2011-10-10 20:29:58 +0000 @@ -20,7 +20,9 @@ # Update the initial RAM file system image update_initramfs() { - update-initramfs -u -k all + if [ -x /usr/sbin/update-initramfs ]; then + update-initramfs -u -k all + fi if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then # Make old initrd.img files unreadable too, in case they were @@ -56,52 +58,16 @@ -a -r /etc/keys/mandos/seckey.txt ]; then return 0 fi - mandos-keygen - gpg-connect-agent KILLAGENT /bye || : -} - -create_dh_params(){ - if [ -r /etc/keys/mandos/dhparams.pem ]; then - return 0 - fi - # Create a Diffe-Hellman parameters file - DHFILE="`mktemp -t mandos-client-dh-parameters.XXXXXXXXXX.pem`" - # First try certtool from GnuTLS - if ! certtool --generate-dh-params --sec-param high \ - --outfile "$DHFILE"; then - # Otherwise try OpenSSL - if ! openssl genpkey -genparam -algorithm DH -out "$DHFILE" \ - -pkeyopt dh_paramgen_prime_len:3072; then - # None of the commands succeded; give up - rm -- "$DHFILE" - return 1 - fi - fi - sed --in-place --expression='0,/^-----BEGIN DH PARAMETERS-----$/d' \ - "$DHFILE" - sed --in-place --expression='1i-----BEGIN DH PARAMETERS-----' \ - "$DHFILE" - cp --archive "$DHFILE" /etc/keys/mandos/dhparams.pem - rm -- "$DHFILE" + if [ -x /usr/sbin/mandos-keygen ]; then + mandos-keygen + fi } case "$1" in configure) add_mandos_user "$@" create_key "$@" - create_dh_params "$@" || : update_initramfs "$@" - if dpkg --compare-versions "$2" lt-nl "1.7.10-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 - chmod u=rwx,go= -- "$PLUGINHELPERDIR" - fi - if ! dpkg-statoverride --list /etc/mandos/plugin-helpers \ - >/dev/null 2>&1; then - chmod u=rwx,go= -- /etc/mandos/plugin-helpers - fi - fi ;; abort-upgrade|abort-deconfigure|abort-remove) ;; === modified file 'debian/mandos-client.postrm' --- debian/mandos-client.postrm 2015-07-27 09:23:56 +0000 +++ debian/mandos-client.postrm 2011-10-10 20:29:58 +0000 @@ -31,7 +31,9 @@ # Update the initial RAM file system image update_initramfs() { - update-initramfs -u -k all || : + if [ -x /usr/sbin/update-initramfs ]; then + update-initramfs -u -k all + fi } case "$1" in @@ -43,8 +45,7 @@ shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || : rm --force /etc/mandos/plugin-runner.conf \ /etc/keys/mandos/pubkey.txt \ - /etc/keys/mandos/seckey.txt \ - /etc/keys/mandos/dhparams.pem 2>/dev/null + /etc/keys/mandos/seckey.txt 2>/dev/null update_initramfs ;; upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) === modified file 'debian/mandos.dirs' --- debian/mandos.dirs 2016-03-19 12:10:15 +0000 +++ debian/mandos.dirs 2011-11-26 22:22:20 +0000 @@ -5,5 +5,3 @@ etc/dbus-1/system.d usr/sbin var/lib/mandos -lib/systemd/system -usr/lib/tmpfiles.d === 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/mandos.postinst' --- debian/mandos.postinst 2016-03-19 03:48:56 +0000 +++ debian/mandos.postinst 2011-11-26 22:22:20 +0000 @@ -34,25 +34,8 @@ --home /nonexistent --no-create-home --group \ --disabled-password --gecos "Mandos password system" \ _mandos - elif dpkg --compare-versions "$2" eq 1.7.4-1 \ - || dpkg --compare-versions "$2" eq "1.7.4-1~bpo8+1" - then - start=no - if ! [ -f /var/lib/mandos/clients.pickle ]; then - invoke-rc.d mandos stop - start=yes - fi - chown _mandos:_mandos /var/lib/mandos/clients.pickle \ - 2>/dev/null || : - if [ "$start" = yes ]; then - invoke-rc.d mandos start - fi - fi - if ! dpkg-statoverride --list "/var/lib/mandos" >/dev/null \ - 2>&1; then - chown _mandos:_mandos /var/lib/mandos - chmod u=rwx,go= /var/lib/mandos - fi + fi + chown _mandos:_mandos /var/lib/mandos ;; abort-upgrade|abort-deconfigure|abort-remove) @@ -64,16 +47,6 @@ ;; esac -# Avahi version 0.6.31-2 and older provides "avahi" (instead of -# "avahi-daemon") in its /etc/init.d script header. To make -# insserv(8) happy, we edit our /etc/init.d script header to contain -# the correct string before the code added by dh_installinit calls -# update.rc-d, which calls insserv. -avahi_version="`dpkg-query --showformat='${Version}' --show avahi-daemon`" -if dpkg --compare-versions "$avahi_version" le 0.6.31-2; then - sed --in-place --expression='/^### BEGIN INIT INFO$/,/^### END INIT INFO$/s/^\(# Required-\(Stop\|Start\):.*avahi\)-daemon\>/\1/g' /etc/init.d/mandos -fi - #DEBHELPER# exit 0 === modified file 'debian/mandos.prerm' --- debian/mandos.prerm 2015-07-12 01:57:54 +0000 +++ debian/mandos.prerm 2011-10-10 20:29:58 +0000 @@ -17,7 +17,13 @@ case "$1" in remove|deconfigure) - invoke-rc.d mandos stop || : + if [ -x /etc/init.d/mandos ]; then + if [ -x /usr/sbin/invoke-rc.d ]; then + invoke-rc.d mandos stop + else + /etc/init.d/mandos stop + fi + fi ;; upgrade|failed-upgrade) ;; === modified file 'debian/rules' --- debian/rules 2016-06-23 19:46:41 +0000 +++ debian/rules 2013-10-05 19:34:40 +0000 @@ -19,17 +19,11 @@ $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client \ install-client-nokey -override_dh_fixperms-arch: +override_dh_fixperms: dh_fixperms --exclude etc/keys/mandos \ + --exclude etc/mandos/clients.conf \ --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/lib/mandos/plugins.d \ --exclude usr/share/doc/mandos-client/examples/network-hooks.d chmod --recursive g-w -- \ "$(CURDIR)/debian/mandos-client/usr/share/doc/mandos-client/examples/network-hooks.d" - -override_dh_fixperms-indep: - dh_fixperms --exclude etc/mandos/clients.conf - -override_dh_auto_test-arch: ; === removed directory 'debian/upstream' === removed file 'debian/upstream/signing-key.asc' --- debian/upstream/signing-key.asc 2014-03-28 22:32:21 +0000 +++ debian/upstream/signing-key.asc 1970-01-01 00:00:00 +0000 @@ -1,52 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.12 (GNU/Linux) - -mQINBFJQOFYBEACoWsEGlOxVWFUAxOxdd3GDLaqEKkKihJwLp102Ks7JKMd9friR -7+OZuo3U0gdqLU9q1jPJn36J1QbaUTOvcaKtZp+QpUoYJ2OaGtlOY5ML8LSoC0rZ -MIzGYTtvriwpU/YplLNGPl/90KsB2VqjrY1l1he5M8zziWDlPdJxwg8GFvmPWoif -6oo+1iCswL5IdQ6c5MVO53zYu0cgyUSazLsVD5Xzy59lefgtaDydahJpPycf5aEQ -DAoC9fZt2mgG3FLIUCZdXIhZdOJGCMdjLThBnJXYgGbG4rbGLNlI4W/uA5aqa4ME -WYSAcCyX3ucKY/LkXRtC+z5s05e7tZ3Z+uAJy1eDsbhDXgZERye7a/zPWx1tAlzQ -E80Oltjh1uXWjQORyx99a0jK87zjm49YjhYw1ZN6Z0HfSaws4Yj2QOzp9t4B3l7f -DIUYoWBfHW7mseQeQ+t3TwQU5gjFCNu7oDeATqi5A5MksXN0+BcksterbGRBhEyp -CybIEyrZE033jIs407Ool4Kv10cnjc8oy609BXex/dxwcvVr2vQHle4NPUZd+Xhg -zC+9Z4jFwE0M/EPvtyieA/DWQse+TZ5itDGMYDub/GJfv1U61ANOgPIbTEF7iSa9 -5nWmq7zyUy/txmABka842Kt0Vp6ayoKcF8EIXCaDrVfPnXj+JlKf3c2u6wARAQAB -tCxNYW5kb3MgTWFpbnRhaW5lciBUZWFtIDxtYW5kb3NAcmVjb21waWxlLnNlPokC -PQQTAQgAJwUCUlA4VgIbAwUJCWYBgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAK -CRByIylzyjTCxETXEACi56jCV9lJNSBbTp0Iet4X/i7Mx0Z8UkFFa3l7o0i4jFQj -CIBrWECDlcxqZziii2dgh7L0ma93vB3rfjfCWeYLcEQw43MFBsd4dHuobrLXTcqU -7n0Zmc8BsXwk5B25CnEYgvlbWX9BCYtxHGRcZzQrqOFjCMKatq0EIIVuWaz5yuCU -V2rEgnr+veTd/rBOE9ez6Ju6xH11Teob5G7pMM0YPKHtZG/J3rvWPw4BDM6Tc60B -G0sTDZNgkrGWxuB8YaLIwVWzliQK/17Jv/0alajyA3cWLWMkcK9Yhi/einzdMRoD -IjnbtoHcSC9g6i0VGelwnMpHlFTwXriXEBSttULarK3iKE4tOv9nxMAEwicqlw14 -X96PPgz6ogtJG7FiwZfy1CQ81Uby+YuIhHZx3ZEyR1TFq70e98EgCuvjMZWMIhSy -gB5Vssfq7c2lXQjltV3ujhK1PD+7/iHlL7t4QRxPDN8fbMS2VPfAdtnWS1K48d5J -D/jP1LrGWS81HIaX8GFVLVw+jSQEu9cn3TFiZxK/4MMsITmlouJdtZWmQ/otMSMl -wiCCZp3dGpRXMmaqR1N8V0nMKshM8mci7bD92ubd/t6cR/G6l+VIp45WyFONvtde -F3ccfmfrKJuroMDfHxPxMf58EroAuWwzJKCPRH4JmwDvSSQoIIAGNL1lOKp6wrkC -DQRSUDhWARAAzN7pbpAu7XLNPODotV/N+JaCFvNAIqTcr9PrbhxiKFCDs9/IExwP -sGENL9GZd1DfoGEgxQ8j3l8VGw9VSeUoN7uMY2NwwbXTilAFkn/S8xnr2zQDRZ+n -EeFSq9MMFxj4Kt1TqVYDbO8vmFfOT3gRCRHeJ+pn4yJeSPau/ndNrmbQ1/Z6vaUG -yfo931ottx7SXZwkHA6jJVFT9rbHTyx9tzOqMKDJiMrx8qKaHpE9B45oHNR0WJJJ -75zoDVuOZ6wAxXZuqBFu2lKPqDTZeawfzcu5qplrm1RPgSOjz6w1A41HBLqGe+v9 -7Twx5wfNMgnKC0V2wUe0xR6hQHQlyZoCwcGyrasiu+v/joZ1p66SSWKjs+LGjoe4 -Lwh6VjZU2x+irVBjcgIoRWf3k4JAef0nGYsm0cFjAnwXac+/CYxVt+7Y4+HG8wPB -oUNZkW+bvdHQouxEClxnccIEgX/AkriJTYDQ4q3tkl2HVE/51R4pdQVLer2a5ov+ -Jwk44DdqzYstMsvqu+iD48hXzADg6HFvofkpct15h463pMaJf99uVVM9ZDNQ6B34 -l3tPX9ZDkIDl6n9dE2Jkaxx1yNVhPXpeMf4EzL+CEdUErVStB66lUkw+tNkuZyVT -ZTQel9h0196+CNSiqAaL8+ZZdbjKKfzlcB4Qnd897XzMfsFQ7mzJ9QsAEQEAAYkC -JQQYAQgADwUCUlA4VgIbDAUJCWYBgAAKCRByIylzyjTCxFmlEACBTOg5NqX63d8D -mwk4smlFPppQBIduxZaMG9HsLcPi3VKTG9Zg6WI6rEdr/4MnoINsudLsEbrQLgRH -2q1Zs+HqIIP5H2/sYHmswyokYB10zKB6gNUUg/GSlcAcrelsHVKx5B8kccWGT5gk -Wo/X0BGMUTOvQ6lJ6YNo1idcQ2ZjsyfZoz3G8JS7/EXN//jAZf+017yj8WsAS7hw -JRFMy7VET4g00JcBoNOAMP7PkozimZ2OwwsggJSYWkR1RaU2tKR1VmDF8R6UxuEd -BJzwFmz+wNC1Kq+FoSaRNsrKEmzLnfV9unDnF2z7Lc4LqOysXdzOk9zTBPur0gd2 -Lh5H/g5rTAMQBARqXfvIwiTtrBGgil8JW8e4Bc0LQUuHAE7x9gMRil+OtkQrCRk9 -0LWXVS+K0tvvruE4EDtCGiS5046+BEI3aYsp4hNzjHADq0TJeCYjNg9kY0CjxcEq -cfuMoUbQ0MkARGuBbykCdlylfTrkxrj/dPhr49lctY3H+Pj6F4fMDM4TP6UTGA8k -993RRNYhkDWSxIp6G7RJpBZobHN+eHQ3r8A4tWdYb4Fvd2lvwEDjUFT9uD6WAff4 -8A1hM2uSy91UYBOPrIjqYdRFKJc9rThYdXH2T6SiRMYtZMrEKhqPffB/i9mqVBlD -6vKRsaQikZujRdP9Dkf0mLmJ7LANWw== -=9Noe ------END PGP PUBLIC KEY BLOCK----- === modified file 'debian/watch' --- debian/watch 2014-02-16 02:42:42 +0000 +++ debian/watch 2011-10-05 16:00:56 +0000 @@ -1,3 +1,2 @@ version=3 -opts=pgpsigurlmangle=s/$/.asc/ \ - ftp://ftp.recompile.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z|xz) +ftp://ftp.recompile.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z|xz) === modified file 'init.d-mandos' --- init.d-mandos 2016-03-13 00:37:02 +0000 +++ init.d-mandos 2012-06-01 21:48:12 +0000 @@ -1,12 +1,12 @@ #! /bin/sh ### BEGIN INIT INFO # Provides: mandos -# Required-Start: $remote_fs $syslog avahi-daemon -# Required-Stop: $remote_fs $syslog avahi-daemon +# Required-Start: $remote_fs $syslog avahi +# Required-Stop: $remote_fs $syslog avahi # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Mandos server -# Description: Server of encrypted passwords to Mandos clients +# Description: Gives encrypted passwords to Mandos clients ### END INIT INFO # Author: Teddy Hogeborn @@ -23,11 +23,7 @@ NAME=mandos DAEMON=/usr/sbin/$NAME DAEMON_ARGS="" -if [ -d /run/. ]; then - PIDFILE=/run/$NAME.pid -else - PIDFILE=/var/run/$NAME.pid -fi +PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed @@ -44,8 +40,7 @@ . /lib/init/vars.sh # Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # @@ -123,9 +118,6 @@ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; - status) - status_of_proc "$DAEMON" "$NAME" -p "$PIDFILE" && exit 0 || exit $? - ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out @@ -152,14 +144,17 @@ esac ;; *) - # Failed to stop + # Failed to stop log_end_msg 1 ;; esac ;; + status) + status_of_proc "$DAEMON" "$NAME" -p "$PIDFILE" + ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;; esac === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2017-02-21 22:15:43 +0000 +++ initramfs-tools-hook 2013-10-13 01:49:18 +0000 @@ -29,15 +29,13 @@ . /usr/share/initramfs-tools/hook-functions -for d in /usr/lib \ - "/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null`" \ - "`rpm --eval='%{_libdir}' 2>/dev/null`" /usr/local/lib; do - if [ -d "$d"/mandos ]; then - libdir="$d" +for d in /usr /usr/local; do + if [ -d "$d"/lib/mandos ]; then + prefix="$d" break fi done -if [ -z "$libdir" ]; then +if [ -z "$prefix" ]; then # Mandos not found exit 1 fi @@ -70,23 +68,21 @@ CONFDIR="/conf/conf.d/mandos" MANDOSDIR="/lib/mandos" PLUGINDIR="${MANDOSDIR}/plugins.d" -PLUGINHELPERDIR="${MANDOSDIR}/plugin-helpers" HOOKDIR="${MANDOSDIR}/network-hooks.d" # Make directories install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \ "${DESTDIR}${MANDOSDIR}" "${DESTDIR}${HOOKDIR}" install --owner=${mandos_user} --group=${mandos_group} --directory \ - --mode=u=rwx "${DESTDIR}${PLUGINDIR}" \ - "${DESTDIR}${PLUGINHELPERDIR}" + --mode=u=rwx "${DESTDIR}${PLUGINDIR}" # Copy the Mandos plugin runner -copy_exec "$libdir"/mandos/plugin-runner "${MANDOSDIR}" +copy_exec "$prefix"/lib/mandos/plugin-runner "${MANDOSDIR}" # Copy the plugins # Copy the packaged plugins -for file in "$libdir"/mandos/plugins.d/*; do +for file in "$prefix"/lib/mandos/plugins.d/*; do base="`basename \"$file\"`" # Is this plugin overridden? if [ -e "/etc/mandos/plugins.d/$base" ]; then @@ -100,21 +96,6 @@ esac done -# Copy the packaged plugin helpers -for file in "$libdir"/mandos/plugin-helpers/*; do - base="`basename \"$file\"`" - # Is this plugin overridden? - if [ -e "/etc/mandos/plugin-helpers/$base" ]; then - continue - fi - case "$base" in - *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) - : ;; - "*") : ;; - *) copy_exec "$file" "${PLUGINHELPERDIR}" ;; - esac -done - # Copy any user-supplied plugins for file in /etc/mandos/plugins.d/*; do base="`basename \"$file\"`" @@ -126,23 +107,12 @@ esac done -# Copy any user-supplied plugin helpers -for file in /etc/mandos/plugin-helpers/*; do - base="`basename \"$file\"`" - case "$base" in - *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) - : ;; - "*") : ;; - *) copy_exec "$file" "${PLUGINHELPERDIR}" ;; - esac -done - # Get DEVICE from initramfs.conf and other files . /etc/initramfs-tools/initramfs.conf 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 +127,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,29 +139,20 @@ 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 # GPGME needs GnuPG -gpg=/usr/bin/gpg libgpgme11_version="`dpkg-query --showformat='${Version}' --show libgpgme11`" -if dpkg --compare-versions "$libgpgme11_version" ge 1.5.0-0.1; then - if [ -e /usr/bin/gpgconf ]; then - if [ ! -e "${DESTDIR}/usr/bin/gpgconf" ]; then - 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 +if dpkg --compare-versions "$libgpgme11_version" ge 1.4.1-0.1; then gpg=/usr/bin/gpg2 +else + gpg=/usr/bin/gpg fi if [ ! -e "${DESTDIR}$gpg" ]; then copy_exec "$gpg" @@ -222,24 +183,10 @@ if [ -d "$file" ]; then continue fi - case "$file" in - *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) - : ;; - "*") : ;; - *) - cp --archive --sparse=always "$file" \ - "${DESTDIR}${CONFDIR}" - chown ${mandos_user}:${mandos_group} \ - "${DESTDIR}${CONFDIR}/`basename \"$file\"`" - ;; - esac + cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" + chown ${mandos_user}:${mandos_group} \ + "${DESTDIR}${CONFDIR}/`basename \"$file\"`" done -# Use Diffie-Hellman parameters file if available -if [ -e "${DESTDIR}${CONFDIR}"/dhparams.pem ]; then - sed --in-place \ - --expression="1i--options-for=mandos-client:--dh-params=${CONFDIR}/dhparams.pem" \ - "${DESTDIR}/${CONFDIR}/plugin-runner.conf" -fi # /lib/mandos/plugin-runner will drop priviliges, but needs access to # its plugin directory and its config file. However, since almost all @@ -269,7 +216,7 @@ done for dir in "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do if [ -d "$dir" ]; then - find "$dir" \! -perm -u+rw,g+r -prune -or \! -type l -print0 \ - | xargs --null --no-run-if-empty chmod a+rX -- + find "$dir" \! -perm -u+rw,g+r -prune -or -print0 \ + | xargs --null --no-run-if-empty chmod a+rX fi done === modified file 'initramfs-tools-script' --- initramfs-tools-script 2017-02-21 21:42:08 +0000 +++ initramfs-tools-script 2011-07-16 00:29:19 +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 @@ -94,9 +94,7 @@ # If we are connecting directly, run "configure_networking" (from # /scripts/functions); it needs IPOPTS and DEVICE if [ "${connect+set}" = set ]; then - set +e # Required by library functions configure_networking - set -e if [ -n "$connect" ]; then cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf @@ -115,7 +113,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 2011-12-31 23:05:34 +0000 @@ -1,7 +1,7 @@ + %common; ]> @@ -32,11 +32,6 @@ 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -78,8 +73,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 @@ -204,16 +197,6 @@ - - How about sniffing the network traffic and decrypting it - later by physically grabbing the Mandos client and using its - key? - - We only use PFS (Perfect Forward Security) - key exchange algorithms in TLS, which protects against this. - - - Physically grabbing the Mandos server computer? @@ -232,22 +215,17 @@ - - Faking checker results? + + Faking ping replies? - If the Mandos client does not have an SSH server, the default - is for the Mandos server to use + The default for the server is to use fping, the replies to which could be faked to eliminate the timeout. But this could easily be changed to any shell command, with any security - measures you like. If the Mandos client - has an SSH server, the default - configuration (as generated by - mandos-keygen with the - option) is for the Mandos server - to use an ssh-keyscan command with strict - keychecking, which can not be faked. Alternatively, IPsec - could be used for the ping packets, making them secure. + measures you like. It could, for instance, be changed to an + SSH command with strict keychecking, which could not be faked. + Or IPsec could be used for the ping packets, making them + secure. @@ -382,11 +360,6 @@ - - BUGS - - - SEE ALSO @@ -420,7 +393,7 @@ - Mandos + Mandos === modified file 'mandos' --- mandos 2017-02-23 20:35:20 +0000 +++ mandos 2013-06-23 15:13:06 +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-2012 Teddy Hogeborn +# Copyright © 2008-2012 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,34 +23,31 @@ # 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) -try: - from future_builtins import * -except ImportError: - pass +from future_builtins import * -try: - import SocketServer as socketserver -except ImportError: - import socketserver +import SocketServer as socketserver import socket import argparse import datetime import errno -try: - import ConfigParser as configparser -except ImportError: - import configparser +import gnutls.crypto +import gnutls.connection +import gnutls.errors +import gnutls.library.functions +import gnutls.library.constants +import gnutls.library.types +import ConfigParser as configparser import sys import re import os @@ -65,104 +62,65 @@ import struct import fcntl import functools -try: - import cPickle as pickle -except ImportError: - import pickle +import cPickle as pickle import multiprocessing import types import binascii import tempfile import itertools import collections -import codecs import dbus import dbus.service -from gi.repository import GLib +import gobject +import avahi from dbus.mainloop.glib import DBusGMainLoop import ctypes import ctypes.util 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 - -if sys.version_info.major == 2: - str = unicode - -version = "1.7.15" + SO_BINDTODEVICE = None + +version = "1.6.0" stored_state_file = "clients.pickle" logger = logging.getLogger() -syslogger = None +syslogger = (logging.handlers.SysLogHandler + (facility = logging.handlers.SysLogHandler.LOG_DAEMON, + address = str("/dev/log"))) try: - if_nametoindex = ctypes.cdll.LoadLibrary( - ctypes.util.find_library("c")).if_nametoindex + 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 with contextlib.closing(socket.socket()) as s: ifreq = fcntl.ioctl(s, SIOCGIFINDEX, - struct.pack(b"16s16x", interface)) - interface_index = struct.unpack("I", ifreq[16:20])[0] + struct.pack(str("16s16x"), + interface)) + interface_index = struct.unpack(str("I"), + ifreq[16:20])[0] return interface_index -def copy_function(func): - """Make a copy of a function""" - if sys.version_info.major == 2: - return types.FunctionType(func.func_code, - func.func_globals, - func.func_name, - func.func_defaults, - func.func_closure) - else: - return types.FunctionType(func.__code__, - func.__globals__, - func.__name__, - func.__defaults__, - func.__closure__) - - 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")) + 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,43 +138,29 @@ class PGPEngine(object): """A simple class for OpenPGP symmetric encryption & decryption""" - def __init__(self): self.tempdir = tempfile.mkdtemp(prefix="mandos-") - self.gpg = "gpg" - try: - output = subprocess.check_output(["gpgconf"]) - for line in output.splitlines(): - name, text, path = line.split(b":") - if name == "gpg": - self.gpg = path - break - except OSError as e: - if e.errno != errno.ENOENT: - raise self.gnupgargs = ['--batch', - '--homedir', self.tempdir, + '--home', 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,100 +168,67 @@ # 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. - encoded = b"mandos" + binascii.hexlify(password) - if len(encoded) > 2048: - # GnuPG can't handle long passwords, so encode differently - encoded = (b"mandos" + password.replace(b"\\", b"\\\\") - .replace(b"\n", b"\\n") - .replace(b"\0", b"\\x00")) - return encoded - + return b"mandos" + binascii.hexlify(password) + def encrypt(self, data, password): passphrase = self.password_encode(password) - with tempfile.NamedTemporaryFile( - dir=self.tempdir) as passfile: + with tempfile.NamedTemporaryFile(dir=self.tempdir + ) as passfile: passfile.write(passphrase) passfile.flush() - proc = subprocess.Popen([self.gpg, '--symmetric', + proc = subprocess.Popen(['gpg', '--symmetric', '--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: + with tempfile.NamedTemporaryFile(dir = self.tempdir + ) as passfile: passfile.write(passphrase) passfile.flush() - proc = subprocess.Popen([self.gpg, '--decrypt', + proc = subprocess.Popen(['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 - 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 -avahi = Avahi() - - class AvahiError(Exception): def __init__(self, value, *args, **kwargs): self.value = value - return super(AvahiError, self).__init__(value, *args, - **kwargs) - + super(AvahiError, self).__init__(value, *args, **kwargs) + def __unicode__(self): + return unicode(repr(self.value)) class AvahiServiceError(AvahiError): pass - class AvahiGroupError(AvahiError): pass 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 +246,11 @@ 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): + + 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): self.interface = interface self.name = name self.type = servicetype @@ -361,33 +265,27 @@ self.server = None self.bus = bus self.entry_group_state_changed_match = None - - def rename(self, remove=True): + + def rename(self): """Derived from the Avahi example code""" if self.rename_count >= self.max_renames: logger.critical("No suitable Zeroconf service name found" " after %i retries, exiting.", self.rename_count) raise AvahiServiceError("Too many renames") - self.name = str( - self.server.GetAlternativeServiceName(self.name)) - self.rename_count += 1 + self.name = unicode(self.server + .GetAlternativeServiceName(self.name)) logger.info("Changing Zeroconf service name to %r ...", self.name) - if remove: - self.remove() + self.remove() try: self.add() except dbus.exceptions.DBusException as error: - if (error.get_dbus_name() - == "org.freedesktop.Avahi.CollisionError"): - logger.info("Local Zeroconf service name collision.") - return self.rename(remove=False) - else: - logger.critical("D-Bus Exception", exc_info=error) - self.cleanup() - os._exit(1) - + logger.critical("D-Bus Exception", exc_info=error) + self.cleanup() + os._exit(1) + self.rename_count += 1 + def remove(self): """Derived from the Avahi example code""" if self.entry_group_state_changed_match is not None: @@ -395,7 +293,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 +316,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: @@ -430,9 +328,10 @@ self.rename() elif state == avahi.ENTRY_GROUP_FAILURE: logger.critical("Avahi: Error in group state changed %s", - str(error)) - raise AvahiGroupError("State changed: {!s}".format(error)) - + unicode(error)) + raise AvahiGroupError("State changed: {0!s}" + .format(error)) + def cleanup(self): """Derived from the Avahi example code""" if self.group is not None: @@ -443,16 +342,17 @@ 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) - bad_states = { - avahi.SERVER_INVALID: "Zeroconf server invalid", - avahi.SERVER_REGISTERING: None, - avahi.SERVER_COLLISION: "Zeroconf server name collision", - avahi.SERVER_FAILURE: "Zeroconf server failure", - } + bad_states = { avahi.SERVER_INVALID: + "Zeroconf server invalid", + avahi.SERVER_REGISTERING: None, + avahi.SERVER_COLLISION: + "Zeroconf server name collision", + avahi.SERVER_FAILURE: + "Zeroconf server failure" } if state in bad_states: if bad_states[state] is not None: if error is None: @@ -461,24 +361,13 @@ logger.error(bad_states[state] + ": %r", error) self.cleanup() elif state == avahi.SERVER_RUNNING: - try: - self.add() - except dbus.exceptions.DBusException as error: - if (error.get_dbus_name() - == "org.freedesktop.Avahi.CollisionError"): - logger.info("Local Zeroconf service name" - " collision.") - return self.rename(remove=False) - else: - logger.critical("D-Bus Exception", exc_info=error) - self.cleanup() - os._exit(1) + self.add() else: if error is None: 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: @@ -488,297 +377,31 @@ follow_name_owner_changes=True), avahi.DBUS_INTERFACE_SERVER) self.server.connect_to_signal("StateChanged", - self.server_state_changed) + self.server_state_changed) self.server_state_changed(self.server.GetState()) class AvahiServiceToSyslog(AvahiService): - def rename(self, *args, **kwargs): + def rename(self): """Add the new name to the syslog messages""" - ret = AvahiService.rename(self, *args, **kwargs) - syslogger.setFormatter(logging.Formatter( - 'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s' - .format(self.name))) + ret = AvahiService.rename(self) + syslogger.setFormatter(logging.Formatter + ('Mandos ({0}) [%(process)d]:' + ' %(levelname)s: %(message)s' + .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 - _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)) - - # 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 - E_AGAIN = -28 - CRT_OPENPGP = 2 - CLIENT = 2 - SHUT_RDWR = 0 - 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 - 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=()): - # Default usage is by a message string, but if a return - # code is passed, convert it to a string with - # gnutls.strerror() - self.code = code - if message is None and code is not None: - message = GnuTLS.strerror(code) - return super(GnuTLS.Error, self).__init__( - message, *args) - - class CertificateSecurityError(Error): - pass - - # Classes - class Credentials(object): - def __init__(self): - self._c_object = gnutls.certificate_credentials_t() - 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): - self._c_object = gnutls.session_t() - gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT) - gnutls.set_default_priority(self._c_object) - gnutls.transport_set_ptr(self._c_object, socket.fileno()) - gnutls.handshake_set_private_extensions(self._c_object, - True) - self.socket = socket - if credentials is None: - credentials = gnutls.Credentials() - gnutls.credentials_set(self._c_object, credentials.type, - 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) - while data_len > 0: - 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 - for the 'restype' attribute on ctypes functions""" - if result >= 0: - return result - if result == gnutls.E_NO_CERTIFICATE_FOUND: - 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""" - while result < 0: - if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN): - 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.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, - ctypes.c_void_p, - 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() +def timedelta_to_milliseconds(td): + "Convert a datetime.timedelta() to milliseconds" + return ((td.days * 24 * 60 * 60 * 1000) + + (td.seconds * 1000) + + (td.microseconds // 1000)) 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 @@ -786,17 +409,17 @@ checker: subprocess.Popen(); a running checker process used to see if the client lives. 'None' if no process is running. - checker_callback_tag: a GLib event source tag, or None + checker_callback_tag: a gobject event source tag, or None checker_command: string; External command which is run to check if client lives. %() expansions are done at runtime with vars(self) as dict, so that for instance %(name)s can be used in the command. - checker_initiator_tag: a GLib event source tag, or None + checker_initiator_tag: a gobject event source tag, or None created: datetime.datetime(); (UTC) object creation client_structure: Object describing what attributes a client has and is used for storing the client at exit current_checker_command: string; current running checker_command - disable_initiator_tag: a GLib event source tag, or None + disable_initiator_tag: a gobject event source tag, or None enabled: bool() fingerprint: string (40 or 32 hexadecimal digits); used to uniquely identify the client @@ -807,8 +430,6 @@ last_checker_status: integer between 0 and 255 reflecting exit status of last checker. -1 reflects crashed checker, -2 means no checker completed yet. - last_checker_signal: The signal which killed the last checker, if - last_checker_status is -1 last_enabled: datetime.datetime(); (UTC) or None name: string; from the config file, used in log messages and D-Bus identifiers @@ -821,24 +442,38 @@ disabled, or None server_settings: The server_settings dict from main() """ - + runtime_expansions = ("approval_delay", "approval_duration", "created", "enabled", "expires", "fingerprint", "host", "interval", "last_approval_request", "last_checked_ok", "last_enabled", "name", "timeout") - client_defaults = { - "timeout": "PT5M", - "extended_timeout": "PT15M", - "interval": "PT2M", - "checker": "fping -q -- %%(host)s", - "host": "", - "approval_delay": "PT0S", - "approval_duration": "PT1S", - "approved_by_default": "True", - "enabled": "True", - } - + client_defaults = { "timeout": "PT5M", + "extended_timeout": "PT15M", + "interval": "PT2M", + "checker": "fping -q -- %%(host)s", + "host": "", + "approval_delay": "PT0S", + "approval_duration": "PT1S", + "approved_by_default": "True", + "enabled": "True", + } + + def timeout_milliseconds(self): + "Return the 'timeout' attribute in milliseconds" + return timedelta_to_milliseconds(self.timeout) + + def extended_timeout_milliseconds(self): + "Return the 'extended_timeout' attribute in milliseconds" + return timedelta_to_milliseconds(self.extended_timeout) + + def interval_milliseconds(self): + "Return the 'interval' attribute in milliseconds" + return timedelta_to_milliseconds(self.interval) + + def approval_delay_milliseconds(self): + return timedelta_to_milliseconds(self.approval_delay) + @staticmethod def config_parser(config): """Construct a new dict of client settings of this form: @@ -851,30 +486,25 @@ 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 + client["fingerprint"] = (section["fingerprint"].upper() .replace(" ", "")) if "secret" in section: - client["secret"] = codecs.decode(section["secret"] - .encode("utf-8"), - "base64") + client["secret"] = section["secret"].decode("base64") elif "secfile" in section: with open(os.path.expanduser(os.path.expandvars (section["secfile"])), "rb") as secfile: client["secret"] = secfile.read() else: - raise TypeError("No secret or secfile for section {}" + raise TypeError("No secret or secfile for section {0}" .format(section)) client["timeout"] = string_to_delta(section["timeout"]) client["extended_timeout"] = string_to_delta( @@ -888,18 +518,18 @@ 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 = {} self.server_settings = server_settings # adding all client settings - for setting, value in settings.items(): + for setting, value in settings.iteritems(): setattr(self, setting, value) - + if self.enabled: if not hasattr(self, "last_enabled"): self.last_enabled = datetime.datetime.utcnow() @@ -909,12 +539,15 @@ else: self.last_enabled = None self.expires = None - + logger.debug("Creating client %r", self.name) + # Uppercase and remove spaces from fingerprint for later + # comparison purposes with return value from the fingerprint() + # function 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 @@ -923,23 +556,26 @@ self.current_checker_command = None self.approved = None self.approvals_pending = 0 - self.changedstate = multiprocessing_manager.Condition( - multiprocessing_manager.Lock()) - self.client_structure = [attr - for attr in self.__dict__.keys() + self.changedstate = (multiprocessing_manager + .Condition(multiprocessing_manager + .Lock())) + self.client_structure = [attr for attr in + self.__dict__.iterkeys() if not attr.startswith("_")] self.client_structure.append("client_structure") - - for name, t in inspect.getmembers( - type(self), lambda obj: isinstance(obj, property)): + + 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 +586,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): @@ -958,88 +594,82 @@ if not quiet: logger.info("Disabling client %s", self.name) if getattr(self, "disable_initiator_tag", None) is not None: - GLib.source_remove(self.disable_initiator_tag) + gobject.source_remove(self.disable_initiator_tag) self.disable_initiator_tag = None self.expires = None if getattr(self, "checker_initiator_tag", None) is not None: - GLib.source_remove(self.checker_initiator_tag) + gobject.source_remove(self.checker_initiator_tag) self.checker_initiator_tag = None self.stop_checker() self.enabled = False if not quiet: self.send_changedstate() - # Do not run this again if called by a GLib.timeout_add + # Do not run this again if called by a gobject.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. if self.checker_initiator_tag is not None: - GLib.source_remove(self.checker_initiator_tag) - self.checker_initiator_tag = GLib.timeout_add( - int(self.interval.total_seconds() * 1000), - self.start_checker) + gobject.source_remove(self.checker_initiator_tag) + self.checker_initiator_tag = (gobject.timeout_add + (self.interval_milliseconds(), + self.start_checker)) # Schedule a disable() when 'timeout' has passed if self.disable_initiator_tag is not None: - GLib.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = GLib.timeout_add( - int(self.timeout.total_seconds() * 1000), self.disable) + gobject.source_remove(self.disable_initiator_tag) + self.disable_initiator_tag = (gobject.timeout_add + (self.timeout_milliseconds(), + self.disable)) # Also start a new checker *right now*. self.start_checker() - - def checker_callback(self, source, condition, connection, - command): + + def checker_callback(self, pid, condition, command): """The checker has completed, so take appropriate actions.""" self.checker_callback_tag = None self.checker = None - # 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 + if os.WIFEXITED(condition): + self.last_checker_status = os.WEXITSTATUS(condition) if self.last_checker_status == 0: logger.info("Checker for %(name)s succeeded", vars(self)) self.checked_ok() else: - logger.info("Checker for %(name)s failed", vars(self)) + logger.info("Checker for %(name)s failed", + vars(self)) else: self.last_checker_status = -1 - self.last_checker_signal = -returncode 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: timeout = self.timeout if self.disable_initiator_tag is not None: - GLib.source_remove(self.disable_initiator_tag) + gobject.source_remove(self.disable_initiator_tag) self.disable_initiator_tag = None if getattr(self, "enabled", False): - self.disable_initiator_tag = GLib.timeout_add( - int(timeout.total_seconds() * 1000), self.disable) + self.disable_initiator_tag = (gobject.timeout_add + (timedelta_to_milliseconds + (timeout), 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,75 +680,105 @@ # 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() - self.checker = None + + # If a checker exists, make sure it is not a zombie + try: + pid, status = os.waitpid(self.checker.pid, os.WNOHANG) + except (AttributeError, OSError) as error: + if (isinstance(error, OSError) + and error.errno != errno.ECHILD): + raise error + else: + if pid: + logger.warning("Checker was a zombie") + gobject.source_remove(self.checker_callback_tag) + self.checker_callback(pid, status, + self.current_checker_command) # Start a new checker if needed if self.checker is None: # Escape attributes for the shell - escaped_attrs = { - attr: re.escape(str(getattr(self, attr))) - for attr in self.runtime_expansions} + escaped_attrs = dict( + (attr, re.escape(unicode(getattr(self, attr)))) + for attr in + self.runtime_expansions) try: command = self.checker_command % escaped_attrs except TypeError as error: logger.error('Could not format string "%s"', - self.checker_command, + self.checker_command, exc_info=error) + return True # Try again later + self.current_checker_command = command + try: + logger.info("Starting checker %r for %s", + command, self.name) + # We don't need to redirect stdout and stderr, since + # in normal mode, that is already done by daemon(), + # and in debug mode we don't want to. (Stdin is + # always replaced by /dev/null.) + # The exception is when not debugging but nevertheless + # running in the foreground; use the previously + # created wnull. + popen_args = {} + if (not self.server_settings["debug"] + and self.server_settings["foreground"]): + popen_args.update({"stdout": wnull, + "stderr": wnull }) + self.checker = subprocess.Popen(command, + close_fds=True, + shell=True, cwd="/", + **popen_args) + except OSError as error: + logger.error("Failed to start subprocess", exc_info=error) - return True # Try again later - self.current_checker_command = command - logger.info("Starting checker %r for %s", command, - self.name) - # We don't need to redirect stdout and stderr, since - # in normal mode, that is already done by daemon(), - # and in debug mode we don't want to. (Stdin is - # always replaced by /dev/null.) - # 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": "/"} - if (not self.server_settings["debug"] - and self.server_settings["foreground"]): - popen_args.update({"stdout": wnull, - "stderr": wnull}) - pipe = multiprocessing.Pipe(duplex=False) - self.checker = multiprocessing.Process( - 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 + self.checker_callback_tag = (gobject.child_watch_add + (self.checker.pid, + self.checker_callback, + data=command)) + # The checker may have completed before the gobject + # watch was added. Check for this. + try: + pid, status = os.waitpid(self.checker.pid, os.WNOHANG) + except OSError as error: + if error.errno == errno.ECHILD: + # This should never happen + logger.error("Child process vanished", + exc_info=error) + return True + raise + if pid: + gobject.source_remove(self.checker_callback_tag) + self.checker_callback(pid, status, command) + # Re-run this periodically if run by gobject.timeout_add return True - + def stop_checker(self): """Force the checker process, if any, to stop.""" if self.checker_callback_tag: - GLib.source_remove(self.checker_callback_tag) + gobject.source_remove(self.checker_callback_tag) self.checker_callback_tag = None if getattr(self, "checker", None) is None: return logger.debug("Stopping checker for %(name)s", vars(self)) - self.checker.terminate() + try: + self.checker.terminate() + #time.sleep(0.5) + #if self.checker.poll() is None: + # self.checker.kill() + except OSError as error: + if error.errno != errno.ESRCH: # No such process + raise self.checker = None -def dbus_service_property(dbus_interface, - signature="v", - access="readwrite", - byte_arrays=False): +def dbus_service_property(dbus_interface, signature="v", + access="readwrite", 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. @@ -1127,8 +787,7 @@ # "Set" method, so we fail early here: if byte_arrays and signature != "ay": raise ValueError("Byte arrays not supported for non-'ay'" - " signature {!r}".format(signature)) - + " signature {0!r}".format(signature)) def decorator(func): func._dbus_is_property = True func._dbus_interface = dbus_interface @@ -1137,59 +796,53 @@ 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"}) + @dbus_service_property("org.example.Interface", signature="b", access="r") + @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true", + "org.freedesktop.DBus.Property." + "EmitsChangedSignal": "false"}) 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 class DBusPropertyException(dbus.exceptions.DBusException): """A base class for D-Bus property-related exceptions """ - pass + def __unicode__(self): + return unicode(str(self)) class DBusPropertyAccessException(DBusPropertyException): @@ -1204,129 +857,53 @@ pass -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. +class DBusObjectWithProperties(dbus.service.Object): + """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. """ - + @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), + return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing), False) - + def _get_all_dbus_things(self, thing): """Returns a generator of (name, attribute) pairs """ - return ((getattr(athing.__get__(self), "_dbus_name", name), + return ((getattr(athing.__get__(self), "_dbus_name", + name), athing.__get__(self)) 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') - 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"): - for tag in if_tag.getElementsByTagName(typ): - annots = dict() - for name, prop in (self. - _get_all_dbus_things(typ)): - if (name == tag.getAttribute("name") - and prop._dbus_interface - == if_tag.getAttribute("name")): - annots.update(getattr( - prop, "_dbus_annotations", {})) - for name, value in annots.items(): - ann_tag = document.createElement( - "annotation") - ann_tag.setAttribute("name", name) - ann_tag.setAttribute("value", value) - tag.appendChild(ann_tag) - # Add interface annotation tags - for annotation, value in dict( - itertools.chain.from_iterable( - annotations().items() - for name, annotations - in self._get_all_dbus_things("interface") - if name == if_tag.getAttribute("name") - )).items(): - ann_tag = document.createElement("annotation") - ann_tag.setAttribute("name", annotation) - ann_tag.setAttribute("value", value) - if_tag.appendChild(ann_tag) - # Fix argument name for the Introspect method itself - if (if_tag.getAttribute("name") - == dbus.INTROSPECTABLE_IFACE): - for cn in if_tag.getElementsByTagName("method"): - if cn.getAttribute("name") == "Introspect": - for arg in cn.getElementsByTagName("arg"): - if (arg.getAttribute("direction") - == "out"): - arg.setAttribute("name", - "xml_data") - xmlstring = document.toxml("utf-8") - document.unlink() - except (AttributeError, xml.dom.DOMException, - xml.parsers.expat.ExpatError) as error: - logger.error("Failed to override Introspection method", - exc_info=error) - return xmlstring - - -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. - """ - + inspect.getmembers(cls, + self._is_dbus_thing(thing))) + 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. """ - for cls in self.__class__.__mro__: - for name, value in inspect.getmembers( - cls, self._is_dbus_thing("property")): + for cls in self.__class__.__mro__: + for name, value in (inspect.getmembers + (cls, + self._is_dbus_thing("property"))): 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""" - return (name for name in set(getattr(getattr(x, attr), - "_dbus_interface", None) - 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", + raise DBusPropertyNotFound(self.dbus_object_path + ":" + + interface_name + "." + + property_name) + + @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss", out_signature="v") def Get(self, interface_name, property_name): """Standard D-Bus property Get() method, see D-Bus standard. @@ -1338,7 +915,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. @@ -1350,20 +927,17 @@ # The byte_arrays option is not supported yet on # signatures other than "ay". if prop._dbus_signature != "ay": - raise ValueError("Byte arrays not supported for non-" - "'ay' signature {!r}" - .format(prop._dbus_signature)) + raise ValueError value = dbus.ByteArray(b''.join(chr(byte) for byte in value)) prop(value) - - @dbus.service.method(dbus.PROPERTIES_IFACE, - in_signature="s", + + @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 = {} @@ -1379,40 +953,29 @@ if not hasattr(value, "variant_level"): properties[name] = value continue - properties[name] = type(value)( - value, variant_level=value.variant_level + 1) + properties[name] = type(value)(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): - """Standard D-Bus PropertiesChanged() signal, see D-Bus - 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, - object_path, - connection) + xmlstring = dbus.service.Object.Introspect(self, object_path, + 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) @@ -1421,22 +984,37 @@ if prop._dbus_interface == if_tag.getAttribute("name")): if_tag.appendChild(tag) - # Add annotation tags for properties - for tag in if_tag.getElementsByTagName("property"): - annots = dict() - for name, prop in self._get_all_dbus_things( - "property"): - if (name == tag.getAttribute("name") - and prop._dbus_interface - == if_tag.getAttribute("name")): - annots.update(getattr( - prop, "_dbus_annotations", {})) - for name, value in annots.items(): - ann_tag = document.createElement( - "annotation") - ann_tag.setAttribute("name", name) - ann_tag.setAttribute("value", value) - tag.appendChild(ann_tag) + # Add annotation tags + for typ in ("method", "signal", "property"): + for tag in if_tag.getElementsByTagName(typ): + annots = dict() + for name, prop in (self. + _get_all_dbus_things(typ)): + if (name == tag.getAttribute("name") + and prop._dbus_interface + == if_tag.getAttribute("name")): + annots.update(getattr + (prop, + "_dbus_annotations", + {})) + for name, value in annots.iteritems(): + ann_tag = document.createElement( + "annotation") + ann_tag.setAttribute("name", name) + ann_tag.setAttribute("value", value) + tag.appendChild(ann_tag) + # Add interface annotation tags + for annotation, value in dict( + itertools.chain.from_iterable( + annotations().iteritems() + for name, annotations in + self._get_all_dbus_things("interface") + if name == if_tag.getAttribute("name") + )).iteritems(): + ann_tag = document.createElement("annotation") + ann_tag.setAttribute("name", annotation) + ann_tag.setAttribute("value", value) + if_tag.appendChild(ann_tag) # Add the names to the return values for the # "org.freedesktop.DBus.Properties" methods if (if_tag.getAttribute("name") @@ -1461,82 +1039,12 @@ 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}}}") - def GetManagedObjects(self): - """This function must be overridden""" - raise NotImplementedError() - - @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, - signature="oa{sa{sv}}") - def InterfacesAdded(self, object_path, interfaces_and_properties): - pass - - @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') - def Introspect(self, object_path, connection): - """Overloading of standard D-Bus method. - - Override return argument name of GetManagedObjects to be - "objpath_interfaces_and_properties" - """ - xmlstring = DBusObjectWithAnnotations.Introspect(self, - object_path, - 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): - for cn in if_tag.getElementsByTagName("method"): - if (cn.getAttribute("name") - == "GetManagedObjects"): - for arg in cn.getElementsByTagName("arg"): - if (arg.getAttribute("direction") - == "out"): - arg.setAttribute( - "name", - "objpath_interfaces" - "_and_properties") - xmlstring = document.toxml("utf-8") - document.unlink() - except (AttributeError, xml.dom.DOMException, - xml.parsers.expat.ExpatError) as error: - logger.error("Failed to override Introspection method", - 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(dt.isoformat(), variant_level=variant_level) + return dbus.String("", variant_level = variant_level) + return dbus.String(dt.isoformat(), + variant_level=variant_level) def alternate_dbus_interfaces(alt_interface_names, deprecate=True): @@ -1544,28 +1052,27 @@ 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()): + alt_interface_names.iteritems()): attr = {} interface_names = set() # Go though all attributes of the class @@ -1573,42 +1080,41 @@ # Ignore non-D-Bus attributes, and D-Bus attributes # with the wrong interface name if (not hasattr(attribute, "_dbus_interface") - or not attribute._dbus_interface.startswith( - orig_interface_name)): + or not attribute._dbus_interface + .startswith(orig_interface_name)): continue # Create an alternate D-Bus interface name based on # the current name - alt_interface = attribute._dbus_interface.replace( - orig_interface_name, alt_interface_name) + alt_interface = (attribute._dbus_interface + .replace(orig_interface_name, + alt_interface_name)) interface_names.add(alt_interface) # Is this a D-Bus signal? if getattr(attribute, "_dbus_is_signal", False): # Extract the original non-method undecorated # function by black magic - if sys.version_info.major == 2: - nonmethod_func = (dict( + nonmethod_func = (dict( zip(attribute.func_code.co_freevars, - attribute.__closure__)) - ["func"].cell_contents) - else: - nonmethod_func = (dict( - zip(attribute.__code__.co_freevars, - attribute.__closure__)) - ["func"].cell_contents) + attribute.__closure__))["func"] + .cell_contents) # Create a new, but exactly alike, function # object, and decorate it to be a new D-Bus signal # with the alternate D-Bus interface name - new_function = copy_function(nonmethod_func) - new_function = (dbus.service.signal( - alt_interface, - attribute._dbus_signature)(new_function)) + new_function = (dbus.service.signal + (alt_interface, + attribute._dbus_signature) + (types.FunctionType( + nonmethod_func.func_code, + nonmethod_func.func_globals, + nonmethod_func.func_name, + nonmethod_func.func_defaults, + nonmethod_func.func_closure))) # Copy annotations, if any try: - new_function._dbus_annotations = dict( - attribute._dbus_annotations) + new_function._dbus_annotations = ( + dict(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 +1123,11 @@ """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 - 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 @@ -1640,16 +1138,20 @@ # object. Decorate it to be a new D-Bus method # with the alternate D-Bus interface name. Add it # to the class. - attr[attrname] = ( - dbus.service.method( - alt_interface, - attribute._dbus_in_signature, - attribute._dbus_out_signature) - (copy_function(attribute))) + attr[attrname] = (dbus.service.method + (alt_interface, + attribute._dbus_in_signature, + attribute._dbus_out_signature) + (types.FunctionType + (attribute.func_code, + attribute.func_globals, + attribute.func_name, + attribute.func_defaults, + attribute.func_closure))) # Copy annotations, if any try: - attr[attrname]._dbus_annotations = dict( - attribute._dbus_annotations) + attr[attrname]._dbus_annotations = ( + dict(attribute._dbus_annotations)) except AttributeError: pass # Is this a D-Bus property? @@ -1658,16 +1160,23 @@ # object, and decorate it to be a new D-Bus # property with the alternate D-Bus interface # name. Add it to the class. - attr[attrname] = (dbus_service_property( - alt_interface, attribute._dbus_signature, - attribute._dbus_access, - attribute._dbus_get_args_options - ["byte_arrays"]) - (copy_function(attribute))) + attr[attrname] = (dbus_service_property + (alt_interface, + attribute._dbus_signature, + attribute._dbus_access, + attribute + ._dbus_get_args_options + ["byte_arrays"]) + (types.FunctionType + (attribute.func_code, + attribute.func_globals, + attribute.func_name, + attribute.func_defaults, + attribute.func_closure))) # Copy annotations, if any try: - attr[attrname]._dbus_annotations = dict( - attribute._dbus_annotations) + attr[attrname]._dbus_annotations = ( + dict(attribute._dbus_annotations)) except AttributeError: pass # Is this a D-Bus interface? @@ -1676,18 +1185,22 @@ # object. Decorate it to be a new D-Bus interface # with the alternate D-Bus interface name. Add it # to the class. - attr[attrname] = ( - dbus_interface_annotations(alt_interface) - (copy_function(attribute))) + attr[attrname] = (dbus_interface_annotations + (alt_interface) + (types.FunctionType + (attribute.func_code, + attribute.func_globals, + attribute.func_name, + attribute.func_defaults, + attribute.func_closure))) if deprecate: # Deprecate all alternate interfaces - iname = "_AlternateDBusNames_interface_annotation{}" + iname="_AlternateDBusNames_interface_annotation{0}" 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()): @@ -1697,55 +1210,46 @@ if interface_names: # Replace the class with a new subclass of it with # methods, signals, etc. as created above. - if sys.version_info.major == 2: - cls = type(b"{}Alternate".format(cls.__name__), - (cls, ), attr) - else: - cls = type("{}Alternate".format(cls.__name__), - (cls, ), attr) + cls = type(b"{0}Alternate".format(cls.__name__), + (cls,), attr) return cls - return wrapper @alternate_dbus_interfaces({"se.recompile.Mandos": - "se.bsnet.fukt.Mandos"}) + "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_object_path",)) + # 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 # the D-Bus - client_object_name = str(self.name).translate( + client_object_name = unicode(self.name).translate( {ord("."): ord("_"), ord("-"): ord("_")}) - self.dbus_object_path = dbus.ObjectPath( - "/clients/" + client_object_name) + self.dbus_object_path = (dbus.ObjectPath + ("/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, - invalidate_only=False, - _interface=_interface): + + def notifychangeproperty(transform_func, + dbus_name, type_func=lambda x: x, + variant_level=1): """ 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 @@ -1753,42 +1257,31 @@ to the D-Bus. Default: no transform variant_level: D-Bus variant level. Default: 1 """ - attrname = "_{}".format(dbus_name) - + attrname = "_{0}".format(dbus_name) def setter(self, value): if hasattr(self, "dbus_object_path"): if (not hasattr(self, attrname) or type_func(getattr(self, attrname, None)) != type_func(value)): - if invalidate_only: - self.PropertiesChanged( - _interface, dbus.Dictionary(), - dbus.Array((dbus_name, ))) - else: - dbus_value = transform_func( - type_func(value), - variant_level=variant_level) - self.PropertyChanged(dbus.String(dbus_name), - dbus_value) - self.PropertiesChanged( - _interface, - dbus.Dictionary({dbus.String(dbus_name): - dbus_value}), - dbus.Array()) + dbus_value = transform_func(type_func(value), + variant_level + =variant_level) + self.PropertyChanged(dbus.String(dbus_name), + dbus_value) 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) + checker = notifychangeproperty(dbus.Boolean, "CheckerRunning", + type_func = lambda checker: + checker is not None) last_checked_ok = notifychangeproperty(datetime_to_dbus, "LastCheckedOK") last_checker_status = notifychangeproperty(dbus.Int16, @@ -1797,28 +1290,28 @@ datetime_to_dbus, "LastApprovalRequest") approved_by_default = notifychangeproperty(dbus.Boolean, "ApprovedByDefault") - approval_delay = notifychangeproperty( - dbus.UInt64, "ApprovalDelay", - type_func=lambda td: td.total_seconds() * 1000) + approval_delay = notifychangeproperty(dbus.UInt64, + "ApprovalDelay", + type_func = + timedelta_to_milliseconds) approval_duration = notifychangeproperty( dbus.UInt64, "ApprovalDuration", - type_func=lambda td: td.total_seconds() * 1000) + type_func = timedelta_to_milliseconds) host = notifychangeproperty(dbus.String, "Host") - timeout = notifychangeproperty( - dbus.UInt64, "Timeout", - type_func=lambda td: td.total_seconds() * 1000) + timeout = notifychangeproperty(dbus.UInt64, "Timeout", + type_func = + timedelta_to_milliseconds) extended_timeout = notifychangeproperty( dbus.UInt64, "ExtendedTimeout", - type_func=lambda td: td.total_seconds() * 1000) - interval = notifychangeproperty( - dbus.UInt64, "Interval", - type_func=lambda td: td.total_seconds() * 1000) + type_func = timedelta_to_milliseconds) + interval = notifychangeproperty(dbus.UInt64, + "Interval", + type_func = + timedelta_to_milliseconds) 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,31 +1320,32 @@ 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, - connection, command, *args, - **kwargs) - exitstatus = self.last_checker_status - if exitstatus >= 0: + + def checker_callback(self, pid, condition, command, + *args, **kwargs): + self.checker_callback_tag = None + self.checker = None + if os.WIFEXITED(condition): + exitstatus = os.WEXITSTATUS(condition) # Emit D-Bus signal self.CheckerCompleted(dbus.Int16(exitstatus), - # This is specific to GNU libC - dbus.Int64(exitstatus << 8), + dbus.Int64(condition), dbus.String(command)) else: # Emit D-Bus signal self.CheckerCompleted(dbus.Int16(-1), - dbus.Int64( - # This is specific to GNU libC - (exitstatus << 8) - | self.last_checker_signal), + dbus.Int64(condition), dbus.String(command)) - return ret - + + return Client.checker_callback(self, pid, condition, command, + *args, **kwargs) + def start_checker(self, *args, **kwargs): - old_checker_pid = getattr(self.checker, "pid", None) + old_checker = self.checker + if self.checker is not None: + old_checker_pid = self.checker.pid + else: + old_checker_pid = None r = Client.start_checker(self, *args, **kwargs) # Only if new checker process was started if (self.checker is not None @@ -1859,42 +1353,48 @@ # 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) + gobject.timeout_add(timedelta_to_milliseconds + (self.approval_duration), + self._reset_approved) self.send_changedstate() - - # D-Bus methods, signals & properties - - # Interfaces - - # Signals - + + ## D-Bus methods, signals & properties + _interface = "se.recompile.Mandos.Client" + + ## Interfaces + + @dbus_interface_annotations(_interface) + def _foo(self): + return { "org.freedesktop.DBus.Property.EmitsChangedSignal": + "false"} + + ## 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,132 +1403,116 @@ 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", + @dbus_service_property(_interface, signature="b", access="readwrite") def ApprovedByDefault_dbus_property(self, value=None): 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", + @dbus_service_property(_interface, signature="t", access="readwrite") def ApprovalDelay_dbus_property(self, value=None): if value is None: # get - return dbus.UInt64(self.approval_delay.total_seconds() - * 1000) + return dbus.UInt64(self.approval_delay_milliseconds()) self.approval_delay = datetime.timedelta(0, 0, 0, value) - + # ApprovalDuration - property - @dbus_service_property(_interface, - signature="t", + @dbus_service_property(_interface, signature="t", access="readwrite") def ApprovalDuration_dbus_property(self, value=None): if value is None: # get - return dbus.UInt64(self.approval_duration.total_seconds() - * 1000) + return dbus.UInt64(timedelta_to_milliseconds( + self.approval_duration)) 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", + @dbus_service_property(_interface, signature="s", access="readwrite") def Host_dbus_property(self, value=None): if value is None: # get return dbus.String(self.host) - self.host = str(value) - + self.host = unicode(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", + @dbus_service_property(_interface, signature="b", access="readwrite") def Enabled_dbus_property(self, value=None): if value is None: # get @@ -2037,39 +1521,38 @@ self.enable() else: self.disable() - + # LastCheckedOK - property - @dbus_service_property(_interface, - signature="s", + @dbus_service_property(_interface, signature="s", access="readwrite") def LastCheckedOK_dbus_property(self, value=None): if value is not None: self.checked_ok() return return datetime_to_dbus(self.last_checked_ok) - + # LastCheckerStatus - property - @dbus_service_property(_interface, signature="n", access="read") + @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", + @dbus_service_property(_interface, signature="t", access="readwrite") def Timeout_dbus_property(self, value=None): if value is None: # get - return dbus.UInt64(self.timeout.total_seconds() * 1000) + return dbus.UInt64(self.timeout_milliseconds()) old_timeout = self.timeout self.timeout = datetime.timedelta(0, 0, 0, value) # Reschedule disabling @@ -2083,50 +1566,46 @@ if (getattr(self, "disable_initiator_tag", None) is None): return - GLib.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = GLib.timeout_add( - int((self.expires - now).total_seconds() * 1000), - self.disable) - + gobject.source_remove(self.disable_initiator_tag) + self.disable_initiator_tag = ( + gobject.timeout_add( + timedelta_to_milliseconds(self.expires - now), + self.disable)) + # ExtendedTimeout - property - @dbus_service_property(_interface, - signature="t", + @dbus_service_property(_interface, signature="t", access="readwrite") def ExtendedTimeout_dbus_property(self, value=None): if value is None: # get - return dbus.UInt64(self.extended_timeout.total_seconds() - * 1000) + return dbus.UInt64(self.extended_timeout_milliseconds()) self.extended_timeout = datetime.timedelta(0, 0, 0, value) - + # Interval - property - @dbus_service_property(_interface, - signature="t", + @dbus_service_property(_interface, signature="t", access="readwrite") def Interval_dbus_property(self, value=None): if value is None: # get - return dbus.UInt64(self.interval.total_seconds() * 1000) + return dbus.UInt64(self.interval_milliseconds()) self.interval = datetime.timedelta(0, 0, 0, value) if getattr(self, "checker_initiator_tag", None) is None: return if self.enabled: # Reschedule checker run - 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 - + gobject.source_remove(self.checker_initiator_tag) + self.checker_initiator_tag = (gobject.timeout_add + (value, self.start_checker)) + self.start_checker() # Start one now, too + # Checker - property - @dbus_service_property(_interface, - signature="s", + @dbus_service_property(_interface, signature="s", access="readwrite") def Checker_dbus_property(self, value=None): if value is None: # get return dbus.String(self.checker_command) - self.checker_command = str(value) - + self.checker_command = unicode(value) + # CheckerRunning - property - @dbus_service_property(_interface, - signature="b", + @dbus_service_property(_interface, signature="b", access="readwrite") def CheckerRunning_dbus_property(self, value=None): if value is None: # get @@ -2135,26 +1614,18 @@ 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": - "invalidates"}) - @dbus_service_property(_interface, - signature="ay", - access="write", - byte_arrays=True) + @dbus_service_property(_interface, signature="ay", + access="write", byte_arrays=True) def Secret_dbus_property(self, value): - self.secret = bytes(value) - + self.secret = str(value) + del _interface @@ -2163,8 +1634,8 @@ self._pipe = child_pipe self._pipe.send(('init', fpr, address)) if not self._pipe.recv(): - raise KeyError(fpr) - + raise KeyError() + def __getattribute__(self, name): if name == '_pipe': return super(ProxyClient, self).__getattribute__(name) @@ -2173,13 +1644,11 @@ 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,84 +1657,93 @@ 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)) + unicode(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")) + + session = (gnutls.connection + .ClientSession(self.request, + gnutls.connection + .X509Credentials())) + + # Note: gnutls.connection.X509Credentials is really a + # generic GnuTLS certificate credentials object so long as + # no X.509 keys are added to it. Therefore, we can use it + # here despite using OpenPGP certificates. + + #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"), - None) - + (gnutls.library.functions + .gnutls_priority_set_direct(session._c_object, + priority, None)) + # Start communication using the Mandos protocol # Get protocol number line = self.request.makefile().readline() logger.debug("Protocol version: %r", line) try: if int(line.strip().split()[0]) > 1: - raise RuntimeError(line) + raise RuntimeError except (ValueError, IndexError, RuntimeError) as error: logger.error("Unknown protocol version: %s", error) return - + # Start GnuTLS connection try: session.handshake() - except gnutls.Error as error: + except gnutls.errors.GNUTLSError as error: logger.warning("Handshake failed: %s", error) # Do not run session.bye() here: the session is not # established. Just abandon the request. return logger.debug("Handshake succeeded") - + approval_required = False try: try: - fpr = self.fingerprint( - self.peer_certificate(session)) - except (TypeError, gnutls.Error) as error: + fpr = self.fingerprint(self.peer_certificate + (session)) + except (TypeError, + gnutls.errors.GNUTLSError) as error: 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", - client.name) + client.name) if self.server.use_dbus: # 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", @@ -2273,8 +1751,8 @@ if self.server.use_dbus: # Emit D-Bus signal client.NeedApproval( - client.approval_delay.total_seconds() - * 1000, client.approved_by_default) + client.approval_delay_milliseconds(), + client.approved_by_default) else: logger.warning("Client %s was not approved", client.name) @@ -2282,11 +1760,13 @@ # 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()) + client.changedstate.wait( + float(timedelta_to_milliseconds(delay) + / 1000)) client.changedstate.release() time2 = datetime.datetime.now() if (time2 - time) >= delay: @@ -2302,77 +1782,93 @@ break else: delay -= time2 - time - - try: - session.send(client.secret) - except gnutls.Error as error: - logger.warning("gnutls send failed", - exc_info=error) - return - + + sent_size = 0 + while sent_size < len(client.secret): + try: + sent = session.send(client.secret[sent_size:]) + except gnutls.errors.GNUTLSError as error: + logger.warning("gnutls send failed", + exc_info=error) + return + logger.debug("Sent: %d, remaining: %d", + sent, len(client.secret) + - (sent_size + sent)) + sent_size += sent + 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 try: session.bye() - except gnutls.Error as error: + except gnutls.errors.GNUTLSError as error: logger.warning("GnuTLS bye failed", exc_info=error) - + @staticmethod def peer_certificate(session): "Return the peer's OpenPGP certificate as a bytestring" # If not an OpenPGP certificate... - if (gnutls.certificate_type_get(session._c_object) - != gnutls.CRT_OPENPGP): - # ...return invalid data - return b"" + if (gnutls.library.functions + .gnutls_certificate_type_get(session._c_object) + != gnutls.library.constants.GNUTLS_CRT_OPENPGP): + # ...do the normal thing + return session.peer_certificate list_size = ctypes.c_uint(1) - cert_list = (gnutls.certificate_get_peers + cert_list = (gnutls.library.functions + .gnutls_certificate_get_peers (session._c_object, ctypes.byref(list_size))) if not bool(cert_list) and list_size.value != 0: - raise gnutls.Error("error getting peer certificate") + raise gnutls.errors.GNUTLSError("error getting peer" + " certificate") if list_size.value == 0: 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" # New GnuTLS "datum" with the OpenPGP public key - datum = gnutls.datum_t( - ctypes.cast(ctypes.c_char_p(openpgp), - ctypes.POINTER(ctypes.c_ubyte)), - ctypes.c_uint(len(openpgp))) + datum = (gnutls.library.types + .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp), + ctypes.POINTER + (ctypes.c_ubyte)), + ctypes.c_uint(len(openpgp)))) # New empty GnuTLS certificate - crt = gnutls.openpgp_crt_t() - gnutls.openpgp_crt_init(ctypes.byref(crt)) + crt = gnutls.library.types.gnutls_openpgp_crt_t() + (gnutls.library.functions + .gnutls_openpgp_crt_init(ctypes.byref(crt))) # Import the OpenPGP public key into the certificate - gnutls.openpgp_crt_import(crt, ctypes.byref(datum), - gnutls.OPENPGP_FMT_RAW) + (gnutls.library.functions + .gnutls_openpgp_crt_import(crt, ctypes.byref(datum), + gnutls.library.constants + .GNUTLS_OPENPGP_FMT_RAW)) # Verify the self signature in the key crtverify = ctypes.c_uint() - gnutls.openpgp_crt_verify_self(crt, 0, - ctypes.byref(crtverify)) + (gnutls.library.functions + .gnutls_openpgp_crt_verify_self(crt, 0, + ctypes.byref(crtverify))) if crtverify.value != 0: - gnutls.openpgp_crt_deinit(crt) - raise gnutls.CertificateSecurityError("Verify failed") + gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) + raise (gnutls.errors.CertificateSecurityError + ("Verify failed")) # New buffer for the fingerprint buf = ctypes.create_string_buffer(20) buf_len = ctypes.c_size_t() # Get the fingerprint from the certificate into the buffer - gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), - ctypes.byref(buf_len)) + (gnutls.library.functions + .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), + ctypes.byref(buf_len))) # Deinit the certificate - gnutls.openpgp_crt_deinit(crt) + gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) # Convert the buffer to a Python bytestring fpr = ctypes.string_at(buf, buf_len.value) # Convert the bytestring to hexadecimal notation @@ -2382,56 +1878,51 @@ 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() + raise NotImplementedError 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, - socketfd=None): + interface=None, use_ipv6=True, socketfd=None): """If socketfd is set, use that file descriptor instead of creating a new one with socket.socket(). """ @@ -2443,13 +1934,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,46 +1957,46 @@ # 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, + str(self.interface + '\0')) + 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]: - self.server_address = (self.server_address[0], 0) + self.server_address = (self.server_address[0], + 0) # if self.interface: # self.server_address = (self.server_address[0], # 0, # port @@ -2518,22 +2008,17 @@ 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. + + Assumes a gobject.MainLoop event loop. """ - def __init__(self, server_address, RequestHandlerClass, - interface=None, - use_ipv6=True, - clients=None, - gnutls_priority=None, - use_dbus=True, - socketfd=None): + interface=None, use_ipv6=True, clients=None, + gnutls_priority=None, use_dbus=True, socketfd=None): self.enabled = False self.clients = clients if self.clients is None: @@ -2542,45 +2027,42 @@ 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)) - - def handle_ipc(self, source, condition, - parent_pipe=None, - proc=None, - client_object=None): + gobject.io_add_watch(parent_pipe.fileno(), + gobject.IO_IN | gobject.IO_HUP, + functools.partial(self.handle_ipc, + parent_pipe = + parent_pipe, + proc = proc)) + + def handle_ipc(self, source, condition, parent_pipe=None, + proc = None, client_object=None): # error, or the other end of multiprocessing.Pipe has closed - if condition & (GLib.IO_ERR | GLib.IO_HUP): + if condition & (gobject.IO_ERR | gobject.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(): + + for c in self.clients.itervalues(): if c.fingerprint == fpr: client = c break @@ -2593,14 +2075,15 @@ 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)) + + gobject.io_add_watch(parent_pipe.fileno(), + gobject.IO_IN | gobject.IO_HUP, + functools.partial(self.handle_ipc, + 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,31 +2092,30 @@ funcname = request[1] args = request[2] kwargs = request[3] - + parent_pipe.send(('data', getattr(client_object, funcname)(*args, - **kwargs))) - + **kwargs))) + if command == 'getattr': attrname = request[1] - if isinstance(client_object.__getattribute__(attrname), - collections.Callable): - parent_pipe.send(('function', )) + if callable(client_object.__getattribute__(attrname)): + parent_pipe.send(('function',)) else: - parent_pipe.send(( - 'data', client_object.__getattribute__(attrname))) - + 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,26 +2131,30 @@ >>> 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 - # a "group" containing digits - "value", # datetime.timedelta or None - "followers")) # Tokens valid after this token + Token = collections.namedtuple("Token", + ("regexp", # To match token; if + # "value" is not None, + # must have a "group" + # containing digits + "value", # datetime.timedelta or + # None + "followers")) # Tokens valid after + # this token # RFC 3339 "duration" tokens, syntax, and semantics; taken from # the "duration" ABNF definition in RFC 3339, Appendix A. token_end = Token(re.compile(r"$"), None, frozenset()) token_second = Token(re.compile(r"(\d+)S"), datetime.timedelta(seconds=1), - frozenset((token_end, ))) + frozenset((token_end,))) token_minute = Token(re.compile(r"(\d+)M"), datetime.timedelta(minutes=1), frozenset((token_second, token_end))) @@ -2690,19 +2176,16 @@ frozenset((token_month, token_end))) token_week = Token(re.compile(r"(\d+)W"), datetime.timedelta(weeks=1), - frozenset((token_end, ))) + frozenset((token_end,))) token_duration = Token(re.compile(r"P"), None, frozenset((token_year, token_month, token_day, token_time, - token_week))) - # Define starting values: - # Value so far - value = datetime.timedelta() + token_week))), + # 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 @@ -2724,15 +2207,14 @@ break else: # No currently valid tokens were found - raise ValueError("Invalid RFC 3339 duration: {!r}" - .format(duration)) + raise ValueError("Invalid RFC 3339 duration") # End token found return value 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,16 +2228,16 @@ >>> 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: - suffix = s[-1] + suffix = unicode(s[-1]) value = int(s[:-1]) if suffix == "d": delta = datetime.timedelta(value) @@ -2768,16 +2250,17 @@ elif suffix == "w": delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value) else: - raise ValueError("Unknown suffix {!r}".format(suffix)) - except IndexError as e: + raise ValueError("Unknown suffix {0!r}" + .format(suffix)) + except (ValueError, IndexError) as e: raise ValueError(*(e.args)) timevalue += delta 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() @@ -2791,7 +2274,7 @@ null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR) if not stat.S_ISCHR(os.fstat(null).st_mode): raise OSError(errno.ENODEV, - "{} not a character device" + "{0} not a character device" .format(os.devnull)) os.dup2(null, sys.stdin.fileno()) os.dup2(null, sys.stdout.fileno()) @@ -2801,13 +2284,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 {0}".format(version), help="show version number and exit") parser.add_argument("-i", "--interface", metavar="IF", help="Bind to interface IF") @@ -2846,45 +2329,40 @@ help="Directory to save/restore state in") parser.add_argument("--foreground", action="store_true", help="Run in foreground", default=None) - 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) - + doctest.testmod() + sys.exit() + # 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:+SIGN-RSA-SHA224", + "servicename": "Mandos", + "use_dbus": "True", + "use_ipv6": "True", + "debuglevel": "", + "restore": "True", + "socket": "", + "statedir": "/var/lib/mandos", + "foreground": "False", + } + # Parse config file for server-global settings server_config = configparser.SafeConfigParser(server_defaults) del server_defaults - server_config.read(os.path.join(options.configdir, "mandos.conf")) + server_config.read(os.path.join(options.configdir, + "mandos.conf")) # 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,38 +2378,32 @@ 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", - "priority", "servicename", "configdir", "use_dbus", - "use_ipv6", "debuglevel", "restore", "statedir", - "socket", "foreground", "zeroconf"): + "priority", "servicename", "configdir", + "use_dbus", "use_ipv6", "debuglevel", "restore", + "statedir", "socket", "foreground"): value = getattr(options, option) if value is not None: server_settings[option] = value del options # Force all strings to be unicode for option in server_settings.keys(): - if isinstance(server_settings[option], bytes): - server_settings[option] = (server_settings[option] - .decode("utf-8")) + if type(server_settings[option]) is str: + server_settings[option] = unicode(server_settings[option]) # Force all boolean options to be boolean for option in ("debug", "use_dbus", "use_ipv6", "restore", - "foreground", "zeroconf"): + "foreground"): server_settings[option] = bool(server_settings[option]) # Debug implies foreground 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"] @@ -2940,8 +2412,7 @@ stored_state_path = os.path.join(server_settings["statedir"], stored_state_file) foreground = server_settings["foreground"] - zeroconf = server_settings["zeroconf"] - + if debug: initlogger(debug, logging.DEBUG) else: @@ -2950,50 +2421,47 @@ 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"]))) - + syslogger.setFormatter(logging.Formatter + ('Mandos ({0}) [%(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"] - tcp_server = MandosServer( - (server_settings["address"], server_settings["port"]), - ClientHandler, - interface=(server_settings["interface"] or None), - use_ipv6=use_ipv6, - gnutls_priority=server_settings["priority"], - use_dbus=use_dbus, - socketfd=socketfd) + + tcp_server = MandosServer((server_settings["address"], + server_settings["port"]), + ClientHandler, + interface=(server_settings["interface"] + or None), + use_ipv6=use_ipv6, + gnutls_priority= + server_settings["priority"], + use_dbus=use_dbus, + socketfd=(server_settings["socket"] + or None)) if not foreground: - pidfilename = "/run/mandos.pid" - if not os.path.isdir("/run/."): - pidfilename = "/var/run/mandos.pid" + pidfilename = "/var/run/mandos.pid" pidfile = None try: - pidfile = codecs.open(pidfilename, "w", encoding="utf-8") + pidfile = open(pidfilename, "w") 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")): + + for name in ("_mandos", "mandos", "nobody"): try: uid = pwd.getpwnam(name).pw_uid - gid = pwd.getpwnam(group).pw_gid + gid = pwd.getpwnam(name).pw_gid break except KeyError: continue @@ -3003,161 +2471,110 @@ try: os.setgid(gid) os.setuid(uid) - if debug: - logger.debug("Did setuid/setgid to {}:{}".format(uid, - gid)) except OSError as error: - logger.warning("Failed to setuid/setgid to {}:{}: {}" - .format(uid, gid, os.strerror(error.errno))) if error.errno != errno.EPERM: - raise - + raise error + 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 + gnutls.library.functions.gnutls_global_set_log_level(11) + + @gnutls.library.types.gnutls_log_func def debug_gnutls(level, string): logger.debug("GnuTLS: %s", string[:-1]) - - gnutls.global_set_log_function(debug_gnutls) - + + (gnutls.library.functions + .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() - + + # multiprocessing will use threads, so before we use gobject we + # need to inform gobject that threads will be used. + gobject.threads_init() + global main_loop # From the Avahi example code DBusGMainLoop(set_as_default=True) - main_loop = GLib.MainLoop() + main_loop = gobject.MainLoop() bus = dbus.SystemBus() # End of Avahi example code if use_dbus: try: bus_name = dbus.service.BusName("se.recompile.Mandos", - bus, - do_not_queue=True) - old_bus_name = dbus.service.BusName( - "se.bsnet.fukt.Mandos", bus, - do_not_queue=True) - except dbus.exceptions.DBusException as e: + bus, do_not_queue=True) + old_bus_name = (dbus.service.BusName + ("se.bsnet.fukt.Mandos", bus, + do_not_queue=True)) + except dbus.exceptions.NameExistsException as e: logger.error("Disabling D-Bus:", exc_info=e) use_dbus = False server_settings["use_dbus"] = False tcp_server.use_dbus = False - 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) - if server_settings["interface"]: - service.interface = if_nametoindex( - server_settings["interface"].encode("utf-8")) - + protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET + service = AvahiServiceToSyslog(name = + server_settings["servicename"], + servicetype = "_mandos._tcp", + protocol = protocol, bus = bus) + if server_settings["interface"]: + service.interface = (if_nametoindex + (str(server_settings["interface"]))) + 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: - 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 - # .keys() - 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()} - 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"]] - # .name & .host - for k in ("name", "host"): - if isinstance(value[k], bytes): - value[k] = value[k].decode("utf-8") - # 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()} - del bytes_old_client_settings - # .host - for value in old_client_settings.values(): - if isinstance(value["host"], bytes): - value["host"] = (value["host"] - .decode("utf-8")) + clients_data, old_client_settings = (pickle.load + (stored_state)) os.remove(stored_state_path) except IOError as e: if e.errno == errno.ENOENT: - logger.warning("Could not load persistent state:" - " {}".format(os.strerror(e.errno))) + logger.warning("Could not load persistent state: {0}" + .format(os.strerror(e.errno))) else: logger.critical("Could not load persistent state:", exc_info=e) raise except EOFError as e: logger.warning("Could not load persistent state: " - "EOFError:", - exc_info=e) - + "EOFError:", exc_info=e) + with PGPEngine() as pgp: - for client_name, client in clients_data.items(): + for client_name, client in clients_data.iteritems(): # 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. @@ -3168,15 +2585,15 @@ # For each value in new config, check if it # differs from the old config value (Except for # the "secret" attribute) - if (name != "secret" - and (value != - old_client_settings[client_name][name])): + if (name != "secret" and + value != old_client_settings[client_name] + [name]): 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 + # enabled if its last checker was successful. Clients # whose checker succeeded before we stored its state is # assumed to have successfully run all checkers during # downtime. @@ -3184,36 +2601,36 @@ if datetime.datetime.utcnow() >= client["expires"]: if not client["last_checked_ok"]: logger.warning( - "disabling client {} - Client never " - "performed a successful checker".format( - client_name)) + "disabling client {0} - Client never " + "performed a successful checker" + .format(client_name)) client["enabled"] = False elif client["last_checker_status"] != 0: logger.warning( - "disabling client {} - Client last" - " checker failed with error code" - " {}".format( - client_name, - client["last_checker_status"])) + "disabling client {0} - Client " + "last checker failed with error code {1}" + .format(client_name, + client["last_checker_status"])) client["enabled"] = False else: - client["expires"] = ( - datetime.datetime.utcnow() - + client["timeout"]) + client["expires"] = (datetime.datetime + .utcnow() + + client["timeout"]) logger.debug("Last checker succeeded," - " keeping {} enabled".format( - client_name)) + " keeping {0} enabled" + .format(client_name)) try: - client["secret"] = pgp.decrypt( - client["encrypted_secret"], - client_settings[client_name]["secret"]) + client["secret"] = ( + pgp.decrypt(client["encrypted_secret"], + client_settings[client_name] + ["secret"])) except PGPError: # If decryption fails, we use secret from new settings - logger.debug("Failed to decrypt {} old secret".format( - client_name)) - client["secret"] = (client_settings[client_name] - ["secret"]) - + logger.debug("Failed to decrypt {0} old secret" + .format(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,189 +2638,139 @@ 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(): + for client_name, client in clients_data.iteritems(): 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() try: with pidfile: - print(pid, file=pidfile) + pid = os.getpid() + pidfile.write(str(pid) + "\n".encode("utf-8")) except IOError: logger.error("Could not write to file %r with PID %d", 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) - + + signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) + signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) + if use_dbus: - - @alternate_dbus_interfaces( - {"se.recompile.Mandos": "se.bsnet.fukt.Mandos"}) - class MandosDBusService(DBusObjectWithObjectManager): + @alternate_dbus_interfaces({"se.recompile.Mandos": + "se.bsnet.fukt.Mandos"}) + class MandosDBusService(DBusObjectWithProperties): """A D-Bus proxy object""" - def __init__(self): dbus.service.Object.__init__(self, bus, "/") - _interface = "se.recompile.Mandos" - + + @dbus_interface_annotations(_interface) + def _foo(self): + return { "org.freedesktop.DBus.Property" + ".EmitsChangedSignal": + "false"} + @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") def GetAllClients(self): "D-Bus method" - return dbus.Array(c.dbus_object_path for c in - tcp_server.clients.values()) - - @dbus_annotations({"org.freedesktop.DBus.Deprecated": - "true"}) + return dbus.Array(c.dbus_object_path + for c in + tcp_server.clients.itervalues()) + @dbus.service.method(_interface, out_signature="a{oa{sv}}") def GetAllClientsWithProperties(self): "D-Bus method" return dbus.Dictionary( - {c.dbus_object_path: c.GetAll( - "se.recompile.Mandos.Client") - for c in tcp_server.clients.values()}, + ((c.dbus_object_path, c.GetAll("")) + for c in tcp_server.clients.itervalues()), signature="oa{sv}") - + @dbus.service.method(_interface, in_signature="o") def RemoveClient(self, object_path): "D-Bus method" - for c in tcp_server.clients.values(): + for c in tcp_server.clients.itervalues(): if c.dbus_object_path == object_path: del tcp_server.clients[c.name] c.remove_from_connection() - # Don't signal the disabling + # Don't signal anything except ClientRemoved c.disable(quiet=True) - # Emit D-Bus signal for removal - self.client_removed_signal(c) + # Emit D-Bus signal + self.ClientRemoved(object_path, c.name) return raise KeyError(object_path) - + del _interface - - @dbus.service.method(dbus.OBJECT_MANAGER_IFACE, - 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()}) - - def client_added_signal(self, client): - """Send the new standard signal and the old signal""" - if use_dbus: - # New standard signal - self.InterfacesAdded( - client.dbus_object_path, - dbus.Dictionary( - {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: - # New standard signal - self.InterfacesRemoved( - client.dbus_object_path, - client._get_all_interface_names()) - # 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() + service.cleanup() + + 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. clients = {} with PGPEngine() as pgp: - for client in tcp_server.clients.values(): + for client in tcp_server.clients.itervalues(): key = client_settings[client.name]["secret"] 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"} - for name, typ in inspect.getmembers(dbus.service - .Object): + exclude = set(("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', - suffix=".pickle", - prefix='clients-', - dir=os.path.dirname(stored_state_path), - delete=False) as stored_state: - pickle.dump((clients, client_settings), stored_state, - protocol=2) - tempname = stored_state.name + with (tempfile.NamedTemporaryFile + (mode='wb', suffix=".pickle", prefix='clients-', + dir=os.path.dirname(stored_state_path), + delete=False)) as stored_state: + pickle.dump((clients, client_settings), stored_state) + tempname=stored_state.name os.rename(tempname, stored_state_path) except (IOError, OSError) as e: if not debug: @@ -3412,41 +2779,42 @@ except NameError: pass if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST): - logger.warning("Could not save persistent state: {}" + logger.warning("Could not save persistent state: {0}" .format(os.strerror(e.errno))) else: logger.warning("Could not save persistent state:", exc_info=e) - raise - + raise e + # Delete all clients, and settings from config while tcp_server.clients: name, client = tcp_server.clients.popitem() if use_dbus: client.remove_from_connection() - # Don't signal the disabling + # Don't signal anything except ClientRemoved client.disable(quiet=True) - # Emit D-Bus signal for removal if use_dbus: - mandos_dbus_service.client_removed_signal(client) + # Emit D-Bus signal + mandos_dbus_service.ClientRemoved(client + .dbus_object_path, + client.name) client_settings.clear() - + atexit.register(cleanup) - - for client in tcp_server.clients.values(): + + for client in tcp_server.clients.itervalues(): if use_dbus: - # Emit D-Bus signal for adding - mandos_dbus_service.client_added_signal(client) + # Emit D-Bus signal + mandos_dbus_service.ClientAdded(client.dbus_object_path) # 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] + service.port = tcp_server.socket.getsockname()[1] if use_ipv6: logger.info("Now listening on address %r, port %d," " flowinfo %d, scope_id %d", @@ -3454,25 +2822,24 @@ 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 - try: - service.activate() - except dbus.exceptions.DBusException as error: - logger.critical("D-Bus Exception", exc_info=error) - 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)) - + # From the Avahi example code + try: + service.activate() + except dbus.exceptions.DBusException as error: + logger.critical("D-Bus Exception", exc_info=error) + cleanup() + sys.exit(1) + # End of Avahi example code + + gobject.io_add_watch(tcp_server.fileno(), gobject.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: @@ -3487,6 +2854,5 @@ # Must run before the D-Bus bus name gets deregistered cleanup() - if __name__ == '__main__': main() === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2017-02-23 19:11:11 +0000 +++ mandos-clients.conf.xml 2012-06-23 00:58:49 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/clients.conf"> - + %common; ]> @@ -37,11 +37,6 @@ 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -122,7 +117,7 @@ How long to wait for external approval before resorting to use the value. The - default is PT0S, i.e. not to wait. + default is 0s, i.e. not to wait. The format of TIME is the same @@ -182,13 +177,7 @@ PATH will be searched. The default value for the checker command is fping %%(host)s. Note that - mandos-keygen, when generating output - to be inserted into this file, normally looks for an SSH - server on the Mandos client, and, if it find one, outputs - a option to check for the - client’s key fingerprint – this is more secure against - spoofing. + >-- %%(host)s. In addition to normal start time expansion, this option @@ -231,7 +220,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. @@ -464,7 +453,6 @@ %(foo)s is obscure. - === modified file 'mandos-ctl' --- mandos-ctl 2017-08-20 13:50:57 +0000 +++ mandos-ctl 2012-11-14 21:03:24 +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-2012 Teddy Hogeborn +# Copyright © 2008-2012 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,21 +15,18 @@ # 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) -try: - from future_builtins import * -except ImportError: - pass +from future_builtins import * import sys import argparse @@ -38,13 +35,10 @@ import re import os import collections -import json +import doctest import dbus -if sys.version_info.major == 2: - str = unicode - locale.setlocale(locale.LC_ALL, "") tablewords = { @@ -64,37 +58,35 @@ "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" busname = domain + ".Mandos" server_path = "/" server_interface = domain + ".Mandos" client_interface = domain + ".Mandos.Client" -version = "1.7.15" - - -try: - dbus.OBJECT_MANAGER_IFACE -except AttributeError: - dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager" - +version = "1.6.0" + +def timedelta_to_milliseconds(td): + """Convert a datetime.timedelta object to milliseconds""" + return ((td.days * 24 * 60 * 60 * 1000) + + (td.seconds * 1000) + + (td.microseconds // 1000)) 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)) + .format(days = "{0}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,26 +102,30 @@ >>> 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 - # a "group" containing digits - "value", # datetime.timedelta or None - "followers")) # Tokens valid after this token + Token = collections.namedtuple("Token", + ("regexp", # To match token; if + # "value" is not None, + # must have a "group" + # containing digits + "value", # datetime.timedelta or + # None + "followers")) # Tokens valid after + # this token # RFC 3339 "duration" tokens, syntax, and semantics; taken from # the "duration" ABNF definition in RFC 3339, Appendix A. token_end = Token(re.compile(r"$"), None, frozenset()) token_second = Token(re.compile(r"(\d+)S"), datetime.timedelta(seconds=1), - frozenset((token_end, ))) + frozenset((token_end,))) token_minute = Token(re.compile(r"(\d+)M"), datetime.timedelta(minutes=1), frozenset((token_second, token_end))) @@ -151,19 +147,16 @@ frozenset((token_month, token_end))) token_week = Token(re.compile(r"(\d+)W"), datetime.timedelta(weeks=1), - frozenset((token_end, ))) + frozenset((token_end,))) token_duration = Token(re.compile(r"P"), None, frozenset((token_year, token_month, token_day, token_time, - token_week))) - # Define starting values: - # Value so far - value = datetime.timedelta() + token_week))), + # 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 @@ -185,37 +178,36 @@ break else: # No currently valid tokens were found - raise ValueError("Invalid RFC 3339 duration: {!r}" - .format(duration)) + raise ValueError("Invalid RFC 3339 duration") # End token found return value def string_to_delta(interval): """Parse a string and return a datetime.timedelta - - >>> string_to_delta('7d') + + >>> string_to_delta("7d") datetime.timedelta(7) - >>> string_to_delta('60s') + >>> string_to_delta("60s") datetime.timedelta(0, 60) - >>> string_to_delta('60m') + >>> string_to_delta("60m") datetime.timedelta(0, 3600) - >>> string_to_delta('24h') + >>> string_to_delta("24h") datetime.timedelta(1) - >>> string_to_delta('1w') + >>> string_to_delta("1w") datetime.timedelta(7) - >>> string_to_delta('5m 30s') + >>> 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)) @@ -231,7 +223,6 @@ value += datetime.timedelta(0, 0, 0, int(num)) return value - def print_clients(clients, keywords): def valuetostring(value, keyword): if type(value) is dbus.Boolean: @@ -239,22 +230,23 @@ if keyword in ("Timeout", "Interval", "ApprovalDelay", "ApprovalDuration", "ExtendedTimeout"): return milliseconds_to_string(value) - return str(value) - + return unicode(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) - for key in keywords) + 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(**dict((key, + valuetostring(client[key], + key)) + for key in keywords))) def has_actions(options): return any((options.enable, @@ -276,18 +268,15 @@ options.approve, options.deny)) - def main(): parser = argparse.ArgumentParser() parser.add_argument("--version", action="version", - version="%(prog)s {}".format(version), + version = "%(prog)s {0}".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", @@ -321,8 +310,7 @@ parser.add_argument("--approval-duration", help="Set duration of one client approval") parser.add_argument("-H", "--host", help="Set host for client") - parser.add_argument("-s", "--secret", - type=argparse.FileType(mode="rb"), + parser.add_argument("-s", "--secret", type=file, help="Set password blob (file) for client") parser.add_argument("-A", "--approve", action="store_true", help="Approve any current client request") @@ -332,116 +320,93 @@ 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) - + sys.exit(0 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) + print("Could not connect to Mandos server", + file=sys.stderr) sys.exit(1) - + mandos_serv = dbus.Interface(mandos_dbus_objc, - 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 + dbus_interface = server_interface) + + #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 = mandos_serv.GetAllClientsWithProperties() 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), + except dbus.exceptions.DBusException: + print("Access denied: Accessing mandos server through dbus.", 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 = dict((bus.get_object(busname, path), properties) + for path, properties in + mandos_clients.iteritems()) else: for name in options.client: - for path, client in mandos_clients.items(): + for path, client in mandos_clients.iteritems(): if client["Name"] == name: client_objc = bus.get_object(busname, path) clients[client_objc] = client break else: - print("Client not found on server: {!r}" + print("Client not found on server: {0!r}" .format(name), file=sys.stderr) sys.exit(1) - + if not has_actions(options) and clients: - if options.verbose or options.dump_json: - keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK", - "Created", "Interval", "Host", "Fingerprint", - "CheckerRunning", "LastEnabled", - "ApprovalPending", "ApprovedByDefault", + 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) - + timedelta_to_milliseconds + (string_to_delta(value))) if options.remove: mandos_serv.RemoveClient(client.__dbus_object_path__) if options.enable: @@ -455,11 +420,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: @@ -491,6 +456,5 @@ client.Approve(dbus.Boolean(False), dbus_interface=client_interface) - if __name__ == "__main__": main() === modified file 'mandos-ctl.xml' --- mandos-ctl.xml 2017-02-23 19:11:11 +0000 +++ mandos-ctl.xml 2012-06-22 23:33:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -34,11 +34,6 @@ 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -53,111 +48,109 @@ &COMMANDNAME; - Control or query the operation of the Mandos server + Control the operation of the Mandos server &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -171,11 +164,8 @@ &COMMANDNAME; - - - - - + + @@ -214,10 +204,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 +470,6 @@ - - - - - Dump client settings as JSON to standard output. - - - - - @@ -531,10 +510,11 @@ - - BUGS - - + + + + + EXAMPLE === modified file 'mandos-keygen' --- mandos-keygen 2017-08-20 14:08:59 +0000 +++ mandos-keygen 2013-09-29 15:52:19 +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-2012 Teddy Hogeborn +# Copyright © 2008-2012 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.6.0" KEYDIR="/etc/keys/mandos" KEYTYPE=RSA @@ -30,10 +30,9 @@ SUBKEYLENGTH=4096 KEYNAME="`hostname --fqdn 2>/dev/null || hostname`" KEYEMAIL="" -KEYCOMMENT="" +KEYCOMMENT="Mandos client key" KEYEXPIRE=0 FORCE=no -SSH=yes KEYCOMMENT_ORIG="$KEYCOMMENT" mode=keygen @@ -42,12 +41,12 @@ fi # Parse options -TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:fS \ - --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force,no-ssh \ +TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \ + --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \ --name "$0" -- "$@"` help(){ -basename="`basename "$0"`" +basename="`basename $0`" cat <&2 + echo "Unknown arguments: '$@'" >&2 exit 1 fi @@ -161,8 +159,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 @@ -191,7 +189,7 @@ trap " set +e; \ test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \ -shred --remove \"$RINGDIR\"/sec* 2>/dev/null; +shred --remove \"$RINGDIR\"/sec*; test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \ rm --recursive --force \"$RINGDIR\"; tty --quiet && stty echo; \ @@ -206,10 +204,10 @@ cat >"$BATCHFILE" <<-EOF Key-Type: $KEYTYPE Key-Length: $KEYLENGTH - Key-Usage: sign,auth + #Key-Usage: encrypt,sign,auth Subkey-Type: $SUBKEYTYPE Subkey-Length: $SUBKEYLENGTH - Subkey-Usage: encrypt + #Subkey-Usage: encrypt,sign,auth Name-Real: $KEYNAME $KEYCOMMENTLINE $KEYEMAILLINE @@ -218,7 +216,6 @@ #Handle: #%pubring pubring.gpg #%secring secring.gpg - %no-protection %commit EOF @@ -232,11 +229,6 @@ date fi - # Make sure trustdb.gpg exists; - # this is a workaround for Debian bug #737128 - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" \ - --import-ownertrust < /dev/null # Generate a new key in the key rings gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always \ @@ -278,30 +270,6 @@ fi if [ "$mode" = password ]; then - - # Make SSH be 0 or 1 - case "$SSH" in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;; - [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;; - esac - - if [ $SSH -eq 1 ]; then - for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do - set +e - ssh_fingerprint="`ssh-keyscan -t $ssh_keytype localhost 2>/dev/null`" - err=$? - set -e - if [ $err -ne 0 ]; then - ssh_fingerprint="" - continue - fi - if [ -n "$ssh_fingerprint" ]; then - ssh_fingerprint="${ssh_fingerprint#localhost }" - break - fi - done - fi - # Import key into temporary key rings gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor \ @@ -312,7 +280,7 @@ # Get fingerprint of key FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \ - --enable-dsa2 --homedir "$RINGDIR" --trust-model always \ + --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \ --fingerprint --with-colons \ | sed --quiet \ --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`" @@ -326,11 +294,11 @@ cat "$PASSFILE" else tty --quiet && stty -echo - echo -n "Enter passphrase: " >/dev/tty - read -r first + echo -n "Enter passphrase: " + read first tty --quiet && echo >&2 - echo -n "Repeat passphrase: " >/dev/tty - read -r second + echo -n "Repeat passphrase: " + read second if tty --quiet; then echo >&2 stty echo @@ -370,10 +338,6 @@ /^[^-]/s/^/ /p } }' < "$SECFILE" - if [ -n "$ssh_fingerprint" ]; then - echo 'checker = ssh-keyscan -t '"$ssh_keytype"' %%(host)s 2>/dev/null | grep --fixed-strings --line-regexp --quiet --regexp=%%(host)s" %(ssh_fingerprint)s"' - echo "ssh_fingerprint = ${ssh_fingerprint}" - fi fi trap - EXIT @@ -384,5 +348,5 @@ shred --remove "$SECFILE" fi # Remove the key rings -shred --remove "$RINGDIR"/sec* 2>/dev/null +shred --remove "$RINGDIR"/sec* rm --recursive --force "$RINGDIR" === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2017-02-23 19:11:11 +0000 +++ mandos-keygen.xml 2013-09-29 15:52:19 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,8 @@ 2008 2009 - 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -125,10 +119,7 @@ TIME - - - - + &COMMANDNAME; @@ -154,10 +145,6 @@ - - - - &COMMANDNAME; @@ -301,7 +288,8 @@ TEXT - Comment field for key. Default is empty. + Comment field for key. The default value is + Mandos client key. @@ -359,22 +347,6 @@ - - - - - - When or - is given, this option will - prevent &COMMANDNAME; from calling - ssh-keyscan to get an SSH fingerprint - for this host and, if successful, output suitable config - options to use this fingerprint as a - option in the output. This is - otherwise the default behavior. - - - @@ -451,10 +423,11 @@ - - BUGS - - + + + + + EXAMPLE @@ -530,9 +503,7 @@ mandos 8, mandos-client - 8mandos, - ssh-keyscan - 1 + 8mandos === modified file 'mandos-monitor' --- mandos-monitor 2017-02-23 20:35:20 +0000 +++ mandos-monitor 2013-05-22 20:00:18 +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-2012 Teddy Hogeborn +# Copyright © 2009-2012 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) @@ -32,6 +32,7 @@ import sys import os +import signal import datetime @@ -39,32 +40,28 @@ import urwid from dbus.mainloop.glib import DBusGMainLoop -from gi.repository import GLib +try: + import gobject +except ImportError: + from gi.repository import GObject as gobject import dbus import locale -import logging - -if sys.version_info.major == 2: +if sys.version_info[0] == 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" - -try: - dbus.OBJECT_MANAGER_IFACE -except AttributeError: - dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager" - +version = "1.6.0" def isoformat_to_datetime(iso): "Parse an ISO 8601 date string to a datetime.datetime()" @@ -79,9 +76,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,29 +85,33 @@ 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, + self.proxy.connect_to_signal("PropertyChanged", + self._property_changed, + client_interface, 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. + + def _property_changed(self, property, value): + """Helper which takes positional arguments""" + return self.property_changed(property=property, value=value) + + def property_changed(self, property=None, value=None): + """This is called whenever we get a PropertyChanged signal + It updates the changed property in the "properties" dict. """ # Update properties dict with new value - if interface == client_interface: - self.properties.update(properties) - + self.properties[property] = value + def delete(self): self.property_changed_match.remove() @@ -119,7 +119,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 +130,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 +140,7 @@ super(MandosClientWidget, self).__init__(**kwargs) self.update() self.opened = False - + self.match_objects = ( self.proxy.connect_to_signal("CheckerCompleted", self.checker_completed, @@ -162,101 +162,109 @@ self.rejected, client_interface, byte_arrays=True)) - self.logger('Created client {}' - .format(self.properties["Name"]), level=0) - + #self.logger('Created client {0}' + # .format(self.properties["Name"])) + def using_timer(self, flag): """Call this method with True or False when timer should be activated or deactivated. """ if flag and self._update_timer_callback_tag is None: # Will update the shown timer value every second - self._update_timer_callback_tag = (GLib.timeout_add + self._update_timer_callback_tag = (gobject.timeout_add (1000, self.update_timer)) elif not (flag or self._update_timer_callback_tag is None): - GLib.source_remove(self._update_timer_callback_tag) + gobject.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 "{}")' - ' succeeded'.format(self.properties["Name"], - command), level=0) self.update() return # Checker failed if os.WIFEXITED(condition): - self.logger('Checker for client {} (command "{}") failed' - ' with exit code {}' + self.logger('Checker for client {0} (command "{1}")' + ' failed with exit code {2}' .format(self.properties["Name"], command, os.WEXITSTATUS(condition))) elif os.WIFSIGNALED(condition): - self.logger('Checker for client {} (command "{}") was' - ' killed by signal {}' + self.logger('Checker for client {0} (command "{1}") was' + ' killed by signal {2}' .format(self.properties["Name"], command, os.WTERMSIG(condition))) + elif os.WCOREDUMP(condition): + self.logger('Checker for client {0} (command "{1}")' + ' dumped core' + .format(self.properties["Name"], command)) + else: + self.logger('Checker for client {0} completed' + ' mysteriously' + .format(self.properties["Name"])) 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) - + """Server signals that a checker started. This could be useful + to log in the future. """ + #self.logger('Client {0} started checker "{1}"' + # .format(self.properties["Name"], + # str(command))) + pass + def got_secret(self): - self.logger('Client {} received its secret' + self.logger('Client {0} received its secret' .format(self.properties["Name"])) - + def need_approval(self, timeout, default): if not default: - message = 'Client {} needs approval within {} seconds' + message = 'Client {0} needs approval within {1} seconds' else: - message = 'Client {} will get its secret in {} seconds' + message = 'Client {0} will get its secret in {1} seconds' self.logger(message.format(self.properties["Name"], timeout/1000)) - + def rejected(self, reason): - self.logger('Client {} was rejected; reason: {}' + self.logger('Client {0} was rejected; reason: {1}' .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: @@ -266,9 +274,9 @@ else: timer = datetime.timedelta() if self.properties["ApprovedByDefault"]: - message = "Approval in {}. (d)eny?" + message = "Approval in {0}. (d)eny?" else: - message = "Denial in {}. (a)pprove?" + message = "Denial in {0}. (a)pprove?" message = message.format(str(timer).rsplit(".", 1)[0]) self.using_timer(True) elif self.properties["LastCheckerStatus"] != 0: @@ -282,14 +290,14 @@ timer = max(expires - datetime.datetime.utcnow(), datetime.timedelta()) message = ('A checker has failed! Time until client' - ' gets disabled: {}' + ' gets disabled: {0}' .format(str(timer).rsplit(".", 1)[0])) self.using_timer(True) else: message = "enabled" self.using_timer(False) - self._text = "{}{}".format(base, message) - + self._text = "{0}{1}".format(base, message) + if not urwid.supports_unicode(): self._text = self._text.encode("ascii", "replace") textlist = [("normal", self._text)] @@ -305,17 +313,16 @@ # 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 - """ + """called by gobject. Will indefinitely loop until + gobject.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) + gobject.source_remove(self._update_timer_callback_tag) self._update_timer_callback_tag = None for match in self.match_objects: match.remove() @@ -323,46 +330,42 @@ 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) + self.proxy.Enable(dbus_interface = client_interface, + ignore_reply=True) elif key == "-": - self.proxy.Set(client_interface, "Enabled", False, - ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + self.proxy.Disable(dbus_interface = client_interface, + ignore_reply=True) 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 .object_path, ignore_reply=True) elif key == "s": - self.proxy.Set(client_interface, "CheckerRunning", - dbus.Boolean(True), ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + self.proxy.StartChecker(dbus_interface = client_interface, + ignore_reply=True) elif key == "S": - self.proxy.Set(client_interface, "CheckerRunning", - dbus.Boolean(False), ignore_reply=True, - dbus_interface=dbus.PROPERTIES_IFACE) + self.proxy.StopChecker(dbus_interface = client_interface, + ignore_reply=True) 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,16 +376,15 @@ # self.open() else: return key - - def properties_changed(self, interface, properties, invalidated): - """Call self.update() if any properties changed. + + def property_changed(self, property=None, **kwargs): + """Call self.update() if old value is not new value. This overrides the method from MandosClientPropertyCache""" - 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) - for key in old_values): + property_name = str(property) + old_value = self.properties.get(property_name) + super(MandosClientWidget, self).property_changed( + property=property, **kwargs) + if self.properties.get(property_name) != old_value: self.update() @@ -392,8 +394,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 @@ -403,11 +404,11 @@ """This is the entire user interface - the whole screen with boxes, lists of client widgets, etc. """ - def __init__(self, max_log_length=1000, log_level=1): + def __init__(self, max_log_length=1000): DBusGMainLoop(set_as_default=True) - + self.screen = urwid.curses_display.Screen() - + self.screen.register_palette(( ("normal", "default", "default", None), @@ -418,8 +419,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,90 +429,86 @@ "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() - + self.main_loop = gobject.MainLoop() + def client_not_found(self, fingerprint, address): - self.log_message("Client with address {} and fingerprint {}" - " could not be found" + self.log_message("Client with address {0} and fingerprint" + " {1} 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): + + def log_message(self, message): """Log message formatted with timestamp""" - if level < self.log_level: - return timestamp = datetime.datetime.now().isoformat() - self.log_message_raw("{}: {}".format(timestamp, message), - level=level) - - def log_message_raw(self, markup, level=1): + self.log_message_raw(timestamp + ": " + message) + + def log_message_raw(self, markup): """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) - + #self.log_message("Log visibility changed to: " + # + str(self.log_visible)) + def change_log_display(self): """Change type of log display. Currently, this toggles wrapping of text lines.""" @@ -522,45 +518,36 @@ self.log_wrap = "clip" for textwidget in self.log: 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): + #self.log_message("Wrap mode: " + self.log_wrap) + + def find_and_remove_client(self, path, name): """Find a client by its object path and remove it. - - This is connected to the InterfacesRemoved signal from the + + This is connected to the ClientRemoved signal from the Mandos server object.""" - if client_interface not in interfaces: - # Not a Mandos client object; ignore - return try: client = self.clients_dict[path] except KeyError: # not found? - self.log_message("Unknown client {!r} removed" - .format(path)) + self.log_message("Unknown client {0!r} ({1!r}) removed" + .format(name, 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. - """ - if client_interface not in ifs_and_props: - # Not a Mandos client object; ignore - return + + def add_new_client(self, path): 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), path=path) - + def add_client(self, client, path=None): self.clients.append(client) if path is None: @@ -568,46 +555,45 @@ 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", + .connect_to_signal("ClientRemoved", self.find_and_remove_client, - dbus_interface=dbus.OBJECT_MANAGER_IFACE, + dbus_interface=server_interface, byte_arrays=True)) (self.mandos_serv - .connect_to_signal("InterfacesAdded", + .connect_to_signal("ClientAdded", self.add_new_client, - dbus_interface=dbus.OBJECT_MANAGER_IFACE, + dbus_interface=server_interface, byte_arrays=True)) (self.mandos_serv .connect_to_signal("ClientNotFound", @@ -617,54 +603,57 @@ 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 + self._input_callback_tag = (gobject.io_add_watch (sys.stdin.fileno(), - GLib.IO_IN, + gobject.IO_IN, self.process_input)) self.main_loop.run() # Main loop has finished, we should close everything now - GLib.source_remove(self._input_callback_tag) + gobject.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 elif key == "window resize": self.size = self.screen.get_cols_rows() self.refresh() - elif key == "ctrl l": - self.screen.clear() + elif key == "\f": # Ctrl-L self.refresh() elif key == "l" or key == "D": self.toggle_log_display() @@ -682,9 +671,7 @@ "?: Help", "l: Log window toggle", "TAB: Switch window", - "w: Wrap (log lines)", - "v: Toggle verbose log", - )))) + "w: Wrap (log)")))) self.log_message_raw(("bold", " " .join(("Clients:", @@ -703,37 +690,29 @@ else: self.topwidget.set_focus(self.logbox) self.refresh() - elif key == "v": - if self.log_level == 0: - self.log_level = 1 - self.log_message("Verbose mode: Off") - 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 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -34,11 +34,6 @@ 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -134,10 +129,6 @@ Toggle log window line wrap - v - Toggle verbose logging - - Up, Ctrl-P, k Move up a line @@ -200,10 +191,9 @@ BUGS This program can currently only be used to monitor and control a - Mandos server with the default D-Bus bus name of - se.recompile.Mandos. + Mandos server with the default D-Bus service name of + Mandos. - === modified file 'mandos-options.xml' --- mandos-options.xml 2015-07-20 03:03:33 +0000 +++ mandos-options.xml 2013-06-23 15:13:06 +0000 @@ -49,15 +49,13 @@ GnuTLS priority string for the TLS handshake. The default is SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA - :+SIGN-DSA-SHA256. + >SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224. See gnutls_priority_init 3 for the syntax. Warning: changing this may make the TLS handshake fail, making server-client - communication impossible. Changing this option may also make the - network traffic decryptable by an attacker. + communication impossible. @@ -113,11 +111,4 @@ implies this option. - - This option controls whether the server will announce its - existence using Zeroconf. Default is to use Zeroconf. If - Zeroconf is not used, a number or a - is required. - - === modified file 'mandos.conf' --- mandos.conf 2015-07-20 03:03:33 +0000 +++ mandos.conf 2013-06-23 15:13:06 +0000 @@ -23,7 +23,7 @@ ;debug = False # GnuTLS priority for the TLS handshake. See gnutls_priority_init(3). -;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256 +;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224 # Zeroconf service name. You need to change this if you for some # reason want to run more than one server on the same *host*. @@ -45,9 +45,3 @@ # Whether to run in the foreground ;foreground = False - -# File descriptor number to use for network socket -;socket = - -# Whether to use ZeroConf; if false, requires port or socket -;zeroconf = True === modified file 'mandos.conf.xml' --- mandos.conf.xml 2017-02-23 19:11:11 +0000 +++ mandos.conf.xml 2012-05-26 22:21:17 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/mandos.conf"> - + %common; ]> @@ -34,14 +34,8 @@ 2008 2009 - 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -205,7 +199,6 @@ built-in module ConfigParser requires it. - @@ -228,8 +221,8 @@ interface = eth0 address = fe80::aede:48ff:fe71:f6f2 port = 1025 -debug = True -priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA +debug = true +priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP servicename = Daena use_dbus = False use_ipv6 = True === modified file 'mandos.lsm' --- mandos.lsm 2017-06-24 10:08:21 +0000 +++ mandos.lsm 2012-06-17 22:26:40 +0000 @@ -1,23 +1,22 @@ Begin4 Title: Mandos -Version: 1.7.15 -Entered-date: 2017-06-24 +Version: 1.6.0 +Entered-date: 2012-06-18 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. +root file systems and at the same time be capable of remote and/or +unattended reboots. Keywords: boot, encryption, luks, cryptsetup, network, openpgp, - tls, dm-crypt +tls, dm-crypt Author: teddy@recompile.se (Teddy Hogeborn), belorn@recompile.se (Björn Påhlsson) 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 -Alternate-site: ftp://ftp.recompile.se/pub/mandos - 173K mandos_1.7.15.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 - distributions, but not other Unixes. -Copying-policy: GNU General Public License version 3.0 or later +Primary-site: http://www.recompile.se/mandos + 150K mandos_1.6.0.orig.tar.gz +Alternate-site: ftp://ftp.recompile.se/pub/mandos + 150K mandos_1.6.0.orig.tar.gz +Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.6, and +various other libraries. While made for Debian GNU/Linux, it is +probably portable to other distributions, but not other Unixes. +Copying-policy: GNU General Public License version 3.0 or later End === removed file 'mandos.service' --- mandos.service 2017-08-20 14:14:14 +0000 +++ mandos.service 1970-01-01 00:00:00 +0000 @@ -1,35 +0,0 @@ -[Unit] -Description=Server of encrypted passwords to Mandos clients -Documentation=man:intro(8mandos) man:mandos(8) -## If the server is configured to listen to a specific IP or network -## interface, it may be necessary to change "network.target" to -## "network-online.target". -After=network.target -## 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 - -[Service] -## If the server's D-Bus interface is disabled, the "BusName" setting -## should be removed or commented out. -BusName=se.recompile.Mandos -ExecStart=/usr/sbin/mandos --foreground -Restart=always -KillMode=mixed -## Using socket activation won't work, because systemd always does -## bind() on the socket, and also won't announce the ZeroConf service. -#ExecStart=/usr/sbin/mandos --foreground --socket=0 -#StandardInput=socket -# Restrict what the Mandos daemon can do. Note that this also affects -# "checker" programs! -PrivateTmp=yes -PrivateDevices=yes -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 2012-06-17 14:55:31 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -36,11 +36,6 @@ 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -110,8 +105,6 @@ FD - - &COMMANDNAME; @@ -328,13 +321,6 @@ - - - - - - - @@ -530,19 +516,18 @@ - /run/mandos.pid + /var/run/mandos.pid The file containing the process id of the &COMMANDNAME; process started last. - Note: If the /run directory does not - exist, /var/run/mandos.pid will be - used instead. + /dev/log + + /var/lib/mandos @@ -554,7 +539,7 @@ - /dev/log + /dev/log The Unix domain socket to where local syslog messages are @@ -589,7 +574,6 @@ This server does not check the expire time of clients’ OpenPGP keys. - @@ -708,7 +692,8 @@ - GnuTLS + GnuTLS @@ -752,12 +737,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. @@ -773,8 +758,8 @@ - RFC 6091: Using OpenPGP Keys for Transport Layer - Security (TLS) Authentication + RFC 5081: Using OpenPGP Keys for Transport Layer + Security === 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 === removed directory 'plugin-helpers' === removed 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 1970-01-01 00:00:00 +0000 @@ -1,282 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * iprouteadddel - Add or delete direct route to a local IP address - * - * Copyright © 2015-2017 Teddy Hogeborn - * Copyright © 2015-2017 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 (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, 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 . - */ - -#define _GNU_SOURCE /* asprintf(), - program_invocation_short_name */ -#include /* bool, false, true */ -#include /* fprintf(), stderr, FILE, vfprintf */ -#include /* program_invocation_short_name, - errno, perror(), EINVAL, ENOMEM */ -#include /* va_list, va_start */ -#include /* EXIT_SUCCESS */ -#include /* struct argp_option, error_t, struct - argp_state, ARGP_KEY_ARG, - argp_usage(), ARGP_KEY_END, - ARGP_ERR_UNKNOWN, struct argp, - argp_parse() */ -#include /* EX_USAGE, EX_OSERR */ -#include /* sa_family_t, AF_INET6, AF_INET */ -#include /* PRIdMAX, intmax_t */ - -#include /* struct nl_addr, nl_addr_parse(), - nl_geterror(), - nl_addr_get_family(), - nl_addr_put() */ -#include /* struct rtnl_route, - struct rtnl_nexthop, - rtnl_route_alloc(), - rtnl_route_set_family(), - rtnl_route_set_protocol(), - RTPROT_BOOT, - rtnl_route_set_scope(), - RT_SCOPE_LINK, - rtnl_route_set_type(), - RTN_UNICAST, - rtnl_route_set_dst(), - rtnl_route_set_table(), - RT_TABLE_MAIN, - rtnl_route_nh_alloc(), - rtnl_route_nh_set_ifindex(), - rtnl_route_add_nexthop(), - rtnl_route_add(), - rtnl_route_delete(), - rtnl_route_put(), - rtnl_route_nh_free() */ -#include /* struct nl_sock, nl_socket_alloc(), - nl_connect(), nl_socket_free() */ -#include /* rtnl_link_get_kernel(), - rtnl_link_get_ifindex(), - rtnl_link_put() */ - -bool debug = false; -const char *argp_program_version = "mandos-client-iprouteadddel " VERSION; -const char *argp_program_bug_address = ""; - -/* Function to use when printing errors */ -void perror_plus(const char *print_text){ - int e = errno; - fprintf(stderr, "Mandos plugin helper %s: ", - program_invocation_short_name); - errno = e; - perror(print_text); -} - -__attribute__((format (gnu_printf, 2, 3), nonnull)) -int fprintf_plus(FILE *stream, const char *format, ...){ - va_list ap; - va_start (ap, format); - - fprintf(stream, "Mandos plugin helper %s: ", - program_invocation_short_name); - return vfprintf(stream, format, ap); -} - -int main(int argc, char *argv[]){ - int ret; - int exitcode = EXIT_SUCCESS; - struct arguments { - bool add; /* true: add, false: delete */ - char *address; /* IP address as string */ - struct nl_addr *nl_addr; /* Netlink IP address */ - char *interface; /* interface name */ - } arguments = { .add = true, .address = NULL, .interface = NULL }; - struct argp_option options[] = { - { .name = "debug", .key = 128, - .doc = "Debug mode" }, - { .name = NULL } - }; - struct rtnl_route *route = NULL; - struct rtnl_nexthop *nexthop = NULL; - struct nl_sock *sk = NULL; - - error_t parse_opt(int key, char *arg, struct argp_state *state){ - int lret; - errno = 0; - switch(key){ - case 128: /* --debug */ - debug = true; - break; - case ARGP_KEY_ARG: - switch(state->arg_num){ - case 0: - if(strcasecmp(arg, "add") == 0){ - ((struct arguments *)(state->input))->add = true; - } else if(strcasecmp(arg, "delete") == 0){ - ((struct arguments *)(state->input))->add = false; - } else { - fprintf_plus(stderr, "Unrecognized command: %s\n", arg); - argp_usage(state); - } - break; - case 1: - ((struct arguments *)(state->input))->address = arg; - lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *) - (state->input)) - ->nl_addr)); - if(lret != 0){ - fprintf_plus(stderr, "Failed to parse address %s: %s\n", - arg, nl_geterror(lret)); - argp_usage(state); - } - break; - case 2: - ((struct arguments *)(state->input))->interface = arg; - break; - default: - argp_usage(state); - } - break; - case ARGP_KEY_END: - if(state->arg_num < 3){ - argp_usage(state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - return errno; - } - - struct argp argp = { .options = options, .parser = parse_opt, - .args_doc = "[ add | delete ] ADDRESS INTERFACE", - .doc = "Mandos client helper -- Add or delete" - " local route to IP address on interface" }; - - ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments); - switch(ret){ - case 0: - break; - case EINVAL: - exit(EX_USAGE); - case ENOMEM: - default: - errno = ret; - perror_plus("argp_parse"); - exitcode = EX_OSERR; - goto end; - } - /* Get netlink socket */ - sk = nl_socket_alloc(); - if(sk == NULL){ - fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n", - nl_geterror(ret)); - exitcode = EX_OSERR; - goto end; - } - /* Connect socket to netlink */ - ret = nl_connect(sk, NETLINK_ROUTE); - if(ret < 0){ - fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n", - nl_geterror(ret)); - exitcode = EX_OSERR; - goto end; - } - /* Get link object of specified interface */ - struct rtnl_link *link = NULL; - ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link); - if(ret < 0){ - fprintf_plus(stderr, "Failed to use interface %s: %s\n", - arguments.interface, nl_geterror(ret)); - exitcode = EX_OSERR; - goto end; - } - /* Get netlink route object */ - route = rtnl_route_alloc(); - if(route == NULL){ - fprintf_plus(stderr, "Failed to get netlink route:\n"); - exitcode = EX_OSERR; - goto end; - } - /* Get address family of specified address */ - sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr); - if(debug){ - fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX - ")\n", arguments.address, - af == AF_INET6 ? "AF_INET6" : - ( af == AF_INET ? "AF_INET" : "UNKNOWN"), - (intmax_t)af); - } - /* Set route parameters: */ - rtnl_route_set_family(route, (uint8_t)af); /* Address family */ - rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see - ip-route(8) */ - rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */ - rtnl_route_set_type(route, RTN_UNICAST); /* normal unicast - address route */ - rtnl_route_set_dst(route, arguments.nl_addr); /* Destination - address */ - rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing - table */ - /* Create nexthop */ - nexthop = rtnl_route_nh_alloc(); - if(nexthop == NULL){ - fprintf_plus(stderr, "Failed to get netlink route nexthop\n"); - exitcode = EX_OSERR; - goto end; - } - /* Get index number of specified interface */ - int ifindex = rtnl_link_get_ifindex(link); - if(debug){ - fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface, - ifindex); - } - /* Set interface index number on nexthop object */ - rtnl_route_nh_set_ifindex(nexthop, ifindex); - /* Set route tu use nexthop object */ - rtnl_route_add_nexthop(route, nexthop); - /* Add or delete route? */ - if(arguments.add){ - ret = rtnl_route_add(sk, route, NLM_F_EXCL); - } else { - ret = rtnl_route_delete(sk, route, 0); - } - if(ret < 0){ - fprintf_plus(stderr, "Failed to %s route: %s\n", - arguments.add ? "add" : "delete", - nl_geterror(ret)); - exitcode = EX_OSERR; - goto end; - } - end: - /* Deallocate route */ - if(route){ - rtnl_route_put(route); - } else if(nexthop) { - /* Deallocate route nexthop */ - rtnl_route_nh_free(nexthop); - } - /* Deallocate parsed address */ - if(arguments.nl_addr){ - nl_addr_put(arguments.nl_addr); - } - /* Deallocate link struct */ - if(link){ - rtnl_link_put(link); - } - /* Deallocate netlink socket struct */ - if(sk){ - nl_socket_free(sk); - } - return exitcode; -} === modified file 'plugin-runner.c' --- plugin-runner.c 2017-02-23 19:11:11 +0000 +++ plugin-runner.c 2013-08-27 21:47:35 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -23,36 +23,38 @@ */ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(), - O_CLOEXEC, pipe2() */ + asprintf(), O_CLOEXEC */ #include /* size_t, NULL */ #include /* malloc(), exit(), EXIT_SUCCESS, realloc() */ #include /* bool, true, false */ #include /* fileno(), fprintf(), - stderr, STDOUT_FILENO, fclose() */ -#include /* fstat(), struct stat, waitpid(), - WIFEXITED(), WEXITSTATUS(), wait(), - pid_t, uid_t, gid_t, getuid(), - getgid() */ + stderr, STDOUT_FILENO */ +#include /* DIR, fdopendir(), stat(), struct + stat, waitpid(), WIFEXITED(), + WEXITSTATUS(), wait(), pid_t, + uid_t, gid_t, getuid(), getgid(), + dirfd() */ #include /* fd_set, select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR */ #include /* wait(), waitpid(), WIFEXITED(), - WEXITSTATUS(), WTERMSIG() */ -#include /* struct stat, fstat(), S_ISREG() */ + WEXITSTATUS(), WTERMSIG(), + WCOREDUMP() */ +#include /* struct stat, stat(), S_ISREG() */ #include /* and, or, not */ -#include /* struct dirent, scandirat() */ -#include /* fcntl(), F_GETFD, F_SETFD, - FD_CLOEXEC, write(), STDOUT_FILENO, - struct stat, fstat(), close(), - setgid(), setuid(), S_ISREG(), - faccessat() pipe2(), fork(), - _exit(), dup2(), fexecve(), read() - */ +#include /* DIR, struct dirent, fdopendir(), + readdir(), closedir(), dirfd() */ +#include /* struct stat, stat(), S_ISREG(), + fcntl(), setuid(), setgid(), + F_GETFD, F_SETFD, FD_CLOEXEC, + access(), pipe(), fork(), close() + dup2(), STDOUT_FILENO, _exit(), + execv(), write(), read(), + close() */ #include /* fcntl(), F_GETFD, F_SETFD, - FD_CLOEXEC, openat(), scandirat(), - pipe2() */ -#include /* strsep, strlen(), strsignal(), - strcmp(), strncmp() */ + FD_CLOEXEC */ +#include /* strsep, strlen(), asprintf(), + strsignal(), strcmp(), strncmp() */ #include /* errno */ #include /* struct argp_option, struct argp_state, struct argp, @@ -70,12 +72,10 @@ EX_CONFIG, EX_UNAVAILABLE, EX_OK */ #include /* errno */ #include /* error() */ -#include /* fnmatch() */ #define BUFFER_SIZE 256 #define PDIR "/lib/mandos/plugins.d" -#define PHDIR "/lib/mandos/plugin-helpers" #define AFILE "/conf/conf.d/mandos/plugin-runner.conf" const char *argp_program_version = "plugin-runner " VERSION; @@ -105,7 +105,6 @@ /* Gets an existing plugin based on name, or if none is found, creates a new one */ -__attribute__((warn_unused_result)) static plugin *getplugin(char *name){ /* Check for existing plugin with that name */ for(plugin *p = plugin_list; p != NULL; p = p->next){ @@ -172,20 +171,18 @@ } /* Helper function for add_argument and add_environment */ -__attribute__((nonnull, warn_unused_result)) +__attribute__((nonnull)) static bool add_to_char_array(const char *new, char ***array, int *len){ /* Resize the pointed-to array to hold one more pointer */ - char **new_array = NULL; do { - new_array = realloc(*array, sizeof(char *) - * (size_t) ((*len) + 2)); - } while(new_array == NULL and errno == EINTR); + *array = realloc(*array, sizeof(char *) + * (size_t) ((*len) + 2)); + } while(*array == NULL and errno == EINTR); /* Malloc check */ - if(new_array == NULL){ + if(*array == NULL){ return false; } - *array = new_array; /* Make a copy of the new string */ char *copy; do { @@ -203,7 +200,7 @@ } /* Add to a plugin's argument vector */ -__attribute__((nonnull(2), warn_unused_result)) +__attribute__((nonnull(2))) static bool add_argument(plugin *p, const char *arg){ if(p == NULL){ return false; @@ -212,7 +209,7 @@ } /* Add to a plugin's environment */ -__attribute__((nonnull(2), warn_unused_result)) +__attribute__((nonnull(2))) static bool add_environment(plugin *p, const char *def, bool replace){ if(p == NULL){ return false; @@ -220,19 +217,19 @@ /* namelen = length of name of environment variable */ size_t namelen = (size_t)(strchrnul(def, '=') - def); /* Search for this environment variable */ - for(char **envdef = p->environ; *envdef != NULL; envdef++){ - if(strncmp(*envdef, def, namelen + 1) == 0){ + for(char **e = p->environ; *e != NULL; e++){ + if(strncmp(*e, def, namelen + 1) == 0){ /* It already exists */ if(replace){ - char *new_envdef; + char *new; do { - new_envdef = realloc(*envdef, strlen(def) + 1); - } while(new_envdef == NULL and errno == EINTR); - if(new_envdef == NULL){ + new = realloc(*e, strlen(def) + 1); + } while(new == NULL and errno == EINTR); + if(new == NULL){ return false; } - *envdef = new_envdef; - strcpy(*envdef, def); + *e = new; + strcpy(*e, def); } return true; } @@ -240,13 +237,11 @@ return add_to_char_array(def, &(p->environ), &(p->envc)); } -#ifndef O_CLOEXEC /* * Based on the example in the GNU LibC manual chapter 13.13 "File * Descriptor Flags". | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] | */ -__attribute__((warn_unused_result)) static int set_cloexec_flag(int fd){ int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0)); /* If reading the flags failed, return error indication now. */ @@ -257,7 +252,6 @@ return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD, ret | FD_CLOEXEC)); } -#endif /* not O_CLOEXEC */ /* Mark processes as completed when they exit, and save their exit @@ -295,7 +289,7 @@ } /* Prints out a password to stdout */ -__attribute__((nonnull, warn_unused_result)) +__attribute__((nonnull)) static bool print_out_password(const char *buffer, size_t length){ ssize_t ret; for(size_t written = 0; written < length; written += (size_t)ret){ @@ -347,10 +341,11 @@ int main(int argc, char *argv[]){ char *plugindir = NULL; - char *pluginhelperdir = NULL; char *argfile = NULL; FILE *conffp; - struct dirent **direntries = NULL; + size_t d_name_len; + DIR *dir = NULL; + struct dirent *dirst; struct stat st; fd_set rfds_all; int ret, maxfd = 0; @@ -364,7 +359,6 @@ .sa_flags = SA_NOCLDSTOP }; char **custom_argv = NULL; int custom_argc = 0; - int dir_fd = -1; /* Establish a signal handler */ sigemptyset(&sigchld_action.sa_mask); @@ -415,10 +409,6 @@ .doc = "Group ID the plugins will run as", .group = 3 }, { .name = "debug", .key = 132, .doc = "Debug mode", .group = 4 }, - { .name = "plugin-helper-dir", .key = 133, - .arg = "DIRECTORY", - .doc = "Specify a different plugin helper directory", - .group = 2 }, /* * These reproduce what we would get without ARGP_NO_HELP */ @@ -445,13 +435,10 @@ break; } } - errno = 0; } break; case 'G': /* --global-env */ - if(add_environment(getplugin(NULL), arg, true)){ - errno = 0; - } + add_environment(getplugin(NULL), arg, true); break; case 'o': /* --options-for */ { @@ -474,7 +461,6 @@ break; } } - errno = 0; } break; case 'E': /* --env-for */ @@ -492,9 +478,7 @@ errno = EINVAL; break; } - if(add_environment(getplugin(arg), envdef, true)){ - errno = 0; - } + add_environment(getplugin(arg), envdef, true); } break; case 'd': /* --disable */ @@ -502,7 +486,6 @@ plugin *p = getplugin(arg); if(p != NULL){ p->disabled = true; - errno = 0; } } break; @@ -511,16 +494,12 @@ plugin *p = getplugin(arg); if(p != NULL){ p->disabled = false; - errno = 0; } } break; case 128: /* --plugin-dir */ free(plugindir); plugindir = strdup(arg); - if(plugindir != NULL){ - errno = 0; - } break; case 129: /* --config-file */ /* This is already done by parse_opt_config_file() */ @@ -534,7 +513,6 @@ break; } uid = (uid_t)tmp_id; - errno = 0; break; case 131: /* --groupid */ tmp_id = strtoimax(arg, &tmp, 10); @@ -545,18 +523,10 @@ break; } gid = (gid_t)tmp_id; - errno = 0; break; case 132: /* --debug */ debug = true; break; - case 133: /* --plugin-helper-dir */ - free(pluginhelperdir); - pluginhelperdir = strdup(arg); - if(pluginhelperdir != NULL){ - errno = 0; - } - break; /* * These reproduce what we would get without ARGP_NO_HELP */ @@ -606,14 +576,10 @@ case 129: /* --config-file */ free(argfile); argfile = strdup(arg); - if(argfile != NULL){ - errno = 0; - } break; case 130: /* --userid */ case 131: /* --groupid */ case 132: /* --debug */ - case 133: /* --plugin-helper-dir */ case '?': /* --help */ case -3: /* --usage */ case 'V': /* --version */ @@ -698,18 +664,13 @@ } custom_argc += 1; - { - char **new_argv = realloc(custom_argv, sizeof(char *) - * ((size_t)custom_argc + 1)); - if(new_argv == NULL){ - error(0, errno, "realloc"); - exitstatus = EX_OSERR; - free(new_arg); - free(org_line); - goto fallback; - } else { - custom_argv = new_argv; - } + custom_argv = realloc(custom_argv, sizeof(char *) + * ((unsigned int) custom_argc + 1)); + if(custom_argv == NULL){ + error(0, errno, "realloc"); + exitstatus = EX_OSERR; + free(org_line); + goto fallback; } custom_argv[custom_argc-1] = new_arg; custom_argv[custom_argc] = NULL; @@ -773,26 +734,8 @@ goto fallback; } - { - char *pluginhelperenv; - bool bret = true; - ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s", - pluginhelperdir != NULL ? pluginhelperdir : PHDIR); - if(ret != -1){ - bret = add_environment(getplugin(NULL), pluginhelperenv, true); - } - if(ret == -1 or not bret){ - error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR" - " environment variable to \"%s\" for all plugins\n", - pluginhelperdir != NULL ? pluginhelperdir : PHDIR); - } - if(ret != -1){ - free(pluginhelperenv); - } - } - 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,12 +750,10 @@ if(getuid() == 0){ /* Work around Debian bug #633582: - */ + */ int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY); if(plugindir_fd == -1){ - if(errno != ENOENT){ - error(0, errno, "open(\"" PDIR "\")"); - } + error(0, errno, "open"); } else { ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st)); if(ret == -1){ @@ -825,7 +766,7 @@ } } } - close(plugindir_fd); + TEMP_FAILURE_RETRY(close(plugindir_fd)); } } @@ -841,13 +782,24 @@ /* Open plugin directory with close_on_exec flag */ { - dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY | -#ifdef O_CLOEXEC - O_CLOEXEC -#else /* not O_CLOEXEC */ - 0 -#endif /* not O_CLOEXEC */ - ); + int dir_fd = -1; + if(plugindir == NULL){ + dir_fd = open(PDIR, O_RDONLY | +#ifdef O_CLOEXEC + O_CLOEXEC +#else /* not O_CLOEXEC */ + 0 +#endif /* not O_CLOEXEC */ + ); + } else { + dir_fd = open(plugindir, O_RDONLY | +#ifdef O_CLOEXEC + O_CLOEXEC +#else /* not O_CLOEXEC */ + 0 +#endif /* not O_CLOEXEC */ + ); + } if(dir_fd == -1){ error(0, errno, "Could not open plugin dir"); exitstatus = EX_UNAVAILABLE; @@ -859,94 +811,129 @@ ret = set_cloexec_flag(dir_fd); if(ret < 0){ error(0, errno, "set_cloexec_flag"); + TEMP_FAILURE_RETRY(close(dir_fd)); exitstatus = EX_OSERR; goto fallback; } #endif /* O_CLOEXEC */ - } - - int good_name(const struct dirent * const dirent){ - const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new", - "*.dpkg-old", "*.dpkg-bak", - "*.dpkg-divert", NULL }; -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - for(const char **pat = (const char **)patterns; - *pat != NULL; pat++){ -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD) - != FNM_NOMATCH){ - if(debug){ - fprintf(stderr, "Ignoring plugin dir entry \"%s\"" - " matching pattern %s\n", dirent->d_name, *pat); - } - return 0; - } + + dir = fdopendir(dir_fd); + if(dir == NULL){ + error(0, errno, "Could not open plugin dir"); + TEMP_FAILURE_RETRY(close(dir_fd)); + exitstatus = EX_OSERR; + goto fallback; } - return 1; - } - - int numplugins = scandirat(dir_fd, ".", &direntries, good_name, - alphasort); - if(numplugins == -1){ - error(0, errno, "Could not scan plugin dir"); - direntries = NULL; - exitstatus = EX_OSERR; - goto fallback; } FD_ZERO(&rfds_all); /* Read and execute any executable in the plugin directory*/ - for(int i = 0; i < numplugins; i++){ - - int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY); - if(plugin_fd == -1){ - error(0, errno, "Could not open plugin"); - free(direntries[i]); + while(true){ + do { + dirst = readdir(dir); + } while(dirst == NULL and errno == EINTR); + + /* All directory entries have been processed */ + if(dirst == NULL){ + if(errno == EBADF){ + error(0, errno, "readdir"); + exitstatus = EX_IOERR; + goto fallback; + } + break; + } + + d_name_len = strlen(dirst->d_name); + + /* Ignore dotfiles, backup files and other junk */ + { + bool bad_name = false; + + const char const *bad_prefixes[] = { ".", "#", NULL }; + + const char const *bad_suffixes[] = { "~", "#", ".dpkg-new", + ".dpkg-old", + ".dpkg-bak", + ".dpkg-divert", NULL }; + for(const char **pre = bad_prefixes; *pre != NULL; pre++){ + size_t pre_len = strlen(*pre); + if((d_name_len >= pre_len) + and strncmp((dirst->d_name), *pre, pre_len) == 0){ + if(debug){ + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad prefix %s\n", dirst->d_name, *pre); + } + bad_name = true; + break; + } + } + if(bad_name){ + continue; + } + for(const char **suf = bad_suffixes; *suf != NULL; suf++){ + size_t suf_len = strlen(*suf); + if((d_name_len >= suf_len) + and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf) + == 0)){ + if(debug){ + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad suffix %s\n", dirst->d_name, *suf); + } + bad_name = true; + break; + } + } + + if(bad_name){ + continue; + } + } + + char *filename; + if(plugindir == NULL){ + ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s", + dirst->d_name)); + } else { + ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s", + plugindir, + dirst->d_name)); + } + if(ret < 0){ + error(0, errno, "asprintf"); continue; } - ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st)); + + ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st)); if(ret == -1){ error(0, errno, "stat"); - close(plugin_fd); - free(direntries[i]); + free(filename); continue; } /* Ignore non-executable files */ if(not S_ISREG(st.st_mode) - or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name, - X_OK, 0)) != 0)){ + or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){ if(debug){ - fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\"" - " with bad type or mode\n", - plugindir != NULL ? plugindir : PDIR, - direntries[i]->d_name); + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad type or mode\n", filename); } - close(plugin_fd); - free(direntries[i]); + free(filename); continue; } - plugin *p = getplugin(direntries[i]->d_name); + plugin *p = getplugin(dirst->d_name); if(p == NULL){ error(0, errno, "getplugin"); - close(plugin_fd); - free(direntries[i]); + free(filename); continue; } if(p->disabled){ if(debug){ fprintf(stderr, "Ignoring disabled plugin \"%s\"\n", - direntries[i]->d_name); + dirst->d_name); } - close(plugin_fd); - free(direntries[i]); + free(filename); continue; } { @@ -966,8 +953,9 @@ } } } - /* If this plugin has any environment variables, we need to - duplicate the environment from this process, too. */ + /* If this plugin has any environment variables, we will call + using execve and need to duplicate the environment from this + process, too. */ if(p->environ[0] != NULL){ for(char **e = environ; *e != NULL; e++){ if(not add_environment(p, *e, false)){ @@ -977,47 +965,25 @@ } int pipefd[2]; -#ifndef O_CLOEXEC ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd)); -#else /* O_CLOEXEC */ - ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC)); -#endif /* O_CLOEXEC */ if(ret == -1){ error(0, errno, "pipe"); exitstatus = EX_OSERR; - free(direntries[i]); - goto fallback; - } - if(pipefd[0] >= FD_SETSIZE){ - fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0], - FD_SETSIZE); - close(pipefd[0]); - close(pipefd[1]); - exitstatus = EX_OSERR; - free(direntries[i]); - goto fallback; - } -#ifndef O_CLOEXEC + goto fallback; + } /* Ask OS to automatic close the pipe on exec */ ret = set_cloexec_flag(pipefd[0]); if(ret < 0){ error(0, errno, "set_cloexec_flag"); - close(pipefd[0]); - close(pipefd[1]); exitstatus = EX_OSERR; - free(direntries[i]); goto fallback; } ret = set_cloexec_flag(pipefd[1]); if(ret < 0){ error(0, errno, "set_cloexec_flag"); - close(pipefd[0]); - close(pipefd[1]); exitstatus = EX_OSERR; - free(direntries[i]); goto fallback; } -#endif /* not O_CLOEXEC */ /* Block SIGCHLD until process is safely in process list */ ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, @@ -1025,7 +991,6 @@ if(ret < 0){ error(0, errno, "sigprocmask"); exitstatus = EX_OSERR; - free(direntries[i]); goto fallback; } /* Starting a new process to be watched */ @@ -1035,12 +1000,7 @@ } while(pid == -1 and errno == EINTR); if(pid == -1){ error(0, errno, "fork"); - TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK, - &sigchld_action.sa_mask, NULL)); - close(pipefd[0]); - close(pipefd[1]); exitstatus = EX_OSERR; - free(direntries[i]); goto fallback; } if(pid == 0){ @@ -1062,19 +1022,29 @@ _exit(EX_OSERR); } - if(fexecve(plugin_fd, p->argv, - (p->environ[0] != NULL) ? p->environ : environ) < 0){ - error(0, errno, "fexecve for %s/%s", - plugindir != NULL ? plugindir : PDIR, - direntries[i]->d_name); - _exit(EX_OSERR); + if(dirfd(dir) < 0){ + /* If dir has no file descriptor, we could not set FD_CLOEXEC + above and must now close it manually here. */ + closedir(dir); + } + if(p->environ[0] == NULL){ + if(execv(filename, p->argv) < 0){ + error(0, errno, "execv for %s", filename); + _exit(EX_OSERR); + } + } else { + if(execve(filename, p->argv, p->environ) < 0){ + error(0, errno, "execve for %s", filename); + _exit(EX_OSERR); + } } /* no return */ } /* Parent process */ - close(pipefd[1]); /* Close unused write end of pipe */ - close(plugin_fd); - plugin *new_plugin = getplugin(direntries[i]->d_name); + TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of + pipe */ + free(filename); + plugin *new_plugin = getplugin(dirst->d_name); if(new_plugin == NULL){ error(0, errno, "getplugin"); ret = (int)(TEMP_FAILURE_RETRY @@ -1084,10 +1054,8 @@ error(0, errno, "sigprocmask"); } exitstatus = EX_OSERR; - free(direntries[i]); goto fallback; } - free(direntries[i]); new_plugin->pid = pid; new_plugin->fd = pipefd[0]; @@ -1103,17 +1071,16 @@ goto fallback; } - FD_SET(new_plugin->fd, &rfds_all); + FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from + -Wconversion */ if(maxfd < new_plugin->fd){ maxfd = new_plugin->fd; } } - free(direntries); - direntries = NULL; - close(dir_fd); - dir_fd = -1; + TEMP_FAILURE_RETRY(closedir(dir)); + dir = NULL; free_plugin(getplugin(NULL)); for(plugin *p = plugin_list; p != NULL; p = p->next){ @@ -1158,11 +1125,15 @@ (intmax_t) (proc->pid), WTERMSIG(proc->status), strsignal(WTERMSIG(proc->status))); + } else if(WCOREDUMP(proc->status)){ + fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped" + " core\n", proc->name, (intmax_t) (proc->pid)); } } /* Remove the plugin */ - FD_CLR(proc->fd, &rfds_all); + FD_CLR(proc->fd, &rfds_all); /* Spurious warning from + -Wconversion */ /* Block signal while modifying process_list */ ret = (int)TEMP_FAILURE_RETRY(sigprocmask @@ -1208,21 +1179,22 @@ } /* This process has not completed. Does it have any output? */ - if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ + if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious + warning from + -Wconversion */ /* This process had nothing to say at this time */ proc = proc->next; continue; } /* Before reading, make the process' data buffer large enough */ if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){ - char *new_buffer = realloc(proc->buffer, proc->buffer_size - + (size_t) BUFFER_SIZE); - if(new_buffer == NULL){ + proc->buffer = realloc(proc->buffer, proc->buffer_size + + (size_t) BUFFER_SIZE); + if(proc->buffer == NULL){ error(0, errno, "malloc"); exitstatus = EX_OSERR; goto fallback; } - proc->buffer = new_buffer; proc->buffer_size += BUFFER_SIZE; } /* Read from the process */ @@ -1281,10 +1253,8 @@ free(custom_argv); } - free(direntries); - - if(dir_fd != -1){ - close(dir_fd); + if(dir != NULL){ + closedir(dir); } /* Kill the processes */ @@ -1310,7 +1280,6 @@ free_plugin_list(); free(plugindir); - free(pluginhelperdir); free(argfile); return exitstatus; === modified file 'plugin-runner.xml' --- plugin-runner.xml 2017-02-23 19:11:11 +0000 +++ plugin-runner.xml 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,7 @@ 2008 2009 - 2010 - 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -121,9 +114,6 @@ - - @@ -330,21 +320,6 @@ - - - - Specify a different plugin helper directory. The default - is /lib/mandos/plugin-helpers, which - will exist in the initial RAM disk - environment. (This will simply be passed to all plugins - via the MANDOSPLUGINHELPERDIR environment - variable. See ) - - - - - @@ -451,11 +426,7 @@ The plugin will run in the initial RAM disk environment, so care must be taken not to depend on any files or running - services not available there. Any helper executables required - by the plugin (which are not in the PATH) can - be placed in the plugin helper directory, the name of which - will be made available to the plugin via the - MANDOSPLUGINHELPERDIR environment variable. + services not available there. The plugin must exit cleanly and free all allocated resources @@ -504,9 +475,7 @@ only passes on its environment to all the plugins. The environment passed to plugins can be modified using the and - options. Also, the option - will affect the environment variable - MANDOSPLUGINHELPERDIR for the plugins. + options. @@ -545,26 +514,6 @@ - - /lib/mandos/plugins.d - - - The default plugin directory; can be changed by the - option. - - - - - /lib/mandos/plugin-helpers - - - The default plugin helper directory; can be changed by - the option. - - - @@ -575,7 +524,6 @@ The option is ignored when specified from within a configuration file. - @@ -624,16 +572,15 @@ - Read a different configuration file, run plugins from a - different directory, specify an alternate plugin helper - directory and add two options to the + Run plugins from a different directory, read a different + configuration file, and add two options to the mandos-client 8mandos plugin: -cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/x86_64-linux-gnu/mandos/plugins.d --plugin-helper-dir /usr/lib/x86_64-linux-gnu/mandos/plugin-helpers --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=seckey.txt +cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=seckey.txt === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2017-02-23 19:11:11 +0000 +++ plugins.d/askpass-fifo.c 2011-12-31 23:05:34 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -23,7 +23,7 @@ */ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ -#include /* uid_t, gid_t, ssize_t */ +#include /* ssize_t */ #include /* mkfifo(), S_IRUSR, S_IWUSR */ #include /* and */ #include /* errno, EACCES, ENOTDIR, ELOOP, @@ -44,8 +44,6 @@ #include /* strerror() */ #include /* va_list, va_start(), ... */ -uid_t uid = 65534; -gid_t gid = 65534; /* Function to use when printing errors */ __attribute__((format (gnu_printf, 3, 4))) @@ -57,7 +55,7 @@ va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); - if(ret == -1){ + if (ret == -1){ fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name); vfprintf(stderr, formatstring, ap); @@ -76,9 +74,6 @@ int ret = 0; ssize_t sret; - uid = getuid(); - gid = getgid(); - /* Create FIFO */ const char passfifo[] = "/lib/cryptsetup/passfifo"; ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); @@ -124,16 +119,6 @@ } } - /* Lower group privileges */ - if(setgid(gid) == -1){ - error_plus(0, errno, "setgid"); - } - - /* Lower user privileges */ - if(setuid(uid) == -1){ - error_plus(0, errno, "setuid"); - } - /* Read from FIFO */ char *buf = NULL; size_t buf_len = 0; === modified file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/askpass-fifo.xml 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,17 +33,12 @@ 2008 2009 - 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson + @@ -118,11 +113,6 @@ - - BUGS - - - EXAMPLE === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2017-02-23 19:11:11 +0000 +++ plugins.d/mandos-client.c 2013-10-05 19:34:40 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -32,23 +32,22 @@ /* Needed by GPGME, specifically gpgme_data_seek() */ #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE -#endif /* not _LARGEFILE_SOURCE */ +#endif #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 -#endif /* not _FILE_OFFSET_BITS */ +#endif #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ #include /* fprintf(), stderr, fwrite(), - stdout, ferror() */ + stdout, ferror(), remove() */ #include /* uint16_t, uint32_t, intptr_t */ #include /* NULL, size_t, ssize_t */ #include /* free(), EXIT_SUCCESS, srand(), strtof(), abort() */ #include /* bool, false, true */ -#include /* strcmp(), strlen(), strerror(), - asprintf(), strncpy(), strsignal() - */ +#include /* memset(), strcmp(), strlen(), + strerror(), asprintf(), strcpy() */ #include /* ioctl */ #include /* socket(), inet_pton(), sockaddr, sockaddr_in6, PF_INET6, @@ -56,18 +55,13 @@ opendir(), DIR */ #include /* open(), S_ISREG */ #include /* socket(), struct sockaddr_in6, - inet_pton(), connect(), - getnameinfo() */ -#include /* open(), unlinkat(), AT_REMOVEDIR */ + inet_pton(), connect() */ +#include /* open() */ #include /* opendir(), struct dirent, readdir() */ #include /* PRIu16, PRIdMAX, intmax_t, strtoimax() */ -#include /* perror(), errno, EINTR, EINVAL, - EAI_SYSTEM, ENETUNREACH, - EHOSTUNREACH, ECONNREFUSED, EPROTO, - EIO, ENOENT, ENXIO, ENOMEM, EISDIR, - ENOTEMPTY, +#include /* perror(), errno, program_invocation_short_name */ #include /* nanosleep(), time(), sleep() */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, @@ -78,9 +72,8 @@ */ #include /* close(), SEEK_SET, off_t, write(), getuid(), getgid(), seteuid(), - setgid(), pause(), _exit(), - unlinkat() */ -#include /* inet_pton(), htons() */ + setgid(), pause(), _exit() */ +#include /* inet_pton(), htons, inet_ntop() */ #include /* not, or, and */ #include /* struct argp_option, error_t, struct argp_state, struct argp, @@ -98,8 +91,6 @@ argz_delete(), argz_append(), argz_stringify(), argz_add(), argz_count() */ -#include /* getnameinfo(), NI_NUMERICHOST, - EAI_SYSTEM, gai_strerror() */ #ifdef __linux__ #include /* klogctl() */ @@ -147,7 +138,6 @@ static const char sys_class_net[] = "/sys/class/net"; char *connect_to = NULL; const char *hookdir = HOOKDIR; -int hookdir_fd = -1; uid_t uid = 65534; gid_t gid = 65534; @@ -190,7 +180,7 @@ perror(print_text); } -__attribute__((format (gnu_printf, 2, 3), nonnull)) +__attribute__((format (gnu_printf, 2, 3))) int fprintf_plus(FILE *stream, const char *format, ...){ va_list ap; va_start (ap, format); @@ -205,26 +195,19 @@ * bytes. "buffer_capacity" is how much is currently allocated, * "buffer_length" is how much is already used. */ -__attribute__((nonnull, warn_unused_result)) size_t incbuffer(char **buffer, size_t buffer_length, size_t buffer_capacity){ if(buffer_length + BUFFER_SIZE > buffer_capacity){ - char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE); - if(new_buf == NULL){ - int old_errno = errno; - free(*buffer); - errno = old_errno; - *buffer = NULL; + *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE); + if(buffer == NULL){ return 0; } - *buffer = new_buf; buffer_capacity += BUFFER_SIZE; } return buffer_capacity; } /* Add server to set of servers to retry periodically */ -__attribute__((nonnull, warn_unused_result)) bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index, int af, server **current_server){ int ret; @@ -239,21 +222,6 @@ .af = af }; if(new_server->ip == NULL){ perror_plus("strdup"); - free(new_server); - return false; - } - ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen)); - if(ret == -1){ - perror_plus("clock_gettime"); -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - free((char *)(new_server->ip)); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - free(new_server); return false; } /* Special case of first server */ @@ -261,31 +229,33 @@ new_server->next = new_server; new_server->prev = new_server; *current_server = new_server; + /* Place the new server last in the list */ } else { - /* Place the new server last in the list */ new_server->next = *current_server; new_server->prev = (*current_server)->prev; new_server->prev->next = new_server; (*current_server)->prev = new_server; } + ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen); + if(ret == -1){ + perror_plus("clock_gettime"); + return false; + } return true; } /* * Initialize GPGME. */ -__attribute__((nonnull, warn_unused_result)) -static bool init_gpgme(const char * const seckey, - const char * const pubkey, - const char * const tempdir, - mandos_context *mc){ +static bool init_gpgme(const char *seckey, const char *pubkey, + const char *tempdir, mandos_context *mc){ gpgme_error_t rc; gpgme_engine_info_t engine_info; /* * Helper function to insert pub and seckey to the engine keyring. */ - bool import_key(const char * const filename){ + bool import_key(const char *filename){ int ret; int fd; gpgme_data_t pgp_data; @@ -310,7 +280,7 @@ return false; } - ret = close(fd); + ret = (int)TEMP_FAILURE_RETRY(close(fd)); if(ret == -1){ perror_plus("close"); } @@ -372,7 +342,6 @@ * Decrypt OpenPGP data. * Returns -1 on error */ -__attribute__((nonnull, warn_unused_result)) static ssize_t pgp_packet_decrypt(const char *cryptotext, size_t crypto_size, char **plaintext, @@ -498,38 +467,35 @@ return plaintext_length; } -__attribute__((warn_unused_result, const)) -static const char *safe_string(const char *str){ - if(str == NULL) - return "(unknown)"; - return str; -} - -__attribute__((warn_unused_result)) -static const char *safer_gnutls_strerror(int value){ +static const char * safer_gnutls_strerror(int value){ const char *ret = gnutls_strerror(value); - return safe_string(ret); + if(ret == NULL) + ret = "(unknown)"; + return ret; } /* GnuTLS log function callback */ -__attribute__((nonnull)) static void debuggnutls(__attribute__((unused)) int level, const char* string){ fprintf_plus(stderr, "GnuTLS: %s", string); } -__attribute__((nonnull(1, 2, 4), warn_unused_result)) static int init_gnutls_global(const char *pubkeyfilename, const char *seckeyfilename, - const char *dhparamsfilename, mandos_context *mc){ int ret; - unsigned int uret; if(debug){ fprintf_plus(stderr, "Initializing GnuTLS\n"); } + ret = gnutls_global_init(); + if(ret != GNUTLS_E_SUCCESS){ + fprintf_plus(stderr, "GnuTLS global_init: %s\n", + safer_gnutls_strerror(ret)); + return -1; + } + if(debug){ /* "Use a log level over 10 to enable all debugging options." * - GnuTLS manual @@ -543,6 +509,7 @@ if(ret != GNUTLS_E_SUCCESS){ fprintf_plus(stderr, "GnuTLS memory error: %s\n", safer_gnutls_strerror(ret)); + gnutls_global_deinit(); return -1; } @@ -573,179 +540,13 @@ safer_gnutls_strerror(ret)); goto globalfail; } - /* If a Diffie-Hellman parameters file was given, try to use it */ - if(dhparamsfilename != NULL){ - gnutls_datum_t params = { .data = NULL, .size = 0 }; - do { - int dhpfile = open(dhparamsfilename, O_RDONLY); - if(dhpfile == -1){ - perror_plus("open"); - dhparamsfilename = NULL; - break; - } - size_t params_capacity = 0; - while(true){ - params_capacity = incbuffer((char **)¶ms.data, - (size_t)params.size, - (size_t)params_capacity); - if(params_capacity == 0){ - perror_plus("incbuffer"); - free(params.data); - params.data = NULL; - dhparamsfilename = NULL; - break; - } - ssize_t bytes_read = read(dhpfile, - params.data + params.size, - BUFFER_SIZE); - /* EOF */ - if(bytes_read == 0){ - break; - } - /* check bytes_read for failure */ - if(bytes_read < 0){ - perror_plus("read"); - free(params.data); - params.data = NULL; - dhparamsfilename = NULL; - break; - } - params.size += (unsigned int)bytes_read; - } - if(params.data == NULL){ - dhparamsfilename = NULL; - } - if(dhparamsfilename == NULL){ - break; - } - ret = gnutls_dh_params_import_pkcs3(mc->dh_params, ¶ms, - GNUTLS_X509_FMT_PEM); - if(ret != GNUTLS_E_SUCCESS){ - fprintf_plus(stderr, "Failed to parse DH parameters in file" - " \"%s\": %s\n", dhparamsfilename, - safer_gnutls_strerror(ret)); - dhparamsfilename = NULL; - } - free(params.data); - } while(false); - } - if(dhparamsfilename == NULL){ - if(mc->dh_bits == 0){ - /* Find out the optimal number of DH bits */ - /* Try to read the private key file */ - gnutls_datum_t buffer = { .data = NULL, .size = 0 }; - do { - int secfile = open(seckeyfilename, O_RDONLY); - if(secfile == -1){ - perror_plus("open"); - break; - } - size_t buffer_capacity = 0; - while(true){ - buffer_capacity = incbuffer((char **)&buffer.data, - (size_t)buffer.size, - (size_t)buffer_capacity); - if(buffer_capacity == 0){ - perror_plus("incbuffer"); - free(buffer.data); - buffer.data = NULL; - break; - } - ssize_t bytes_read = read(secfile, - buffer.data + buffer.size, - BUFFER_SIZE); - /* EOF */ - if(bytes_read == 0){ - break; - } - /* check bytes_read for failure */ - if(bytes_read < 0){ - perror_plus("read"); - free(buffer.data); - buffer.data = NULL; - break; - } - buffer.size += (unsigned int)bytes_read; - } - close(secfile); - } while(false); - /* If successful, use buffer to parse private key */ - gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA; - if(buffer.data != NULL){ - { - gnutls_openpgp_privkey_t privkey = NULL; - ret = gnutls_openpgp_privkey_init(&privkey); - if(ret != GNUTLS_E_SUCCESS){ - fprintf_plus(stderr, "Error initializing OpenPGP key" - " structure: %s", - safer_gnutls_strerror(ret)); - free(buffer.data); - buffer.data = NULL; - } else { - ret = gnutls_openpgp_privkey_import - (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0); - if(ret != GNUTLS_E_SUCCESS){ - fprintf_plus(stderr, "Error importing OpenPGP key : %s", - safer_gnutls_strerror(ret)); - privkey = NULL; - } - free(buffer.data); - buffer.data = NULL; - if(privkey != NULL){ - /* Use private key to suggest an appropriate - sec_param */ - sec_param = gnutls_openpgp_privkey_sec_param(privkey); - gnutls_openpgp_privkey_deinit(privkey); - if(debug){ - fprintf_plus(stderr, "This OpenPGP key implies using" - " a GnuTLS security parameter \"%s\".\n", - safe_string(gnutls_sec_param_get_name - (sec_param))); - } - } - } - } - if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){ - /* Err on the side of caution */ - sec_param = GNUTLS_SEC_PARAM_ULTRA; - if(debug){ - fprintf_plus(stderr, "Falling back to security parameter" - " \"%s\"\n", - safe_string(gnutls_sec_param_get_name - (sec_param))); - } - } - } - uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param); - if(uret != 0){ - mc->dh_bits = uret; - if(debug){ - fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter" - " implies %u DH bits; using that.\n", - safe_string(gnutls_sec_param_get_name - (sec_param)), - mc->dh_bits); - } - } else { - fprintf_plus(stderr, "Failed to get implied number of DH" - " bits for security parameter \"%s\"): %s\n", - safe_string(gnutls_sec_param_get_name - (sec_param)), - safer_gnutls_strerror(ret)); - goto globalfail; - } - } else if(debug){ - fprintf_plus(stderr, "DH bits explicitly set to %u\n", - mc->dh_bits); - } - ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits); - if(ret != GNUTLS_E_SUCCESS){ - fprintf_plus(stderr, "Error in GnuTLS prime generation (%u" - " bits): %s\n", mc->dh_bits, - safer_gnutls_strerror(ret)); - goto globalfail; - } - } + ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits); + if(ret != GNUTLS_E_SUCCESS){ + fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n", + safer_gnutls_strerror(ret)); + goto globalfail; + } + gnutls_certificate_set_dh_params(mc->cred, mc->dh_params); return 0; @@ -753,11 +554,11 @@ globalfail: gnutls_certificate_free_credentials(mc->cred); + gnutls_global_deinit(); gnutls_dh_params_deinit(mc->dh_params); return -1; } -__attribute__((nonnull, warn_unused_result)) static int init_gnutls_session(gnutls_session_t *session, mandos_context *mc){ int ret; @@ -810,6 +611,8 @@ /* ignore client certificate if any. */ gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE); + gnutls_dh_set_prime_bits(*session, mc->dh_bits); + return 0; } @@ -817,232 +620,16 @@ static void empty_log(__attribute__((unused)) AvahiLogLevel level, __attribute__((unused)) const char *txt){} -/* Set effective uid to 0, return errno */ -__attribute__((warn_unused_result)) -int raise_privileges(void){ - int old_errno = errno; - int ret = 0; - if(seteuid(0) == -1){ - ret = errno; - } - errno = old_errno; - return ret; -} - -/* Set effective and real user ID to 0. Return errno. */ -__attribute__((warn_unused_result)) -int raise_privileges_permanently(void){ - int old_errno = errno; - int ret = raise_privileges(); - if(ret != 0){ - errno = old_errno; - return ret; - } - if(setuid(0) == -1){ - ret = errno; - } - errno = old_errno; - return ret; -} - -/* Set effective user ID to unprivileged saved user ID */ -__attribute__((warn_unused_result)) -int lower_privileges(void){ - int old_errno = errno; - int ret = 0; - if(seteuid(uid) == -1){ - ret = errno; - } - errno = old_errno; - return ret; -} - -/* Lower privileges permanently */ -__attribute__((warn_unused_result)) -int lower_privileges_permanently(void){ - int old_errno = errno; - int ret = 0; - if(setuid(uid) == -1){ - ret = errno; - } - errno = old_errno; - return ret; -} - -/* Helper function to add_local_route() and delete_local_route() */ -__attribute__((nonnull, warn_unused_result)) -static bool add_delete_local_route(const bool add, - const char *address, - AvahiIfIndex if_index){ - int ret; - char helper[] = "mandos-client-iprouteadddel"; - char add_arg[] = "add"; - char delete_arg[] = "delete"; - char debug_flag[] = "--debug"; - char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR"); - if(pluginhelperdir == NULL){ - if(debug){ - fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment" - " variable not set; cannot run helper\n"); - } - return false; - } - - char interface[IF_NAMESIZE]; - if(if_indextoname((unsigned int)if_index, interface) == NULL){ - perror_plus("if_indextoname"); - return false; - } - - int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY)); - if(devnull == -1){ - perror_plus("open(\"/dev/null\", O_RDONLY)"); - return false; - } - pid_t pid = fork(); - if(pid == 0){ - /* Child */ - /* Raise privileges */ - errno = raise_privileges_permanently(); - if(errno != 0){ - perror_plus("Failed to raise privileges"); - /* _exit(EX_NOPERM); */ - } else { - /* Set group */ - errno = 0; - ret = setgid(0); - if(ret == -1){ - perror_plus("setgid"); - _exit(EX_NOPERM); - } - /* Reset supplementary groups */ - errno = 0; - ret = setgroups(0, NULL); - if(ret == -1){ - perror_plus("setgroups"); - _exit(EX_NOPERM); - } - } - ret = dup2(devnull, STDIN_FILENO); - if(ret == -1){ - perror_plus("dup2(devnull, STDIN_FILENO)"); - _exit(EX_OSERR); - } - ret = close(devnull); - if(ret == -1){ - perror_plus("close"); - _exit(EX_OSERR); - } - ret = dup2(STDERR_FILENO, STDOUT_FILENO); - if(ret == -1){ - perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)"); - _exit(EX_OSERR); - } - int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir, - O_RDONLY - | O_DIRECTORY - | O_PATH - | O_CLOEXEC)); - if(helperdir_fd == -1){ - perror_plus("open"); - _exit(EX_UNAVAILABLE); - } - int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd, - helper, O_RDONLY)); - if(helper_fd == -1){ - perror_plus("openat"); - close(helperdir_fd); - _exit(EX_UNAVAILABLE); - } - close(helperdir_fd); -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - if(fexecve(helper_fd, (char *const []) - { helper, add ? add_arg : delete_arg, (char *)address, - interface, debug ? debug_flag : NULL, NULL }, - environ) == -1){ -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - perror_plus("fexecve"); - _exit(EXIT_FAILURE); - } - } - if(pid == -1){ - perror_plus("fork"); - return false; - } - int status; - pid_t pret = -1; - errno = 0; - do { - pret = waitpid(pid, &status, 0); - if(pret == -1 and errno == EINTR and quit_now){ - int errno_raising = 0; - if((errno = raise_privileges()) != 0){ - errno_raising = errno; - perror_plus("Failed to raise privileges in order to" - " kill helper program"); - } - if(kill(pid, SIGTERM) == -1){ - perror_plus("kill"); - } - if((errno_raising == 0) and (errno = lower_privileges()) != 0){ - perror_plus("Failed to lower privileges after killing" - " helper program"); - } - return false; - } - } while(pret == -1 and errno == EINTR); - if(pret == -1){ - perror_plus("waitpid"); - return false; - } - if(WIFEXITED(status)){ - if(WEXITSTATUS(status) != 0){ - fprintf_plus(stderr, "Error: iprouteadddel exited" - " with status %d\n", WEXITSTATUS(status)); - return false; - } - return true; - } - if(WIFSIGNALED(status)){ - fprintf_plus(stderr, "Error: iprouteadddel died by" - " signal %d\n", WTERMSIG(status)); - return false; - } - fprintf_plus(stderr, "Error: iprouteadddel crashed\n"); - return false; -} - -__attribute__((nonnull, warn_unused_result)) -static bool add_local_route(const char *address, - AvahiIfIndex if_index){ - if(debug){ - fprintf_plus(stderr, "Adding route to %s\n", address); - } - return add_delete_local_route(true, address, if_index); -} - -__attribute__((nonnull, warn_unused_result)) -static bool delete_local_route(const char *address, - AvahiIfIndex if_index){ - if(debug){ - fprintf_plus(stderr, "Removing route to %s\n", address); - } - return add_delete_local_route(false, address, if_index); -} - /* Called when a Mandos server is found */ -__attribute__((nonnull, warn_unused_result)) static int start_mandos_communication(const char *ip, in_port_t port, AvahiIfIndex if_index, int af, mandos_context *mc){ int ret, tcp_sd = -1; ssize_t sret; - struct sockaddr_storage to; + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + } to; char *buffer = NULL; char *decrypted_buffer = NULL; size_t buffer_length = 0; @@ -1051,7 +638,6 @@ int retval = -1; gnutls_session_t session; int pf; /* Protocol family */ - bool route_added = false; errno = 0; @@ -1079,9 +665,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; @@ -1116,7 +701,7 @@ PRIuMAX "\n", ip, (uintmax_t)port); } - tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0); + tcp_sd = socket(pf, SOCK_STREAM, 0); if(tcp_sd < 0){ int e = errno; perror_plus("socket"); @@ -1129,14 +714,13 @@ goto mandos_end; } + memset(&to, 0, sizeof(to)); if(af == AF_INET6){ - struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to; - *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af }; - ret = inet_pton(af, ip, &to6->sin6_addr); + to.in6.sin6_family = (sa_family_t)af; + ret = inet_pton(af, ip, &to.in6.sin6_addr); } else { /* IPv4 */ - struct sockaddr_in *to4 = (struct sockaddr_in *)&to; - *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af }; - ret = inet_pton(af, ip, &to4->sin_addr); + to.in.sin_family = (sa_family_t)af; + ret = inet_pton(af, ip, &to.in.sin_addr); } if(ret < 0 ){ int e = errno; @@ -1151,9 +735,10 @@ goto mandos_end; } if(af == AF_INET6){ - ((struct sockaddr_in6 *)&to)->sin6_port = htons(port); - if(IN6_IS_ADDR_LINKLOCAL - (&((struct sockaddr_in6 *)&to)->sin6_addr)){ + to.in6.sin6_port = htons(port); + if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */ + (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and + -Wunreachable-code*/ if(if_index == AVAHI_IF_UNSPEC){ fprintf_plus(stderr, "An IPv6 link-local address is" " incomplete without a network interface\n"); @@ -1161,10 +746,12 @@ goto mandos_end; } /* Set the network interface number as scope */ - ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index; + to.in6.sin6_scope_id = (uint32_t)if_index; } } else { - ((struct sockaddr_in *)&to)->sin_port = htons(port); + to.in.sin_port = htons(port); /* Spurious warnings from + -Wconversion and + -Wunreachable-code */ } if(quit_now){ @@ -1187,86 +774,45 @@ } char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = ""; - if(af == AF_INET6){ - ret = getnameinfo((struct sockaddr *)&to, - sizeof(struct sockaddr_in6), - addrstr, sizeof(addrstr), NULL, 0, - NI_NUMERICHOST); - } else { - ret = getnameinfo((struct sockaddr *)&to, - sizeof(struct sockaddr_in), - addrstr, sizeof(addrstr), NULL, 0, - NI_NUMERICHOST); - } - if(ret == EAI_SYSTEM){ - perror_plus("getnameinfo"); - } else if(ret != 0) { - fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret)); - } else if(strcmp(addrstr, ip) != 0){ - fprintf_plus(stderr, "Canonical address form: %s\n", addrstr); - } - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - while(true){ - if(af == AF_INET6){ - ret = connect(tcp_sd, (struct sockaddr *)&to, - sizeof(struct sockaddr_in6)); - } else { - ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */ - sizeof(struct sockaddr_in)); - } - if(ret < 0){ - if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH)) - and if_index != AVAHI_IF_UNSPEC - and connect_to == NULL - and not route_added and - ((af == AF_INET6 and not - IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *) - &to)->sin6_addr))) - or (af == AF_INET and - /* Not a a IPv4LL address */ - (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr) - & 0xFFFF0000L) != 0xA9FE0000L))){ - /* Work around Avahi bug - Avahi does not announce link-local - addresses if it has a global address, so local hosts with - *only* a link-local address (e.g. Mandos clients) cannot - connect to a Mandos server announced by Avahi on a server - host with a global address. Work around this by retrying - with an explicit route added with the server's address. - - Avahi bug reference: - https://lists.freedesktop.org/archives/avahi/2010-February/001833.html - https://bugs.debian.org/587961 - */ - if(debug){ - fprintf_plus(stderr, "Mandos server unreachable, trying" - " direct route\n"); - } - int e = errno; - route_added = add_local_route(ip, if_index); - if(route_added){ - continue; - } - errno = e; - } - if(errno != ECONNREFUSED or debug){ - int e = errno; - perror_plus("connect"); - errno = e; - } - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - break; + const char *pcret; + if(af == AF_INET6){ + pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr, + sizeof(addrstr)); + } else { + pcret = inet_ntop(af, &(to.in.sin_addr), addrstr, + sizeof(addrstr)); + } + if(pcret == NULL){ + perror_plus("inet_ntop"); + } else { + if(strcmp(addrstr, ip) != 0){ + fprintf_plus(stderr, "Canonical address form: %s\n", addrstr); + } + } + } + + if(quit_now){ + errno = EINTR; + goto mandos_end; + } + + if(af == AF_INET6){ + ret = connect(tcp_sd, &to.in6, sizeof(to)); + } else { + ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */ + } + if(ret < 0){ + if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){ + int e = errno; + perror_plus("connect"); + errno = e; + } + goto mandos_end; + } + + if(quit_now){ + errno = EINTR; + goto mandos_end; } const char *out = mandos_protocol_version; @@ -1426,7 +972,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 +993,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; } } @@ -1466,17 +1001,11 @@ mandos_end: { - if(route_added){ - if(not delete_local_route(ip, if_index)){ - fprintf_plus(stderr, "Failed to delete local route to %s on" - " interface %d", ip, if_index); - } - } int e = errno; free(decrypted_buffer); free(buffer); if(tcp_sd >= 0){ - ret = close(tcp_sd); + ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd)); } if(ret == -1){ if(e == 0){ @@ -1507,7 +1036,7 @@ AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - void *mc){ + void* mc){ if(r == NULL){ return; } @@ -1516,7 +1045,6 @@ timed out */ if(quit_now){ - avahi_s_service_resolver_free(r); return; } @@ -1567,7 +1095,7 @@ const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - void *mc){ + void* mc){ if(b == NULL){ return; } @@ -1633,33 +1161,30 @@ errno = old_errno; } -__attribute__((nonnull, warn_unused_result)) bool get_flags(const char *ifname, struct ifreq *ifr){ int ret; - int old_errno; + error_t ret_errno; int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); if(s < 0){ - old_errno = errno; + ret_errno = errno; perror_plus("socket"); - errno = old_errno; + errno = ret_errno; return false; } - strncpy(ifr->ifr_name, ifname, IF_NAMESIZE); - ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */ + strcpy(ifr->ifr_name, ifname); ret = ioctl(s, SIOCGIFFLAGS, ifr); if(ret == -1){ if(debug){ - old_errno = errno; + ret_errno = errno; perror_plus("ioctl SIOCGIFFLAGS"); - errno = old_errno; + errno = ret_errno; } return false; } return true; } -__attribute__((nonnull, warn_unused_result)) bool good_flags(const char *ifname, const struct ifreq *ifr){ /* Reject the loopback device */ @@ -1707,7 +1232,6 @@ * corresponds to an acceptable network device. * (This function is passed to scandir(3) as a filter function.) */ -__attribute__((nonnull, warn_unused_result)) int good_interface(const struct dirent *if_entry){ if(if_entry->d_name[0] == '.'){ return 0; @@ -1731,7 +1255,6 @@ /* * This function determines if a network interface is up. */ -__attribute__((nonnull, warn_unused_result)) bool interface_is_up(const char *interface){ struct ifreq ifr; if(not get_flags(interface, &ifr)){ @@ -1748,7 +1271,6 @@ /* * This function determines if a network interface is running */ -__attribute__((nonnull, warn_unused_result)) bool interface_is_running(const char *interface){ struct ifreq ifr; if(not get_flags(interface, &ifr)){ @@ -1762,7 +1284,6 @@ return (bool)(ifr.ifr_flags & IFF_RUNNING); } -__attribute__((nonnull, pure, warn_unused_result)) int notdotentries(const struct dirent *direntry){ /* Skip "." and ".." */ if(direntry->d_name[0] == '.' @@ -1775,7 +1296,6 @@ } /* Is this directory entry a runnable program? */ -__attribute__((nonnull, warn_unused_result)) int runnable_hook(const struct dirent *direntry){ int ret; size_t sret; @@ -1789,7 +1309,7 @@ sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" - "_.-"); + "_-"); if((direntry->d_name)[sret] != '\0'){ /* Contains non-allowed characters */ if(debug){ @@ -1799,7 +1319,14 @@ return 0; } - ret = fstatat(hookdir_fd, direntry->d_name, &st, 0); + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name); + if(ret < 0){ + perror_plus("asprintf"); + return 0; + } + + ret = stat(fullname, &st); if(ret == -1){ if(debug){ perror_plus("Could not stat hook"); @@ -1829,7 +1356,6 @@ return 1; } -__attribute__((nonnull, warn_unused_result)) int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval, mandos_context *mc){ int ret; @@ -1839,13 +1365,13 @@ while(true){ if(mc->current_server == NULL){ - if(debug){ + if (debug){ fprintf_plus(stderr, "Wait until first server is found." " No timeout!\n"); } ret = avahi_simple_poll_iterate(s, -1); } else { - if(debug){ + if (debug){ fprintf_plus(stderr, "Check current_server if we should run" " it, or wait\n"); } @@ -1868,7 +1394,7 @@ - ((intmax_t)waited_time.tv_sec * 1000)) - ((intmax_t)waited_time.tv_nsec / 1000000)); - if(debug){ + if (debug){ fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n", block_time); } @@ -1896,198 +1422,204 @@ ret = avahi_simple_poll_iterate(s, (int)block_time); } if(ret != 0){ - if(ret > 0 or errno != EINTR){ + if (ret > 0 or errno != EINTR){ return (ret != 1) ? ret : 0; } } } } -__attribute__((nonnull)) -void run_network_hooks(const char *mode, const char *interface, +/* Set effective uid to 0, return errno */ +error_t raise_privileges(void){ + error_t old_errno = errno; + error_t ret_errno = 0; + if(seteuid(0) == -1){ + ret_errno = errno; + perror_plus("seteuid"); + } + errno = old_errno; + return ret_errno; +} + +/* Set effective and real user ID to 0. Return errno. */ +error_t raise_privileges_permanently(void){ + error_t old_errno = errno; + error_t ret_errno = raise_privileges(); + if(ret_errno != 0){ + errno = old_errno; + return ret_errno; + } + if(setuid(0) == -1){ + ret_errno = errno; + perror_plus("seteuid"); + } + errno = old_errno; + return ret_errno; +} + +/* Set effective user ID to unprivileged saved user ID */ +error_t lower_privileges(void){ + error_t old_errno = errno; + error_t ret_errno = 0; + if(seteuid(uid) == -1){ + ret_errno = errno; + perror_plus("seteuid"); + } + errno = old_errno; + return ret_errno; +} + +/* Lower privileges permanently */ +error_t lower_privileges_permanently(void){ + error_t old_errno = errno; + error_t ret_errno = 0; + if(setuid(uid) == -1){ + ret_errno = errno; + perror_plus("setuid"); + } + errno = old_errno; + return ret_errno; +} + +bool run_network_hooks(const char *mode, const char *interface, const float delay){ - struct dirent **direntries = NULL; - if(hookdir_fd == -1){ - hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH - | O_CLOEXEC); - if(hookdir_fd == -1){ - if(errno == ENOENT){ - if(debug){ - fprintf_plus(stderr, "Network hook directory \"%s\" not" - " found\n", hookdir); - } - } else { - perror_plus("open"); - } - return; - } - } - int numhooks = scandirat(hookdir_fd, ".", &direntries, - runnable_hook, alphasort); + struct dirent **direntries; + struct dirent *direntry; + int ret; + int numhooks = scandir(hookdir, &direntries, runnable_hook, + alphasort); if(numhooks == -1){ - perror_plus("scandir"); - return; - } - struct dirent *direntry; - int ret; - int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY)); - if(devnull == -1){ - perror_plus("open(\"/dev/null\", O_RDONLY)"); - return; - } - for(int i = 0; i < numhooks; i++){ - direntry = direntries[i]; - if(debug){ - fprintf_plus(stderr, "Running network hook \"%s\"\n", - direntry->d_name); + if(errno == ENOENT){ + if(debug){ + fprintf_plus(stderr, "Network hook directory \"%s\" not" + " found\n", hookdir); + } + } else { + perror_plus("scandir"); } - pid_t hook_pid = fork(); - if(hook_pid == 0){ - /* Child */ - /* Raise privileges */ - errno = raise_privileges_permanently(); - if(errno != 0){ - perror_plus("Failed to raise privileges"); - _exit(EX_NOPERM); - } - /* Set group */ - errno = 0; - ret = setgid(0); - if(ret == -1){ - perror_plus("setgid"); - _exit(EX_NOPERM); - } - /* Reset supplementary groups */ - errno = 0; - ret = setgroups(0, NULL); - if(ret == -1){ - perror_plus("setgroups"); - _exit(EX_NOPERM); - } - ret = setenv("MANDOSNETHOOKDIR", hookdir, 1); - if(ret == -1){ - perror_plus("setenv"); - _exit(EX_OSERR); - } - ret = setenv("DEVICE", interface, 1); - if(ret == -1){ - perror_plus("setenv"); - _exit(EX_OSERR); - } - ret = setenv("VERBOSITY", debug ? "1" : "0", 1); - if(ret == -1){ - perror_plus("setenv"); - _exit(EX_OSERR); - } - ret = setenv("MODE", mode, 1); - if(ret == -1){ - perror_plus("setenv"); - _exit(EX_OSERR); - } - char *delaystring; - ret = asprintf(&delaystring, "%f", (double)delay); - if(ret == -1){ + } else { + int devnull = open("/dev/null", O_RDONLY); + for(int i = 0; i < numhooks; i++){ + direntry = direntries[i]; + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name); + if(ret < 0){ perror_plus("asprintf"); - _exit(EX_OSERR); - } - ret = setenv("DELAY", delaystring, 1); - if(ret == -1){ + continue; + } + if(debug){ + fprintf_plus(stderr, "Running network hook \"%s\"\n", + direntry->d_name); + } + pid_t hook_pid = fork(); + if(hook_pid == 0){ + /* Child */ + /* Raise privileges */ + raise_privileges_permanently(); + /* Set group */ + errno = 0; + ret = setgid(0); + if(ret == -1){ + perror_plus("setgid"); + } + /* Reset supplementary groups */ + errno = 0; + ret = setgroups(0, NULL); + if(ret == -1){ + perror_plus("setgroups"); + } + dup2(devnull, STDIN_FILENO); + close(devnull); + dup2(STDERR_FILENO, STDOUT_FILENO); + ret = setenv("MANDOSNETHOOKDIR", hookdir, 1); + if(ret == -1){ + perror_plus("setenv"); + _exit(EX_OSERR); + } + ret = setenv("DEVICE", interface, 1); + if(ret == -1){ + perror_plus("setenv"); + _exit(EX_OSERR); + } + ret = setenv("VERBOSITY", debug ? "1" : "0", 1); + if(ret == -1){ + perror_plus("setenv"); + _exit(EX_OSERR); + } + ret = setenv("MODE", mode, 1); + if(ret == -1){ + perror_plus("setenv"); + _exit(EX_OSERR); + } + char *delaystring; + ret = asprintf(&delaystring, "%f", delay); + if(ret == -1){ + perror_plus("asprintf"); + _exit(EX_OSERR); + } + ret = setenv("DELAY", delaystring, 1); + if(ret == -1){ + free(delaystring); + perror_plus("setenv"); + _exit(EX_OSERR); + } free(delaystring); - perror_plus("setenv"); - _exit(EX_OSERR); - } - free(delaystring); - if(connect_to != NULL){ - ret = setenv("CONNECT", connect_to, 1); - if(ret == -1){ - perror_plus("setenv"); - _exit(EX_OSERR); - } - } - int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd, - direntry->d_name, - O_RDONLY)); - if(hook_fd == -1){ - perror_plus("openat"); - _exit(EXIT_FAILURE); - } - if(close(hookdir_fd) == -1){ - perror_plus("close"); - _exit(EXIT_FAILURE); - } - ret = dup2(devnull, STDIN_FILENO); - if(ret == -1){ - perror_plus("dup2(devnull, STDIN_FILENO)"); - _exit(EX_OSERR); - } - ret = close(devnull); - if(ret == -1){ - perror_plus("close"); - _exit(EX_OSERR); - } - ret = dup2(STDERR_FILENO, STDOUT_FILENO); - if(ret == -1){ - perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)"); - _exit(EX_OSERR); - } - if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL }, - environ) == -1){ - perror_plus("fexecve"); - _exit(EXIT_FAILURE); - } - } else { - if(hook_pid == -1){ - perror_plus("fork"); - free(direntry); - continue; - } - int status; - if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){ - perror_plus("waitpid"); - free(direntry); - continue; - } - if(WIFEXITED(status)){ - if(WEXITSTATUS(status) != 0){ - fprintf_plus(stderr, "Warning: network hook \"%s\" exited" - " with status %d\n", direntry->d_name, - WEXITSTATUS(status)); - free(direntry); - continue; - } - } else if(WIFSIGNALED(status)){ - fprintf_plus(stderr, "Warning: network hook \"%s\" died by" - " signal %d\n", direntry->d_name, - WTERMSIG(status)); - free(direntry); - continue; + if(connect_to != NULL){ + ret = setenv("CONNECT", connect_to, 1); + if(ret == -1){ + perror_plus("setenv"); + _exit(EX_OSERR); + } + } + if(execl(fullname, direntry->d_name, mode, NULL) == -1){ + perror_plus("execl"); + _exit(EXIT_FAILURE); + } } else { - fprintf_plus(stderr, "Warning: network hook \"%s\"" - " crashed\n", direntry->d_name); - free(direntry); - continue; - } - } - if(debug){ - fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n", - direntry->d_name); - } - free(direntry); - } - free(direntries); - if(close(hookdir_fd) == -1){ - perror_plus("close"); - } else { - hookdir_fd = -1; - } - close(devnull); + int status; + if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){ + perror_plus("waitpid"); + free(fullname); + continue; + } + if(WIFEXITED(status)){ + if(WEXITSTATUS(status) != 0){ + fprintf_plus(stderr, "Warning: network hook \"%s\" exited" + " with status %d\n", direntry->d_name, + WEXITSTATUS(status)); + free(fullname); + continue; + } + } else if(WIFSIGNALED(status)){ + fprintf_plus(stderr, "Warning: network hook \"%s\" died by" + " signal %d\n", direntry->d_name, + WTERMSIG(status)); + free(fullname); + continue; + } else { + fprintf_plus(stderr, "Warning: network hook \"%s\"" + " crashed\n", direntry->d_name); + free(fullname); + continue; + } + } + free(fullname); + if(debug){ + fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n", + direntry->d_name); + } + } + close(devnull); + } + return true; } -__attribute__((nonnull, warn_unused_result)) -int bring_up_interface(const char *const interface, - const float delay){ - int old_errno = errno; - int ret; +error_t bring_up_interface(const char *const interface, + const float delay){ + int sd = -1; + error_t old_errno = errno; + error_t ret_errno = 0; + int ret, ret_setflags; struct ifreq network; unsigned int if_index = if_nametoindex(interface); if(if_index == 0){ @@ -2102,30 +1634,24 @@ } if(not interface_is_up(interface)){ - int ret_errno = 0; - int ioctl_errno = 0; - if(not get_flags(interface, &network)){ + if(not get_flags(interface, &network) and debug){ ret_errno = errno; fprintf_plus(stderr, "Failed to get flags for interface " "\"%s\"\n", interface); - errno = old_errno; return ret_errno; } - network.ifr_flags |= IFF_UP; /* set flag */ + network.ifr_flags |= IFF_UP; - int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if(sd == -1){ + sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if(sd < 0){ ret_errno = errno; perror_plus("socket"); errno = old_errno; return ret_errno; } - + if(quit_now){ - ret = close(sd); - if(ret == -1){ - perror_plus("close"); - } + close(sd); errno = old_errno; return EINTR; } @@ -2135,29 +1661,21 @@ interface); } - /* Raise privileges */ - ret_errno = raise_privileges(); - if(ret_errno != 0){ - errno = ret_errno; - perror_plus("Failed to raise privileges"); - } + /* Raise priviliges */ + raise_privileges(); #ifdef __linux__ - int ret_linux; - bool restore_loglevel = false; - if(ret_errno == 0){ - /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO - messages about the network interface to mess up the prompt */ - ret_linux = klogctl(8, NULL, 5); - if(ret_linux == -1){ - perror_plus("klogctl"); - } else { - restore_loglevel = true; - } + /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO + messages about the network interface to mess up the prompt */ + int ret_linux = klogctl(8, NULL, 5); + bool restore_loglevel = true; + if(ret_linux == -1){ + restore_loglevel = false; + perror_plus("klogctl"); } #endif /* __linux__ */ - int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network); - ioctl_errno = errno; + ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network); + ret_errno = errno; #ifdef __linux__ if(restore_loglevel){ ret_linux = klogctl(7, NULL, 0); @@ -2167,27 +1685,20 @@ } #endif /* __linux__ */ - /* If raise_privileges() succeeded above */ - if(ret_errno == 0){ - /* Lower privileges */ - ret_errno = lower_privileges(); - if(ret_errno != 0){ - errno = ret_errno; - perror_plus("Failed to lower privileges"); - } - } + /* Lower privileges */ + lower_privileges(); /* Close the socket */ - ret = close(sd); + ret = (int)TEMP_FAILURE_RETRY(close(sd)); if(ret == -1){ perror_plus("close"); } if(ret_setflags == -1){ - errno = ioctl_errno; + errno = ret_errno; perror_plus("ioctl SIOCSIFFLAGS +IFF_UP"); errno = old_errno; - return ioctl_errno; + return ret_errno; } } else if(debug){ fprintf_plus(stderr, "Interface \"%s\" is already up; good\n", @@ -2196,7 +1707,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; } @@ -2211,9 +1722,11 @@ return 0; } -__attribute__((nonnull, warn_unused_result)) -int take_down_interface(const char *const interface){ - int old_errno = errno; +error_t take_down_interface(const char *const interface){ + int sd = -1; + error_t old_errno = errno; + error_t ret_errno = 0; + int ret, ret_setflags; struct ifreq network; unsigned int if_index = if_nametoindex(interface); if(if_index == 0){ @@ -2222,19 +1735,16 @@ return ENXIO; } if(interface_is_up(interface)){ - int ret_errno = 0; - int ioctl_errno = 0; if(not get_flags(interface, &network) and debug){ ret_errno = errno; fprintf_plus(stderr, "Failed to get flags for interface " "\"%s\"\n", interface); - errno = old_errno; return ret_errno; } network.ifr_flags &= ~(short)IFF_UP; /* clear flag */ - int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if(sd == -1){ + sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if(sd < 0){ ret_errno = errno; perror_plus("socket"); errno = old_errno; @@ -2246,37 +1756,26 @@ interface); } - /* Raise privileges */ - ret_errno = raise_privileges(); - if(ret_errno != 0){ - errno = ret_errno; - perror_plus("Failed to raise privileges"); - } - - int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network); - ioctl_errno = errno; - - /* If raise_privileges() succeeded above */ - if(ret_errno == 0){ - /* Lower privileges */ - ret_errno = lower_privileges(); - if(ret_errno != 0){ - errno = ret_errno; - perror_plus("Failed to lower privileges"); - } - } + /* Raise priviliges */ + raise_privileges(); + + ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network); + ret_errno = errno; + + /* Lower privileges */ + lower_privileges(); /* Close the socket */ - int ret = close(sd); + ret = (int)TEMP_FAILURE_RETRY(close(sd)); if(ret == -1){ perror_plus("close"); } if(ret_setflags == -1){ - errno = ioctl_errno; + errno = ret_errno; perror_plus("ioctl SIOCSIFFLAGS -IFF_UP"); errno = old_errno; - return ioctl_errno; + return ret_errno; } } else if(debug){ fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n", @@ -2288,11 +1787,10 @@ } int main(int argc, char *argv[]){ - mandos_context mc = { .server = NULL, .dh_bits = 0, - .priority = "SECURE256:!CTYPE-X.509" - ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256", - .current_server = NULL, .interfaces = NULL, - .interfaces_size = 0 }; + mandos_context mc = { .server = NULL, .dh_bits = 1024, + .priority = "SECURE256:!CTYPE-X.509:" + "+CTYPE-OPENPGP", .current_server = NULL, + .interfaces = NULL, .interfaces_size = 0 }; AvahiSServiceBrowser *sb = NULL; error_t ret_errno; int ret; @@ -2301,14 +1799,13 @@ int exitcode = EXIT_SUCCESS; char *interfaces_to_take_down = NULL; size_t interfaces_to_take_down_size = 0; - char run_tempdir[] = "/run/tmp/mandosXXXXXX"; - char old_tempdir[] = "/tmp/mandosXXXXXX"; - char *tempdir = NULL; + char tempdir[] = "/tmp/mandosXXXXXX"; + bool tempdir_created = false; AvahiIfIndex if_index = AVAHI_IF_UNSPEC; const char *seckey = PATHDIR "/" SECKEY; const char *pubkey = PATHDIR "/" PUBKEY; - const char *dh_params_file = NULL; char *interfaces_hooks = NULL; + size_t interfaces_hooks_size = 0; bool gnutls_initialized = false; bool gpgme_initialized = false; @@ -2366,11 +1863,6 @@ .doc = "Bit length of the prime number used in the" " Diffie-Hellman key exchange", .group = 2 }, - { .name = "dh-params", .key = 134, - .arg = "FILE", - .doc = "PEM-encoded PKCS#3 file with pre-generated parameters" - " for the Diffie-Hellman key exchange", - .group = 2 }, { .name = "priority", .key = 130, .arg = "STRING", .doc = "GnuTLS priority string for the TLS handshake", @@ -2431,9 +1923,6 @@ } mc.dh_bits = (typeof(mc.dh_bits))tmpmax; break; - case 134: /* --dh-params */ - dh_params_file = arg; - break; case 130: /* --priority */ mc.priority = arg; break; @@ -2479,14 +1968,14 @@ .args_doc = "", .doc = "Mandos client -- Get and decrypt" " passwords from a Mandos server" }; - ret_errno = argp_parse(&argp, argc, argv, - ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL); - switch(ret_errno){ + ret = argp_parse(&argp, argc, argv, + ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL); + switch(ret){ case 0: break; case ENOMEM: default: - errno = ret_errno; + errno = ret; perror_plus("argp_parse"); exitcode = EX_OSERR; goto end; @@ -2495,17 +1984,13 @@ goto end; } } - + { /* Work around Debian bug #633582: - */ + */ - /* Re-raise privileges */ - ret = raise_privileges(); - if(ret != 0){ - errno = ret; - perror_plus("Failed to raise privileges"); - } else { + /* Re-raise priviliges */ + if(raise_privileges() == 0){ struct stat st; if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){ @@ -2525,10 +2010,10 @@ } } } - close(seckey_fd); + TEMP_FAILURE_RETRY(close(seckey_fd)); } } - + if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){ int pubkey_fd = open(pubkey, O_RDONLY); if(pubkey_fd == -1){ @@ -2546,38 +2031,12 @@ } } } - close(pubkey_fd); - } - } - - if(dh_params_file != NULL - and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){ - int dhparams_fd = open(dh_params_file, O_RDONLY); - if(dhparams_fd == -1){ - perror_plus("open"); - } else { - ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st)); - if(ret == -1){ - perror_plus("fstat"); - } else { - if(S_ISREG(st.st_mode) - and st.st_uid == 0 and st.st_gid == 0){ - ret = fchown(dhparams_fd, uid, gid); - if(ret == -1){ - perror_plus("fchown"); - } - } - } - close(dhparams_fd); - } - } - + TEMP_FAILURE_RETRY(close(pubkey_fd)); + } + } + /* Lower privileges */ - ret = lower_privileges(); - if(ret != 0){ - errno = ret; - perror_plus("Failed to lower privileges"); - } + lower_privileges(); } } @@ -2607,10 +2066,14 @@ goto end; } memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size); - argz_stringify(interfaces_hooks, mc.interfaces_size, (int)','); - } - run_network_hooks("start", interfaces_hooks != NULL ? - interfaces_hooks : "", delay); + interfaces_hooks_size = mc.interfaces_size; + argz_stringify(interfaces_hooks, interfaces_hooks_size, + (int)','); + } + if(not run_network_hooks("start", interfaces_hooks != NULL ? + interfaces_hooks : "", delay)){ + goto end; + } } if(not debug){ @@ -2694,7 +2157,7 @@ /* If no interfaces were specified, make a list */ if(mc.interfaces == NULL){ - struct dirent **direntries = NULL; + struct dirent **direntries; /* Look for any good interfaces */ ret = scandir(sys_class_net, &direntries, good_interface, alphasort); @@ -2704,22 +2167,17 @@ ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size, direntries[i]->d_name); if(ret_errno != 0){ - errno = ret_errno; perror_plus("argz_add"); - free(direntries[i]); continue; } if(debug){ fprintf_plus(stderr, "Will use interface \"%s\"\n", direntries[i]->d_name); } - free(direntries[i]); } free(direntries); } else { - if(ret == 0){ - free(direntries); - } + free(direntries); fprintf_plus(stderr, "Could not find a network interface\n"); exitcode = EXIT_FAILURE; goto end; @@ -2748,18 +2206,15 @@ break; } bool interface_was_up = interface_is_up(interface); - errno = bring_up_interface(interface, delay); + ret = bring_up_interface(interface, delay); if(not interface_was_up){ - if(errno != 0){ - fprintf_plus(stderr, "Failed to bring up interface \"%s\":" - " %s\n", interface, strerror(errno)); + if(ret != 0){ + errno = ret; + perror_plus("Failed to bring up interface"); } else { - errno = argz_add(&interfaces_to_take_down, - &interfaces_to_take_down_size, - interface); - if(errno != 0){ - perror_plus("argz_add"); - } + ret_errno = argz_add(&interfaces_to_take_down, + &interfaces_to_take_down_size, + interface); } } } @@ -2781,7 +2236,7 @@ goto end; } - ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc); + ret = init_gnutls_global(pubkey, seckey, &mc); if(ret == -1){ fprintf_plus(stderr, "init_gnutls_global failed\n"); exitcode = EX_UNAVAILABLE; @@ -2794,19 +2249,11 @@ goto end; } - /* Try /run/tmp before /tmp */ - tempdir = mkdtemp(run_tempdir); - if(tempdir == NULL and errno == ENOENT){ - if(debug){ - fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n", - run_tempdir, old_tempdir); - } - tempdir = mkdtemp(old_tempdir); - } - if(tempdir == NULL){ + if(mkdtemp(tempdir) == NULL){ perror_plus("mkdtemp"); goto end; } + tempdir_created = true; if(quit_now){ goto end; @@ -2887,7 +2334,7 @@ sleep((unsigned int)retry_interval); } - if(not quit_now){ + if (not quit_now){ exitcode = EXIT_SUCCESS; } @@ -2909,7 +2356,7 @@ /* Allocate a new server */ mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll), - &config, NULL, NULL, &ret); + &config, NULL, NULL, &ret_errno); /* Free the Avahi configuration data */ avahi_server_config_free(&config); @@ -2918,7 +2365,7 @@ /* Check if creating the Avahi server object succeeded */ if(mc.server == NULL){ fprintf_plus(stderr, "Failed to create Avahi server: %s\n", - avahi_strerror(ret)); + avahi_strerror(ret_errno)); exitcode = EX_UNAVAILABLE; goto end; } @@ -2948,7 +2395,7 @@ if(debug){ fprintf_plus(stderr, "Starting Avahi loop search\n"); } - + ret = avahi_loop_with_timeout(simple_poll, (int)(retry_interval * 1000), &mc); if(debug){ @@ -2959,13 +2406,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 */ @@ -2982,6 +2423,7 @@ if(gnutls_initialized){ gnutls_certificate_free_credentials(mc.cred); + gnutls_global_deinit(); gnutls_dh_params_deinit(mc.dh_params); } @@ -2995,119 +2437,77 @@ mc.current_server->prev->next = NULL; while(mc.current_server != NULL){ server *next = mc.current_server->next; -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - free((char *)(mc.current_server->ip)); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif free(mc.current_server); mc.current_server = next; } } - /* Re-raise privileges */ + /* Re-raise priviliges */ { - ret = raise_privileges(); - if(ret != 0){ - errno = ret; - perror_plus("Failed to raise privileges"); - } else { - - /* Run network hooks */ - run_network_hooks("stop", interfaces_hooks != NULL ? - interfaces_hooks : "", delay); - - /* 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))){ - ret = take_down_interface(interface); - if(ret != 0){ - errno = ret; - perror_plus("Failed to take down interface"); - } - } - if(debug and (interfaces_to_take_down == NULL)){ - fprintf_plus(stderr, "No interfaces needed to be taken" - " down\n"); - } - } - } - - ret = lower_privileges_permanently(); - if(ret != 0){ - errno = ret; - perror_plus("Failed to lower privileges permanently"); - } + raise_privileges(); + + /* Run network hooks */ + run_network_hooks("stop", interfaces_hooks != NULL ? + interfaces_hooks : "", delay); + + /* 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))){ + ret_errno = take_down_interface(interface); + if(ret_errno != 0){ + errno = ret_errno; + perror_plus("Failed to take down interface"); + } + } + if(debug and (interfaces_to_take_down == NULL)){ + fprintf_plus(stderr, "No interfaces needed to be taken" + " down\n"); + } + } + + lower_privileges_permanently(); } free(interfaces_to_take_down); free(interfaces_hooks); - void clean_dir_at(int base, const char * const dirname, - uintmax_t level){ + /* Removes the GPGME temp directory and all files inside */ + if(tempdir_created){ struct dirent **direntries = NULL; - int dret; - int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname, - O_RDONLY - | O_NOFOLLOW - | O_DIRECTORY - | O_PATH)); - if(dir_fd == -1){ - perror_plus("open"); - } - int numentries = scandirat(dir_fd, ".", &direntries, - notdotentries, alphasort); - if(numentries >= 0){ + struct dirent *direntry = NULL; + int numentries = scandir(tempdir, &direntries, notdotentries, + alphasort); + if (numentries > 0){ for(int i = 0; i < numentries; i++){ - if(debug){ - fprintf_plus(stderr, "Unlinking \"%s/%s\"\n", - dirname, direntries[i]->d_name); - } - dret = unlinkat(dir_fd, direntries[i]->d_name, 0); - if(dret == -1){ - if(errno == EISDIR){ - dret = unlinkat(dir_fd, direntries[i]->d_name, - AT_REMOVEDIR); - } - if((dret == -1) and (errno == ENOTEMPTY) - and (strcmp(direntries[i]->d_name, "private-keys-v1.d") - == 0) and (level == 0)){ - /* Recurse only in this special case */ - clean_dir_at(dir_fd, direntries[i]->d_name, level+1); - dret = 0; - } - if(dret == -1){ - fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname, - direntries[i]->d_name, strerror(errno)); - } - } - free(direntries[i]); - } - - /* need to clean even if 0 because man page doesn't specify */ - free(direntries); - if(numentries == -1){ - perror_plus("scandirat"); - } - dret = unlinkat(base, dirname, AT_REMOVEDIR); - if(dret == -1 and errno != ENOENT){ - perror_plus("rmdir"); - } - } else { - perror_plus("scandirat"); - } - close(dir_fd); - } - - /* Removes the GPGME temp directory and all files inside */ - if(tempdir != NULL){ - clean_dir_at(-1, tempdir, 0); + direntry = direntries[i]; + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", tempdir, + direntry->d_name); + if(ret < 0){ + perror_plus("asprintf"); + continue; + } + ret = remove(fullname); + if(ret == -1){ + fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname, + strerror(errno)); + } + free(fullname); + } + } + + /* need to clean even if 0 because man page doesn't specify */ + free(direntries); + if (numentries == -1){ + perror_plus("scandir"); + } + ret = rmdir(tempdir); + if(ret == -1 and errno != ENOENT){ + perror_plus("rmdir"); + } } if(quit_now){ === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/mandos-client.xml 2013-06-23 15:13:06 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,7 @@ 2008 2009 - 2010 - 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -103,10 +96,6 @@ - - - - @@ -229,9 +218,8 @@ assumed to separate the address from the port number. - Normally, Zeroconf would be used to locate Mandos servers, - in which case this option would only be used when testing - and debugging. + This option is normally only useful for testing and + debugging. @@ -270,9 +258,10 @@ NAME can be the string none; this will make - &COMMANDNAME; only bring up interfaces - specified before this string. This - is not recommended, and only meant for advanced users. + &COMMANDNAME; not bring up + any interfaces specified + after this string. This is not + recommended, and only meant for advanced users. @@ -320,27 +309,7 @@ Sets the number of bits to use for the prime number in the - TLS Diffie-Hellman key exchange. The default value is - selected automatically based on the OpenPGP key. Note - that if the option is used, - the values from that file will be used instead. - - - - - - - - - Specifies a PEM-encoded PKCS#3 file to read the parameters - needed by the TLS Diffie-Hellman key exchange from. If - this option is not given, or if the file for some reason - could not be used, the parameters will be generated on - startup, which will take some time and processing power. - Those using servers running under time, power or processor - constraints may want to generate such a file in advance - and use this option. + TLS Diffie-Hellman key exchange. Default is 1024. @@ -473,22 +442,9 @@ ENVIRONMENT - - - MANDOSPLUGINHELPERDIR - - - This environment variable will be assumed to contain the - directory containing any helper executables. The use and - nature of these helper executables, if any, is - purposefully not documented. - - - - - This program does not use any other environment variables, not - even the ones provided by cryptsetup8 . @@ -694,10 +650,11 @@ - - BUGS - - + + + + + EXAMPLE @@ -789,9 +746,8 @@ It will also help if the checker program on the server is configured to request something from the client which can not be - spoofed by someone else on the network, like SSH server key - fingerprints, and unlike unencrypted ICMP - echo (ping) replies. + spoofed by someone else on the network, unlike unencrypted + ICMP echo (ping) replies. Note: This makes it completely insecure to @@ -843,7 +799,8 @@ - GnuTLS + GnuTLS @@ -855,7 +812,7 @@ - GPGME @@ -899,12 +856,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 +878,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 2013-10-05 19:34:40 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -72,6 +72,16 @@ /* Needed for conflict resolution */ const char plymouth_name[] = "plymouthd"; +__attribute__((format (gnu_printf, 2, 3), nonnull(1))) +int fprintf_plus(FILE *stream, const char *format, ...){ + va_list ap; + va_start (ap, format); + + TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ", + program_invocation_short_name)); + return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap)); +} + /* Function to use when printing errors */ __attribute__((format (gnu_printf, 3, 4))) void error_plus(int status, int errnum, const char *formatstring, @@ -82,7 +92,7 @@ va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); - if(ret == -1){ + if (ret == -1){ fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name); vfprintf(stderr, formatstring, ap); @@ -212,15 +222,9 @@ struct dirent **direntries = NULL; int ret; ret = scandir("/proc", &direntries, is_plymouth, alphasort); - if(ret == -1){ + if (ret == -1){ error_plus(1, errno, "scandir"); } - { - int i = ret; - while(i--){ - free(direntries[i]); - } - } free(direntries); return ret > 0; } @@ -309,7 +313,7 @@ fprintf(stderr, "Starting %s\n", argv[0]); } - if(conflict_detection()){ + if (conflict_detection()){ if(debug){ fprintf(stderr, "Stopping %s because of conflict\n", argv[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 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,8 @@ 2008 2009 - 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -227,7 +221,9 @@ BUGS - + + None are known at this time. + === modified file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2017-02-23 19:11:11 +0000 +++ plugins.d/plymouth.c 2013-08-27 21:47:35 +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-2012 Teddy Hogeborn + * Copyright © 2010-2012 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 @@ -84,7 +84,7 @@ va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); - if(ret == -1){ + if (ret == -1){ fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name); vfprintf(stderr, formatstring, ap); @@ -174,16 +174,12 @@ } } - char **new_argv = malloc(sizeof(const char *)); - if(new_argv == NULL){ - error_plus(0, errno, "malloc"); - _exit(EX_OSERR); - } + char **new_argv = NULL; char **tmp; int i = 0; - for (; argv[i] != NULL; i++){ - tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2)); - if(tmp == NULL){ + for (; argv[i]!=NULL; i++){ + tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1)); + if (tmp == NULL){ error_plus(0, errno, "realloc"); free(new_argv); _exit(EX_OSERR); @@ -294,12 +290,12 @@ if(proc_id == 0){ struct dirent **direntries = NULL; ret = scandir("/proc", &direntries, is_plymouth, alphasort); - if(ret == -1){ + if (ret == -1){ error_plus(0, errno, "scandir"); } - if(ret > 0){ + if (ret > 0){ ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id); - if(ret < 0){ + if (ret < 0){ error_plus(0, errno, "sscanf"); } } === modified file 'plugins.d/plymouth.xml' --- plugins.d/plymouth.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/plymouth.xml 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -34,11 +34,6 @@ 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -206,7 +201,6 @@ daemon and starting a new one is ugly, but necessary as long as it does not support aborting a password request. - === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2017-02-23 19:11:11 +0000 +++ plugins.d/splashy.c 2011-12-31 23:05:34 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -70,7 +70,7 @@ va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); - if(ret == -1){ + if (ret == -1){ fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name); vfprintf(stderr, formatstring, ap); === modified file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/splashy.xml 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,7 @@ 2008 2009 - 2010 - 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -210,7 +203,6 @@ is ugly, but necessary as long as it does not support aborting a password request. - === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2017-02-23 19:11:11 +0000 +++ plugins.d/usplash.c 2011-12-31 23:05:34 +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-2012 Teddy Hogeborn + * Copyright © 2008-2012 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 @@ -67,7 +67,7 @@ va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); - if(ret == -1){ + if (ret == -1){ fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name); vfprintf(stderr, formatstring, ap); @@ -117,7 +117,7 @@ ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg); if(ret == -1){ int e = errno; - close(*fifo_fd_r); + TEMP_FAILURE_RETRY(close(*fifo_fd_r)); errno = e; return false; } @@ -133,7 +133,7 @@ cmd_line_len - written); if(sret == -1){ int e = errno; - close(*fifo_fd_r); + TEMP_FAILURE_RETRY(close(*fifo_fd_r)); free(cmd_line_alloc); errno = e; return false; @@ -491,7 +491,7 @@ error_plus(0, errno, "read"); status = EX_OSERR; } - close(outfifo_fd); + TEMP_FAILURE_RETRY(close(outfifo_fd)); goto failure; } if(interrupted_by_signal){ @@ -578,7 +578,7 @@ /* Close FIFO */ if(fifo_fd != -1){ - ret = close(fifo_fd); + ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ error_plus(0, errno, "close"); } @@ -587,7 +587,7 @@ /* Close output FIFO */ if(outfifo_fd != -1){ - ret = close(outfifo_fd); + ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd)); if(ret == -1){ error_plus(0, errno, "close"); } @@ -655,7 +655,7 @@ /* Close FIFO (again) */ if(fifo_fd != -1){ - ret = close(fifo_fd); + ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); if(ret == -1 and errno != EINTR){ error_plus(0, errno, "close"); } === modified file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2017-02-23 19:11:11 +0000 +++ plugins.d/usplash.xml 2011-12-31 23:05:34 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -33,14 +33,8 @@ 2008 2009 - 2010 2011 2012 - 2013 - 2014 - 2015 - 2016 - 2017 Teddy Hogeborn Björn Påhlsson @@ -224,7 +218,6 @@ is ugly, but necessary as long as it does not support aborting a password request. - === removed file 'tmpfiles.d-mandos.conf' --- tmpfiles.d-mandos.conf 2016-03-19 03:51:23 +0000 +++ tmpfiles.d-mandos.conf 1970-01-01 00:00:00 +0000 @@ -1,1 +0,0 @@ -d /var/lib/mandos 700 _mandos _mandos