=== removed directory '.bzr-builddeb'
=== removed file '.bzr-builddeb/default.conf'
--- .bzr-builddeb/default.conf 2008-09-17 00:34:09 +0000
+++ .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-[BUILDDEB]
-split = True
=== modified file '.bzrignore'
--- .bzrignore 2012-05-17 01:55:58 +0000
+++ .bzrignore 2008-08-27 01:18:25 +0000
@@ -1,14 +1,8 @@
*.5
*.8
*.8mandos
+plugin-runner
+plugins.d/password-prompt
+plugins.d/password-request
confdir
keydir
-statedir
-man
-plugin-runner
-plugins.d/askpass-fifo
-plugins.d/mandos-client
-plugins.d/password-prompt
-plugins.d/splashy
-plugins.d/usplash
-plugins.d/plymouth
=== removed file 'DBUS-API'
--- DBUS-API 2019-02-09 23:23:26 +0000
+++ DBUS-API 1970-01-01 00:00:00 +0000
@@ -1,155 +0,0 @@
- -*- mode: org; coding: utf-8 -*-
-
- Mandos Server D-Bus Interface
-
-This file documents the D-Bus interface to the Mandos server.
-
-* Bus: System bus
- Bus name: "se.recompile.Mandos"
-
-
-* Object Paths:
-
- | 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.)
-
-
-* Mandos Server Interface:
- Interface name: "se.recompile.Mandos"
-
-** Methods:
-*** RemoveClient(o: ObjectPath) → nothing
- Removes a client
-
-** Signals:
-*** ClientNotFound(s: KeyID, s: Address)
- A client connected from Address using KeyID, but was
- rejected because it was not found in the server. The key ID
- is represented as a string of hexadecimal digits. The address is
- an IPv4 or IPv6 address in its normal string format.
-
-
-* Mandos Client Interface:
- Interface name: "se.recompile.Mandos.Client"
-
-** Methods
-*** Approve(b: Approve) → nothing
- Approve or deny a connected client waiting for approval. If
- denied, a client will not be sent its secret.
-
-*** CheckedOK() → nothing
- 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.
-
-** Properties
-
- Note: Many of these properties directly correspond to a setting in
- "clients.conf", in which case they are fully documented in
- mandos-clients.conf(5).
-
- | Name | Type | Access | clients.conf |
- |-------------------------+------+------------+---------------------|
- | ApprovedByDefault | b | Read/Write | approved_by_default |
- | ApprovalDelay (a) | t | Read/Write | approval_delay |
- | ApprovalDuration (a) | t | Read/Write | approval_duration |
- | ApprovalPending (b) | b | Read | N/A |
- | Checker | s | Read/Write | checker |
- | CheckerRunning (c) | b | Read/Write | N/A |
- | Created (d) | s | Read | N/A |
- | Enabled (e) | b | Read/Write | N/A |
- | Expires (f) | s | Read | N/A |
- | ExtendedTimeout (a) | t | Read/Write | extended_timeout |
- | Fingerprint | s | Read | fingerprint |
- | KeyID | s | Read | key_id |
- | Host | s | Read/Write | host |
- | Interval (a) | t | Read/Write | interval |
- | LastApprovalRequest (g) | s | Read | N/A |
- | LastCheckedOK (h) | s | Read/Write | N/A |
- | LastCheckerStatus (i) | n | Read | N/A |
- | LastEnabled (j) | s | Read | N/A |
- | Name | s | Read | (Section name) |
- | Secret (k) | ay | Write | secret (or secfile) |
- | Timeout (a) | t | Read/Write | timeout |
-
- a) Represented as milliseconds.
-
- b) An approval is currently pending.
-
- c) Changing this property can either start a new checker or abort a
- running one.
-
- d) The creation time of this client object, as an RFC 3339 string.
-
- e) Changing this property enables or disables a client.
-
- f) The date and time this client will be disabled, as an RFC 3339
- string, or an empty string if this is not scheduled.
-
- g) The date and time of the last approval request, as an RFC 3339
- string, or an empty string if this has not happened.
-
- h) The date and time a checker was last successful, as an RFC 3339
- string, or an empty string if this has not happened. Setting
- this property is equivalent to calling CheckedOK(), i.e. the
- current time is set, regardless of the string sent. Please
- always use an empty string when setting this property, to allow
- for possible future expansion.
-
- i) The exit status of the last checker, -1 if it did not exit
- cleanly, -2 if a checker has not yet returned.
-
- j) The date and time this client was last enabled, as an RFC 3339
- string, or an empty string if this has not happened.
-
- k) A raw byte array, not hexadecimal digits.
-
-** Signals
-*** CheckerCompleted(n: Exitcode, x: Waitstatus, s: Command)
- A checker (Command) has completed. Exitcode is either the exit
- code or -1 for abnormal exit. In any case, the full Waitstatus
- (as from wait(2)) is also available.
-
-*** CheckerStarted(s: Command)
- A checker command (Command) has just been started.
-
-*** GotSecret()
- This client has been sent its secret.
-
-*** NeedApproval(t: Timeout, b: ApprovedByDefault)
- This client will be approved or denied in exactly Timeout
- milliseconds, depending on ApprovedByDefault. Approve() can now
- usefully be called on this client object.
-
-*** Rejected(s: Reason)
- This client was not given its secret for a specified Reason.
-
-* Copyright
-
- Copyright © 2010-2018 Teddy Hogeborn
- Copyright © 2010-2018 Björn Påhlsson
-
-** License:
-
- This file is part of Mandos.
-
- Mandos 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.
-
- Mandos 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 Mandos. If not, see .
-
-
-#+STARTUP: showall
=== removed file 'INSTALL'
--- INSTALL 2019-02-09 23:23:26 +0000
+++ INSTALL 1970-01-01 00:00:00 +0000
@@ -1,147 +0,0 @@
--*- org -*-
-
-* Prerequisites
-
-** Operating System
-
- Debian 8.0 "jessie" or Ubuntu 15.10 "Wily Werewolf" (or later).
-
- 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.
-
-** Libraries
-
- The following libraries and packages are needed. (It is possible
- that it might work with older versions of some of these, but these
- versions are confirmed to work. Newer versions are almost
- certainly OK.)
-
-*** Documentation
- These are required to build the manual pages for both the server
- and client:
-
- + 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
-
- Package names:
- docbook docbook-xsl
-
- To build just the documentation, run the command "make doc". Then
- the manual page "mandos.8", for example, can be read by running
- "man -l mandos.8".
-
-*** Mandos Server
- + GnuTLS 3.3 https://www.gnutls.org/
- (but not 3.6.0 or later, until 3.6.6, which works)
- + 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.)
-
- Strongly recommended:
- + fping 2.4b2-to-ipv6 http://www.fping.org/
- + ssh-keyscan from OpenSSH http://www.openssh.com/
-
- Package names:
- avahi-daemon python python-dbus python-gi python-urwid pkg-config
- fping ssh-client
-
-*** 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/
- (but not 3.6.0 or later, until 3.6.6 which works)
- + 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/
-
- Package names:
- initramfs-tools libgnutls-dev gnutls-bin libavahi-core-dev gnupg
- libgpgme11-dev pkg-config ssh
-
-* Installing the Mandos server
-
- 1. Do "make doc".
-
- 2. On the computer to run as a Mandos server, run the following
- command:
- For Debian: su -c 'make install-server'
- For Ubuntu: sudo make install-server
-
- (This creates a configuration without any clients configured; you
- need an actually configured client to do that; see below.)
-
-* Installing the Mandos client.
-
- 1. Do "make all doc".
-
- 2. On the computer to run as a Mandos client, run the following
- command:
- For Debian: su -c 'make install-client'
- For Ubuntu: sudo make install-client
-
- This will also create an OpenPGP key, which will take some time
- and entropy, so be patient.
-
- 3. Run the following command:
- For Debian: su -c 'mandos-keygen --password'
- For Ubuntu: sudo mandos-keygen --password
-
- When prompted, enter the password/passphrase for the encrypted
- root file system on this client computer. The command will
- output a section of text, starting with a [section header]. Copy
- 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:
-
- # update-initramfs -k all -u
-
- 5. On the server computer, start the server by running the command
- For Debian: su -c 'invoke-rc.d mandos start'
- For Ubuntu: sudo service mandos start
-
- At this point, it is possible to verify that the correct password
- will be received by the client by running the command:
-
- # /usr/lib/mandos/plugins.d/mandos-client \
- --pubkey=/etc/keys/mandos/pubkey.txt \
- --seckey=/etc/keys/mandos/seckey.txt \
- --tls-privkey=/etc/keys/mandos/tls-privkey.pem \
- --tls-pubkey=/etc/keys/mandos/tls-pubkey.pem; echo
-
- This command should retrieve the password from the server,
- decrypt it, and output it to standard output.
-
- 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.
-
-* 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
=== modified file 'Makefile'
--- Makefile 2019-02-09 23:23:26 +0000
+++ Makefile 2008-08-25 06:44:13 +0000
@@ -1,515 +1,208 @@
-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=2 -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 .
-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:=
-
-# If BROKEN_PIE is set, do not build with -pie
-ifndef BROKEN_PIE
-FORTIFY += -fPIE
-LINK_FORTIFY += -pie
-endif
+ -Wconversion -Wstrict-prototypes -Wold-style-definition \
+ -Wpacked -Wnested-externs -Wunreachable-code -Winline \
+ -Wvolatile-register-var
+DEBUG=-ggdb3
+# For info about _FORTIFY_SOURCE, see
+#
+FORTIFY=-D_FORTIFY_SOURCE=2 # -fstack-protector-all
#COVERAGE=--coverage
-OPTIMIZE:=-Os -fno-strict-aliasing
-LANGUAGE:=-std=gnu11
-htmldir:=man
-version:=1.7.20
-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)))
-
-## Use these settings for a traditional /usr/local install
-# PREFIX:=$(DESTDIR)/usr/local
-# CONFDIR:=$(DESTDIR)/etc/mandos
-# KEYDIR:=$(DESTDIR)/etc/mandos/keys
-# MANDIR:=$(PREFIX)/man
-# INITRAMFSTOOLS:=$(DESTDIR)/etc/initramfs-tools
-# STATEDIR:=$(DESTDIR)/var/lib/mandos
-# LIBDIR:=$(PREFIX)/lib
-##
-
-## These settings are for a package-type install
-PREFIX:=$(DESTDIR)/usr
-CONFDIR:=$(DESTDIR)/etc/mandos
-KEYDIR:=$(DESTDIR)/etc/keys/mandos
-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)
-AVAHI_LIBS:=$(shell pkg-config --libs avahi-core)
-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)
+OPTIMIZE=-Os
+LANGUAGE=-std=gnu99
+# PREFIX=/usr/local
+PREFIX=$(DESTDIR)/usr
+# CONFDIR=/usr/local/lib/mandos
+CONFDIR=$(DESTDIR)/etc/mandos
+# MANDIR=/usr/local/man
+MANDIR=$(DESTDIR)/usr/share/man
+
+GNUTLS_CFLAGS=$(shell libgnutls-config --cflags)
+GNUTLS_LIBS=$(shell libgnutls-config --libs)
+AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core)
+AVAHI_LIBS=$(shell pkg-config --libs avahi-core)
+GPGME_CFLAGS=$(shell gpgme-config --cflags)
+GPGME_LIBS=$(shell gpgme-config --libs)
# Do not change these two
-CFLAGS+=$(WARN) $(DEBUG) $(FORTIFY) $(SANITIZE) $(COVERAGE) \
- $(OPTIMIZE) $(LANGUAGE) -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)
+LDFLAGS=$(COVERAGE)
-# Commands to format a DocBook document into a manual page
-DOCBOOKTOMAN=$(strip cd $(dir $<); xsltproc --nonet --xinclude \
+# Commands to format a DocBook refentry document into a manual page
+DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \
--param man.charmap.use.subset 0 \
--param make.year.ranges 1 \
--param make.single.year.ranges 1 \
--param man.output.quietly 1 \
--param man.authors.section.enabled 0 \
- /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \
+ /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \
$(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)
-
-DOCBOOKTOHTML=$(strip xsltproc --nonet --xinclude \
- --param make.year.ranges 1 \
- --param make.single.year.ranges 1 \
- --param man.output.quietly 1 \
- --param man.authors.section.enabled 0 \
- --param citerefentry.link 1 \
- --output $@ \
- /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \
- $<; $(HTMLPOST) $@)
-# Fix citerefentry links
-HTMLPOST:=$(SED) --in-place \
- --expression='s/\(\)\([^<]*\)\(<\/span>(\)\([^)]*\)\()<\/span><\/a>\)/\1\3.\5\2\3\4\5\6/g'
-
-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)
-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 \
- plugins.d/mandos-client.8mandos \
- plugins.d/password-prompt.8mandos plugins.d/usplash.8mandos \
- plugins.d/splashy.8mandos plugins.d/askpass-fifo.8mandos \
- plugins.d/plymouth.8mandos intro.8mandos
-
-htmldocs:=$(addsuffix .xhtml,$(DOCS))
-
-objects:=$(addsuffix .o,$(CPROGS))
-
-all: $(PROGS) mandos.lsm
+ $(MANPOST) $(notdir $@)
+# DocBook-to-man post-processing to fix a \n escape bug
+MANPOST=sed --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g'
+
+PLUGINS=plugins.d/password-prompt plugins.d/password-request
+PROGS=plugin-runner $(PLUGINS)
+DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \
+ plugins.d/password-request.8mandos \
+ plugins.d/password-prompt.8mandos mandos.conf.5 \
+ mandos-clients.conf.5
+
+objects=$(addsuffix .o,$(PROGS))
+
+all: $(PROGS)
doc: $(DOCS)
-html: $(htmldocs)
-
-%.5: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.5.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-%.8: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.8.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-%.8mandos: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.8mandos.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-intro.8mandos: intro.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-intro.8mandos.xhtml: intro.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos.8: mandos.xml common.ent mandos-options.xml overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos.8.xhtml: mandos.xml common.ent mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-keygen.8: mandos-keygen.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-keygen.8.xhtml: mandos-keygen.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-monitor.8: mandos-monitor.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-monitor.8.xhtml: mandos-monitor.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-ctl.8: mandos-ctl.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-ctl.8.xhtml: mandos-ctl.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos.conf.5: mandos.conf.xml common.ent mandos-options.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos.conf.5.xhtml: mandos.conf.xml common.ent mandos-options.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-plugin-runner.8mandos: plugin-runner.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-plugin-runner.8mandos.xhtml: plugin-runner.xml common.ent \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \
- common.ent \
- mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOMAN)
-plugins.d/mandos-client.8mandos.xhtml: plugins.d/mandos-client.xml \
- common.ent \
- mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-# Update all these files with version number $(version)
-common.ent: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\($$/\1$(version)">/' \
- $@)
-
-mandos: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-keygen: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(VERSION="\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-ctl: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-monitor: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos.lsm: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(Version:\).*/\1\t$(version)/' \
- $@)
- $(strip $(SED) --in-place \
- --expression='s/^\(Entered-date:\).*/\1\t$(shell date --rfc-3339=date --reference=Makefile)/' \
- $@)
- $(strip $(SED) --in-place \
- --expression='s/\(mandos_\)[0-9.]\+\(\.orig\.tar\.gz\)/\1$(version)\2/' \
- $@)
-
-# Need to add the GnuTLS, Avahi and GPGME libraries, and can't use
-# -fsanitize=leak because GnuTLS and GPGME both leak memory.
-plugins.d/mandos-client: plugins.d/mandos-client.c
- $(CC) $(filter-out -fsanitize=leak,$(CFLAGS)) $(strip\
- ) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) $(strip\
- ) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(strip\
- ) -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
+%.5: %.xml
+ $(DOCBOOKTOMAN)
+
+%.8: %.xml
+ $(DOCBOOKTOMAN)
+
+%.8mandos: %.xml
+ $(DOCBOOKTOMAN)
+
+mandos.8: mandos.xml mandos-options.xml
+ $(DOCBOOKTOMAN)
+
+mandos.conf.5: mandos.conf.xml mandos-options.xml
+ $(DOCBOOKTOMAN)
+
+plugins.d/password-request: plugins.d/password-request.o
+ $(LINK.o) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) \
+ $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+.PHONY : all doc 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
+ -rm --force $(PROGS) $(objects) $(DOCS) core
distclean: clean
mostlyclean: clean
maintainer-clean: clean
- -rm --force --recursive keydir confdir statedir
+ -rm --force --recursive keydir confdir
-check: all
+check:
./mandos --check
- ./mandos-ctl --check
-# Run the client with a local config and key
-run-client: all keydir/seckey.txt keydir/pubkey.txt keydir/tls-privkey.pem keydir/tls-pubkey.pem
- @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 "# From askpass-fifo: mkfifo: Permission denied #"
- @echo "# From mandos-client: #"
- @echo "# Failed to raise privileges: Operation not permitted #"
- @echo "# Warning: network hook \"*\" exited with status * #"
- @echo "# #"
- @echo "# (The messages are caused by not running as root, but you should #"
- @echo "# NOT run \"make run-client\" as root unless you also unpacked and #"
- @echo "# compiled Mandos as root, which is also NOT recommended.) #"
- @echo "###################################################################"
-# We set GNOME_KEYRING_CONTROL to block pam_gnome_keyring
+# Run the server with a local key
+run-client: all keydir/seckey.txt keydir/pubkey.txt \
+ keydir/secring.gpg keydir/pubring.gpg
./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,--tls-privkey=keydir/tls-privkey.pem,--tls-pubkey=keydir/tls-pubkey.pem,--network-hook-dir=network-hooks.d \
- --env-for=mandos-client:GNOME_KEYRING_CONTROL= \
- $(CLIENTARGS)
+ --options-for=password-request:--keydir=keydir
# Used by run-client
-keydir/seckey.txt keydir/pubkey.txt keydir/tls-privkey.pem keydir/tls-pubkey.pem: mandos-keygen
+keydir/secring.gpg: keydir/seckey.txt
+ gpg --homedir $(dir $<) --import $^
+keydir/pubring.gpg: keydir/pubkey.txt
+ gpg --homedir $(dir $<) --import $^
+keydir/seckey.txt keydir/pubkey.txt: mandos-keygen
install --directory keydir
./mandos-keygen --dir keydir --force
# Run the server with a local config
-run-server: confdir/mandos.conf confdir/clients.conf statedir
- ./mandos --debug --no-dbus --configdir=confdir \
- --statedir=statedir $(SERVERARGS)
+run-server: confdir/mandos.conf confdir/clients.conf
+ ./mandos --debug --configdir=confdir
# Used by run-server
confdir/mandos.conf: mandos.conf
install --directory confdir
- install --mode=u=rw,go=r $^ $@
-confdir/clients.conf: clients.conf keydir/seckey.txt keydir/tls-pubkey.pem
+ install $^ $@
+confdir/clients.conf: clients.conf keydir/seckey.txt
install --directory confdir
- install --mode=u=rw $< $@
+ install clients.conf $@
# Add a client password
- ./mandos-keygen --dir keydir --password --no-ssh >> $@
-statedir:
- install --directory statedir
-
-install: install-server install-client-nokey
-
-install-html: html
- install --directory $(htmldir)
- install --mode=u=rw,go=r --target-directory=$(htmldir) \
- $(htmldocs)
+ ./mandos-keygen --dir keydir --password >> $@
+
+install: install-server install-client
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 --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
- mandos-ctl
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
- mandos-monitor
- install --mode=u=rw,go=r --target-directory=$(CONFDIR) \
- mandos.conf
- install --mode=u=rw --target-directory=$(CONFDIR) \
+ install --directory --parents $(CONFDIR) $(MANDIR)/man5 \
+ $(MANDIR)/man8
+ install --mode=0755 mandos $(PREFIX)/sbin/mandos
+ install --mode=0644 --target-directory=$(CONFDIR) mandos.conf
+ install --mode=0640 --target-directory=$(CONFDIR) \
clients.conf
- install --mode=u=rw,go=r dbus-mandos.conf \
- $(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 \
- update-rc.d mandos defaults 25 15;\
- fi
gzip --best --to-stdout mandos.8 \
> $(MANDIR)/man8/mandos.8.gz
- gzip --best --to-stdout mandos-monitor.8 \
- > $(MANDIR)/man8/mandos-monitor.8.gz
- gzip --best --to-stdout mandos-ctl.8 \
- > $(MANDIR)/man8/mandos-ctl.8.gz
gzip --best --to-stdout mandos.conf.5 \
> $(MANDIR)/man5/mandos.conf.5.gz
gzip --best --to-stdout mandos-clients.conf.5 \
> $(MANDIR)/man5/mandos-clients.conf.5.gz
- gzip --best --to-stdout intro.8mandos \
- > $(MANDIR)/man8/intro.8mandos.gz
-install-client-nokey: all doc
- install --directory $(LIBDIR)/mandos $(CONFDIR)
- install --directory --mode=u=rwx $(KEYDIR) \
- $(LIBDIR)/mandos/plugins.d \
- $(LIBDIR)/mandos/plugin-helpers
- if [ "$(CONFDIR)" != "$(LIBDIR)/mandos" ]; then \
- install --mode=u=rwx \
- --directory "$(CONFDIR)/plugins.d" \
- "$(CONFDIR)/plugin-helpers"; \
- fi
- install --mode=u=rwx,go=rx --directory \
- "$(CONFDIR)/network-hooks.d"
- install --mode=u=rwx,go=rx \
- --target-directory=$(LIBDIR)/mandos plugin-runner
- install --mode=u=rwx,go=rx \
- --target-directory=$(LIBDIR)/mandos mandos-to-cryptroot-unlock
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
+install-client: all doc /usr/share/initramfs-tools/hooks/.
+ install --directory --parents $(PREFIX)/lib/mandos \
+ $(CONFDIR) $(MANDIR)/man8
+ install --directory --mode=0700 $(PREFIX)/lib/mandos/plugins.d
+ chmod u=rwx,g=,o= $(PREFIX)/lib/mandos/plugins.d
+ install --mode=0755 --target-directory=$(PREFIX)/lib/mandos \
+ plugin-runner
+ install --mode=0755 --target-directory=$(PREFIX)/sbin \
mandos-keygen
- install --mode=u=rwx,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
+ install --mode=0755 \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
plugins.d/password-prompt
- install --mode=u=rwxs,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
- plugins.d/mandos-client
- install --mode=u=rwxs,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
- plugins.d/usplash
- install --mode=u=rwxs,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
- plugins.d/splashy
- install --mode=u=rwxs,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
- plugins.d/askpass-fifo
- install --mode=u=rwxs,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugins.d \
- plugins.d/plymouth
- install --mode=u=rwx,go=rx \
- --target-directory=$(LIBDIR)/mandos/plugin-helpers \
- plugin-helpers/mandos-client-iprouteadddel
+ install --mode=4755 \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/password-request
install initramfs-tools-hook \
- $(INITRAMFSTOOLS)/hooks/mandos
- install --mode=u=rw,go=r initramfs-tools-conf \
- $(INITRAMFSTOOLS)/conf.d/mandos-conf
+ /usr/share/initramfs-tools/hooks/mandos
+ install initramfs-tools-hook-conf \
+ /usr/share/initramfs-tools/conf-hooks.d/mandos
install initramfs-tools-script \
- $(INITRAMFSTOOLS)/scripts/init-premount/mandos
- install initramfs-tools-script-stop \
- $(INITRAMFSTOOLS)/scripts/local-premount/mandos
- install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR)
+ /usr/share/initramfs-tools/scripts/local-top/mandos
gzip --best --to-stdout mandos-keygen.8 \
> $(MANDIR)/man8/mandos-keygen.8.gz
gzip --best --to-stdout plugin-runner.8mandos \
> $(MANDIR)/man8/plugin-runner.8mandos.gz
- gzip --best --to-stdout plugins.d/mandos-client.8mandos \
- > $(MANDIR)/man8/mandos-client.8mandos.gz
gzip --best --to-stdout plugins.d/password-prompt.8mandos \
> $(MANDIR)/man8/password-prompt.8mandos.gz
- gzip --best --to-stdout plugins.d/usplash.8mandos \
- > $(MANDIR)/man8/usplash.8mandos.gz
- gzip --best --to-stdout plugins.d/splashy.8mandos \
- > $(MANDIR)/man8/splashy.8mandos.gz
- gzip --best --to-stdout plugins.d/askpass-fifo.8mandos \
- > $(MANDIR)/man8/askpass-fifo.8mandos.gz
- gzip --best --to-stdout plugins.d/plymouth.8mandos \
- > $(MANDIR)/man8/plymouth.8mandos.gz
-
-install-client: install-client-nokey
-# Post-installation stuff
- -$(PREFIX)/sbin/mandos-keygen --dir "$(KEYDIR)"
+ gzip --best --to-stdout plugins.d/password-request.8mandos \
+ > $(MANDIR)/man8/password-request.8mandos.gz
+ -$(PREFIX)/sbin/mandos-keygen
update-initramfs -k all -u
- echo "Now run mandos-keygen --password --dir $(KEYDIR)"
uninstall: uninstall-server uninstall-client
-uninstall-server:
+uninstall-server: $(PREFIX)/sbin/mandos
-rm --force $(PREFIX)/sbin/mandos \
- $(PREFIX)/sbin/mandos-ctl \
- $(PREFIX)/sbin/mandos-monitor \
$(MANDIR)/man8/mandos.8.gz \
- $(MANDIR)/man8/mandos-monitor.8.gz \
- $(MANDIR)/man8/mandos-ctl.8.gz \
$(MANDIR)/man5/mandos.conf.5.gz \
$(MANDIR)/man5/mandos-clients.conf.5.gz
- update-rc.d -f mandos remove
-rmdir $(CONFDIR)
uninstall-client:
# Refuse to uninstall client if /etc/crypttab is explicitly configured
# to use it.
! grep --regexp='^ *[^ #].*keyscript=[^,=]*/mandos/' \
- $(DESTDIR)/etc/crypttab
+ /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 \
- $(INITRAMFSTOOLS)/hooks/mandos \
- $(INITRAMFSTOOLS)/conf-hooks.d/mandos \
- $(INITRAMFSTOOLS)/scripts/init-premount/mandos \
+ $(PREFIX)/lib/mandos/plugin-runner \
+ $(PREFIX)/lib/mandos/plugins.d/password-prompt \
+ $(PREFIX)/lib/mandos/plugins.d/password-request \
+ /usr/share/initramfs-tools/hooks/mandos \
+ /usr/share/initramfs-tools/conf-hooks.d/mandos \
+ $(MANDIR)/man8/plugin-runner.8mandos.gz \
$(MANDIR)/man8/mandos-keygen.8.gz \
- $(MANDIR)/man8/plugin-runner.8mandos.gz \
- $(MANDIR)/man8/mandos-client.8mandos.gz
$(MANDIR)/man8/password-prompt.8mandos.gz \
- $(MANDIR)/man8/usplash.8mandos.gz \
- $(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)
+ $(MANDIR)/man8/password-request.8mandos.gz
+ -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \
+ $(PREFIX)/lib/mandos $(CONFDIR)
update-initramfs -k all -u
purge: purge-server purge-client
purge-server: uninstall-server
- -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf \
- $(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
+ -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf
-rmdir $(CONFDIR)
purge-client: uninstall-client
- -shred --remove $(KEYDIR)/seckey.txt $(KEYDIR)/tls-privkey.pem
- -rm --force $(CONFDIR)/plugin-runner.conf \
- $(KEYDIR)/pubkey.txt $(KEYDIR)/seckey.txt \
- $(KEYDIR)/tls-pubkey.txt $(KEYDIR)/tls-privkey.txt
- -rmdir $(KEYDIR) $(CONFDIR)/plugins.d $(CONFDIR)
+ -rm --force $(CONFDIR)/seckey.txt $(CONFDIR)/pubkey.txt
+ -rmdir $(CONFDIR) $(CONFDIR)/plugins.d
=== removed file 'NEWS'
--- NEWS 2018-08-19 20:17:48 +0000
+++ NEWS 1970-01-01 00:00:00 +0000
@@ -1,497 +0,0 @@
-This NEWS file records noteworthy changes, very tersely.
-See the manual for detailed information.
-
-Version 1.7.20 (2018-08-19)
-* Client
-** Fix: Adapt to the Debian cryptsetup package 2.0.3 or later.
- Important: in that version or later, the plugins "askpass-fifo",
- "password-prompt", and "plymouth" will no longer be run, since they
- would conflict with what cryptsetup is doing. Other plugins, such
- as mandos-client and any user-supplied plugins, will still run.
-** Better error message if failing to decrypt secret data
-** Check for (and report) any key import failure from GPGME
-** Better error message if self-signature verification fails
-** Set system clock if not set; required by GnuPG for key import
-** When debugging plugin-runner, it will now show starting plugins
-
-Version 1.7.19 (2018-02-22)
-* Client
-** Do not print "unlink(...): No such file or directory".
-** Bug fixes: Fix file descriptor leaks.
-** Bug fix: Don't use leak sanitizer with leaking libraries.
-
-Version 1.7.18 (2018-02-12)
-* Client
-** Bug fix: Revert faulty fix for a nonexistent bug in the
- plugin-runner
-
-Version 1.7.17 (2018-02-10)
-* Client
-** Bug fix: Fix a memory leak in the plugin-runner
-** Bug fix: Fix memory leaks in the plymouth plugin
-
-Version 1.7.16 (2017-08-20)
-* Client
-** Bug fix: ignore "resumedev" entries in initramfs' cryptroot file
-** Bug fix in plymouth plugin: fix memory leak, avoid warning output
-
-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
-** Init script supports new "status" action.
-* Client
-** Now uses all interfaces by default; the --interface option can
- still be used to restrict it, and the argument to --interface (as
- well as the $DEVICE environment variable for the network hooks) is
- now a comma-separated list of interfaces to use.
-
-Version 1.5.5 (2012-06-01)
-* Server
-** Server takes new --socket option
-
-Version 1.5.4 (2012-05-20)
-* Server
-** Bug fix: Regression fix: Make non-zero approval timeout values work.
-** Bug fix: Regression fix: Allow changing the Timeout D-Bus property.
-** Fall back to not bind to an interface if an invalid interface name
- is given.
-** Removed support for undocumented feature of using plain "%%s" in
- "checker" client option.
-** Old D-Bus interface are now marked as deprecated.
-** mandos-monitor: Bug fix: show approval timers correctly.
-** mandos-ctl: Show "Extended Timeout" correctly, not as milliseconds.
-
-Version 1.5.3 (2012-01-15)
-* Server
-** Add D-Bus property se.recompile.Client.LastCheckerStatus and use it
- in mandos-monitor.
-* Client
-** Fix bugs in the example "bridge" network hook.
-
-Version 1.5.2 (2012-01-08)
-* Server
-** Removed D-Bus signal se.recompile.Mandos.NewRequest() added in
- 1.5.0. It was buggy and was of questionable utility.
-
-Version 1.5.1 (2012-01-01)
-* Server
-** Include intro(8mandos) manual page, missing since migration from
- README file in version 1.4.0.
-
-Version 1.5.0 (2012-01-01)
-* Client
-** Network hooks. The Mandos client can now run custom scripts to take
- up a network interface before the client is run. Three example
- scripts are provided: "wireless", "openvpn", and "bridge".
- To facilitate this, the client now prefers network interfaces which
- are up (if any) over all other interfaces.
-* Server
-** Persistent state. Client state is now saved between server
- restarts.
-** clients.conf file can now contain "enabled" setting for clients.
-** Bug fix: Fix rare crash bug.
-** Bug fix: Send corrent D-Bus type in PropertyChanged for
- "ApprovalDelay", "ApprovalDuration", "Timeout", and
- "ExtendedTimeout".
-** mandos-ctl: Bare numbers as arguments are taken to be milliseconds.
-** Bug fix: mandos-ctl --secret option now works.
-** New D-Bus signal: se.recompile.Mandos.NewRequest(s).
-
-Version 1.4.1 (2011-10-15)
-* Server
-** Make D-Bus properties settable again, and handle checkers
- for disabled clients correctly.
-* Miscellaneous fixes to "pedantic" Lintian warnings
-
-Version 1.4.0 (2011-10-09)
-* README file migrated to manual page intro(8mandos).
-* Client:
-** Fixed warning about "rmdir: Directory not empty".
-* Server:
-** Default values changed: timeout 5 minutes, interval 2 minutes.
-** Clients gets an expiration extension when receiving a password,
- controlled by new "extended_timeout" setting.
-** New domain name: "fukt.bsnet.se" changes to "recompile.se". This
- also affects the D-Bus bus and interface names (old names still
- work). Users should start using the new names immediately.
-** New D-Bus Client object properties "Expires" and "ExtendedTimeout";
- see DBUS-API for details.
-
-Version 1.3.1 (2011-07-27)
-* Client:
-** Client now retries all Mandos servers periodically.
-** Work around Debian bug #633582 - fixes "Permission denied" problem.
-
-Version 1.3.0 (2011-03-08)
-* Server:
-** Updated for Python 2.6.
-* Client:
-** Bug fix: Make the password-prompt plugin not conflict with
- Plymouth.
-** Bug fix: Bug fix: update initramfs also when purging package.
-
-Version 1.2.3 (2010-10-11)
-* Server:
-** Bug fix: Expose D-Bus API also in non-debug mode.
-
-Version 1.2.2 (2010-10-07)
-* Client:
-** splashy: Minor fix to compile with non-Linux kernels.
-
-Version 1.2.1 (2010-10-02)
-* Server:
-** mandos-monitor(8): Documentation bug fix: Key for removing client
- is "R", not "r".
-
-Version 1.2 (2010-09-28)
-* Client:
-** New "plymouth" plugin to ask for a password using the Plymouth
- graphical boot system.
-** The Mandos client now automatically chooses a network interface if
- the DEVICE setting in /etc/initramfs-tools/initramfs.conf is set to
- the empty string. This is also the new default instead of "eth0".
-** The Mandos client --connect option now loops indefinitely until a
- password is received from the specified server.
-** Bug fix: Quote directory correctly in mandos-keygen with --password
-** Bug fix: don't use "echo -e" in mandos-keygen; unsupported by dash.
-* Server:
-** Terminology change: clients are now "ENABLED" or "DISABLED", not
- "valid" or "invalid".
-** New D-Bus API; see the file "DBUS-API".
-** New control utilities using the new D-Bus API:
- + mandos-ctl A command-line based utility
- + mandos-monitor A text-based GUI interface
-** New feature: manual interactive approval or denying of clients on a
- case-by-case basis.
-** New --debuglevel option to control logging
-** Will not write PID file if --debug is passed
-** Bug fix: Avoid race conditions with short "interval" values or
- fast checkers.
-** Bug fix: Don't try to bind to a network interface when none is
- specified
-
-Version 1.0.14 (2009-10-25)
-Enable building without -pie and -fPIE if BROKEN_PIE is set.
-
-Version 1.0.13 (2009-10-22)
-* Client
-** Security bug fix: If Mandos server is also installed, do not copy
- its config files (with encrypted passwords) into the initrd.img-*
- files.
-
-Version 1.0.12 (2009-09-17)
-* Client
-** Bug fix: Allow network interface renaming by "udev" by taking down
- the network interface after using it.
-** Bug fix: User-supplied plugins are now installed correctly.
-** Bug fix: If usplash was used but the password was instead provided
- by the Mandos server, the usplash daemon used to ignore the first
- command passed to it. This has been fixed.
-** Bug fix: Make the "--userid" and "--groupid" options in
- "plugin-runner.conf" work.
-* Server
-** Bug fix: Fix the LSB header in the init.d script to make dependency
- based booting work.
-** A client receiving its password now also counts as if a checker was
- run successfully (i.e. the timeout timer is reset).
-
-Version 1.0.11 (2009-05-23)
-* Client
-** Bug fix: Use "pkg-config" instead of old "libgnutls-config".
-
-Version 1.0.10 (2009-05-17)
-* Client
-** Security bug fix: Fix permissions on initrd.img-*.bak files when
- upgrading from older versions.
-
-Version 1.0.9 (2009-05-17)
-* Client
-** Security bug fix: Fix permissions on initrd.img file when
- installing new linux-image-* packages calling mkinitramfs-kpkg (all
- version lower than 2.6.28-1-* does this).
-
-Version 1.0.8 (2009-02-25)
-* Client
-** Bug fix: Fix missing quote characters in initramfs-tools-hook.
-
-Version 1.0.7 (2009-02-24)
-* Client
-** Bug fix: Do not depend on GNU awk.
-
-Version 1.0.6 (2009-02-13)
-* Server
-** Fix bug where server would stop responding, with a zombie checker
-** Support for disabling IPv6 (only for advanced users)
-** Fix bug which made server not change group ID
-
-* Client
-** Bug fix: Fix permission for /lib64 (on relevant architechtures).
-** Add support for IPv4 addresses.
-** Add support in mandos-client for not bringing up a network
- interface by specifying an empty string to "--interface".
-** Make password prompt on boot not be mangled by kernel log messages
- about network interface.
-** Get network interface from initramfs.conf and/or from kernel
- command line.
-** If set by "ip=" kernel command line, configure network on boot.
-** Support connecting directly using "mandos=connect" kernel command.
- line option, provided network is configured using "ip=".
-** Fix bug which made plugin-runner and mandos-client not change group
- ID.
-** Fix bug where the "--options-for" option of plugin-runner would
- truncate the value at the first colon character.
-** Fix bug where plugin-runner would not go to fallback if all plugins
- failed.
-** Fix bug where mandos-client would not clean temporary directory on
- a signal or on certain file systems.
-** Bug fix: remove bashism in /bin/sh script "mandos-keygen".
-
-Version 1.0.5 (2009-01-17)
-* Client
-** Fix small memory leak in plugin-runner.
-
-Version 1.0.4 (2009-01-15)
-* Server
-** Only find matched user/group pairs when searching for suitable
- nonprivileged user/group to switch to.
-
-* Client
-** New kernel parameter "mandos=off" makes client not run at boot.
-** Fix linking errors and compilation warnings on AMD64.
-** Parse numbers in command line options better.
-** The splashy and usplash plugins are more robust while traversing
- /proc, and will not abort if a process suddenly disappears.
-
-Version 1.0.3 (2009-01-06)
-* Server
-** Now tries to change to user and group "_mandos" before falling back
- to trying the old values "mandos", "nobody:nogroup", and "65534".
-** Now does not abort on startup even if no clients are defined in
- clients.conf.
-
-* Client
-** Plugins named "*.dpkg-bak" are now ignored.
-** Hopefully fixed compilation failure on some architectures where the
- C compiler does not recognize the "-z" option as a linker option.
-
-Version 1.0.2 (2008-10-17)
-* mandos-keygen now signs the encrypted key blobs. This signature is
- not currently verified by mandos-client, but this may change in the
- future.
-
-Version 1.0.1 (2008-10-07)
-* Server
-** Expand environment variables and ~user in clients.conf's "secfile"
- The "secfile" option in /etc/mandos/clients.conf now expands
- "~user/foo" and "$ENVVAR" strings.
-
-* Client (plugin-runner, plugins, etc.)
-** Manual pages for the usplash, splashy, and askpass-fifo plugins.
- All plugins now have man pages.
-** More secure compilation and linking flags.
- All programs are now compiled with "-fstack-protector-all -fPIE
- -pie", and linked using "-z relro -pie" for additional security.
-
-* There is now a "NEWS" file (this one), giving a history of
- noteworthy changes.
=== removed file 'README'
--- README 2016-03-23 07:11:22 +0000
+++ README 1970-01-01 00:00:00 +0000
@@ -1,11 +0,0 @@
-Please see: https://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
-Mandos server and run this command:
-
- man 8mandos intro
-
-In short, this is the Mandos system; it allows computers to have
-encrypted root file systems and at the same time be capable of remote
-and/or unattended reboots.
=== modified file 'TODO'
--- TODO 2019-02-09 23:31:44 +0000
+++ TODO 2008-08-27 01:18:25 +0000
@@ -1,123 +1,150 @@
-*- org -*-
-* 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 getaddrinfo(hints=AI_NUMERICHOST) instead of inet_pton()
-** TODO [#C] Make start_mandos_communication() take "struct server".
-** TODO [#C] --interfaces=regex,eth*,noregex (bridge-utils-interfaces(5))
-** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL
-
-* splashy
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL
-
-* usplash (Deprecated)
-** TODO [#B] Make it work again
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL
-
-* askpass-fifo
-** TODO [#A] Detect partial writes to stdout and exit with EX_TEMPFAIL
+* [#A] README file
+
+* plugin-runner
+** [#B] Add more comments to code
+** [#B] Add more if(debug) calls
+** [#B] Seperate more code to function for more readability
+** [#A] Man page: man8/plugin-runner.8mandos
+*** EXIT STATUS
+*** ENVIRONMENT
+ Environment is modified according to options and passed to plugins
+*** EXAMPLE
+ Examples of normal usage, debug usage, debugging single or all
+ plugins, etc.
+*** FILES
+*** SECURITY
+ Note the danger of using this program, since you might lock
+ yourself out of your system without any means of entering the root
+ file system password. This is, however, very unlikely considering
+ the fallback to getpass(3).
+*** BUGS
+*** SEE ALSO
+ Explaining text on what you can read
+
+* password-request
+** [#A] Man page: man8/password-request.8mandos
+*** SYNOPSIS
+ Document short options
+*** DESCRIPTION
+ State that this command is not meant to be invoked directly, but
+ is run as a plugin from mandos-client(8) and only run in the
+ initrd environment, not the real system.
+*** PURPOSE
+ As in mandos.xml
+*** OVERVIEW
+ As in mandos.xml
+*** EXIT STATUS
+*** ENVIRONMENT
+ Note that it does *not* currently use cryptsource or crypttarget.
+*** FILES
+ Describe the key files and the key ring files. Also note that
+ they should normally have been automatically created.
+*** BUGS
+*** EXAMPLE
+ Examples of normal usage, debug usage, debugging by connecting
+ directly, etc.
+*** SECURITY
+*** SEE ALSO
+ Update from mandos.xml
+** [#B] Temporarily lower kernel log level
+ for less printouts during sucessfull boot.
+** IPv4 support
+** use strsep instead of strtok?
+** Do not depend on GnuPG key rings on disk
+ This would mean creating new GnuPG key rings with GPGME by
+ importing the key files from scratch on every program start.
+** Keydir move: /etc/mandos -> /etc/keys/mandos
+ Must create in preinst if not pre-depending on cryptsetup
* 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 [#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
+** [#A] Man page: man8/password-prompt.8mandos
+*** SYNOPSIS
+ Document short options
+*** DESCRIPTION
+ Note that this is more or less a simple getpass(3) wrapper, even
+ though actual use of getpass(3) is not guaranteed.
+*** EXIT STATUS
+*** ENVIRONMENT
+ Document use of "cryptsource" and "crypttarget".
+*** FILES
+*** BUGS
+*** EXAMPLE
+ Examples of normal usage, debug usage, with a prefix, etc.
+*** SECURITY
+ Not much to do here but it is noteworthy to state the danger of
+ not having a fallback option.
+*** SEE ALSO
+ Refer to mandos-client(8mandos) and password-request(8mandos)
+ and also, perhaps, to cryptsetup(8)?
+** Use getpass(3)?
+ Man page says "obsolete", but [[info:libc:getpass][GNU LibC Manual: Reading Passwords]]
+ does not. See also [[http://sources.redhat.com/ml/libc-alpha/2003-05/msg00251.html][Marcus Brinkmann: Re: getpass obsolete?]] and
+ [[http://article.gmane.org/gmane.comp.lib.glibc.alpha/4906][Petter Reinholdtsen: Re: getpass obsolete?]], and especially also
+ [[http://www.steve.org.uk/Reference/Unix/faq_4.html#SEC48][Unix Programming FAQ 3.1 How can I make my program not echo input?]]
* 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?
-** TODO [#C] config for TXT record
-** TODO Log level dbus option
- SetLogLevel D-Bus call
-** TODO [#C] DBusServiceObjectUsingSuper
-** TODO [#B] Global enable/disable flag
-** TODO [#B] By-client countdown on number of secrets given
-** D-Bus Client method NeedsPassword(50) - Timeout, default disapprove
- + SetPass(u"gazonk", True) -> Approval, persistent
- + Approve(False) -> Close client connection immediately
-** TODO [#C] python-parsedatetime
-** TODO Separate logging logic to own object
-** TODO [#B] 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 D-Bus AddClient() method on server object
-** TODO Use org.freedesktop.DBus.Method.NoReply annotation on async methods. :2:
-** 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/
-** 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()
-
-* mandos-ctl
-*** Handle "no D-Bus server" and/or "no Mandos server found" better
-** TODO Remove old string_to_delta format :2:
-
-* TODO mandos-dispatch
- Listens for specified D-Bus signals and spawns shell commands with
- 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.
-
-* mandos-keygen
-** TODO "--secfile" option
- Using the "secfile" option instead of "secret"
-** TODO [#B] "--test" option
- For testing decryption before rebooting.
-
-* Package
+** [#A] /etc/init.d/mandos-server :teddy:
+** [#B] Log level :bugs:
+** /etc/mandos/clients.d/*.conf
+ Watch this directory and add/remove/update clients?
+** config for TXT record
+** [#B] Run-time communication with server :bugs:
+ Probably using D-Bus
+ See also [[*Mandos-tools]]
+** Implement --foreground :bugs:
+ [[info:standards:Option%20Table][Table of Long Options]]
+** Implement --socket
+ [[info:standards:Option%20Table][Table of Long Options]]
+** Date+time on console log messages :bugs:
+ Is this the default?
+
+* Mandos-tools/utilities
+ All of this probably using D-Bus
+** List clients
+** Disable client
+** Enable client
+
+* Man pages tags
+ Go through all man pages to conform to the style of tags chosen in
+ [[http://svn.debian.org/wsvn/debian-xml-sgml/packages/docbook-xsl/trunk/debian/examples/foo.1.example_manpage.xml?op=file&rev=0&sc=0][foo.1.example_manpage.xml]].
+
+* Installer
+** Client-side
+*** Update initrd.img after installation
+ This seems to use some kind of "trigger" system
+ [[file:/usr/share/doc/dpkg/triggers.txt.gz]]
+ dpkg-trigger(1), deb-triggers(5)
+*** Keydir move: /etc/mandos -> /etc/keys/mandos
+ Must create in preinst if not pre-depending on cryptsetup
+*** mandos-keygen
+**** "--passfile" option
+ Using the "secfile" option instead of "secret"
+**** [#A] "--test" option
+ For testing decryption before rebooting.
+** Server-side
+*** [#A] Create mandos user and group for server
+*** [#A] Create /var/run/mandos directory with perm and ownership
+
+* [#A] 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)
+*** Do not install in initrd.img if configured not to.
+ Use "/etc/initramfs-tools/conf.d/mandos"? Definitely a debconf
+ question.
+** /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]]
+** unperish
+** bzr-builddeb
+
+* INSTALL file
+
+* Web site
+
+* Mailing list
+
+* Announce project on news
+ [[news:comp.os.linux.announce]]
#+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 'clients.conf'
--- clients.conf 2019-02-09 23:34:15 +0000
+++ clients.conf 2008-08-27 01:18:25 +0000
@@ -2,45 +2,25 @@
# values, so uncomment and change them if you want different ones.
[DEFAULT]
-# How long until a client is disabled and not be allowed to get the
-# data this server holds.
-;timeout = PT5M
+# How long until a client is considered invalid - that is, ineligible
+# to get the data this server holds.
+;timeout = 1h
# How often to run the checker to confirm that a client is still up.
# Note: a new checker will not be started if an old one is still
# running. The server will wait for a checker to complete until the
-# above "timeout" occurs, at which time the client will be disabled,
-# and any running checker killed.
-;interval = PT2M
-
-# Extended timeout is an added timeout that is given once after a
-# password has been sent sucessfully to a client. This allows for
-# additional delays caused by file system checks and quota checks.
-;extended_timeout = PT15M
+# above "timeout" occurs, at which time the client will be marked
+# invalid, and any running checker killed.
+;interval = 5m
# What command to run as "the checker".
-;checker = fping -q -- %%(host)s
-
-# Whether to approve a client by default after the approval delay.
-;approved_by_default = True
-
-# How long to wait for approval.
-;approval_delay = PT0S
-
-# How long one approval will last.
-;approval_duration = PT1S
-
-# Whether this client is enabled by default
-;enabled = True
+;checker = fping -q -- %(host)s
;####
;# Example client
;[foo]
;
-;# TLS public key ID
-;key_id = f33fcbed11ed5e03073f6a55b86ffe92af0e24c045fb6e3b40547b3dc0c030ed
-;
;# OpenPGP key fingerprint
;fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920
;
@@ -71,23 +51,15 @@
;####
;# Another example client, named "bar".
;[bar]
-;# The key ID is not space or case sensitive
-;key_id = F33FCBED11ED5E03073F6A55B86FFE92 AF0E24C045FB6E3B40547B3DC0C030ED
-;
;# The fingerprint is not space or case sensitive
;fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27
;
;# If "secret" is not specified, a file can be read for the data.
-;secfile = /etc/keys/mandos/bar-secret.bin
+;;secfile = /etc/mandos/bar-secret.txt.asc
;
;# An IP address for host is also fine, if the checker accepts it.
;host = 192.0.2.3
;
;# Parameters from the [DEFAULT] section can be overridden per client.
-;interval = PT1M
-;
-;# This client requires manual approval before it receives its secret.
-;approved_by_default = False
-;# Require approval within 30 seconds.
-;approval_delay = PT30S
+;interval = 5m
;####
=== removed file 'common.ent'
--- common.ent 2018-08-19 20:17:48 +0000
+++ common.ent 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-
-
-
=== removed file 'dbus-mandos.conf'
--- dbus-mandos.conf 2011-10-02 19:18:24 +0000
+++ dbus-mandos.conf 1970-01-01 00:00:00 +0000
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
=== removed directory 'debian'
=== removed file 'debian/changelog'
--- debian/changelog 2018-08-19 20:17:48 +0000
+++ debian/changelog 1970-01-01 00:00:00 +0000
@@ -1,735 +0,0 @@
-mandos (1.7.20-1) unstable; urgency=medium
-
- * New upstream release.
- * Fix "[tethys] mandos-client: Mandos client fails while booting but
- works from chroot into unpacked initramfs" by setting system clock if
- necessary (Closes: #894495)
- * Fix "initramfs boot script assumes internal cryptsetup implementation
- details and is now broken" by only using documented
- interfaces (Closes: #904899)
- * debian/mandos-client.dirs: Add
- "usr/share/initramfs-tools/scripts/local-premount" and
- "usr/share/initramfs-tools/conf.d", and remove
- "usr/share/initramfs-tools/conf-hooks.d".
- * debian/control (mandos-client/Depends): Add "(>= 0.99)" to dependency
- on "initramfs-tools".
- * debian/control (Source: mandos/Rules-Requires-Root): New; set to
- "binary-targets".
- (Standards-Version): Update to "4.2.0".
-
- -- Teddy Hogeborn Sun, 19 Aug 2018 22:14:04 +0200
-
-mandos (1.7.19-1) unstable; urgency=medium
-
- * New upstream release.
- * Fix "fails with "LeakSanitizer has encountered a fatal error"" by not
- using LeakSanitizer in affected binary (Closes: #886595)
-
- -- Teddy Hogeborn Thu, 22 Feb 2018 19:47:59 +0100
-
-mandos (1.7.18-1) unstable; urgency=medium
-
- * New upstream release.
-
- -- Teddy Hogeborn Mon, 12 Feb 2018 16:00:11 +0100
-
-mandos (1.7.17-1) unstable; urgency=medium
-
- * New upstream release.
- * Fix "fails with "LeakSanitizer has encountered a fatal error""
- by fixing memory leak in plugin-runner (Closes: #886595)
- * debian/control (Build-Depends): Also depend on "libgnutls28-dev (<<
- 3.6.0) | libgnutls30 (<< 3.6.0)".
- (Package: mandos/Depends): - '' -
- * debian/compat: Change to "10".
- * debian/watch (version): Change to "4".
- (opts/pgpsigurlmangle): Remove.
- (opts/pgpmode): New; set to "auto".
- (URL): Change to "https://ftp.recompile.se/pub/@PACKAGE@/@PACKAGE@
- @ANY_VERSION@(?:\.orig)?@ARCHIVE_EXT@".
- * debian/copyright: Update copyright year to 2018.
- * debian/rules: Support the "noopt" and "parallel" DEB_BUILD_OPTIONS.
- (override_dh_fixperms-arch): Use the DEB_HOST_MULTIARCH
- variable directly instead of shelling out to "dpkg-architecture".
- * debian/control (Standards-Version): Update to "4.1.3".
- (Build-Depends): Change version of debhelper dependency to ">= 10".
- * debian/mandos.lintian-overrides
- (init.d-script-needs-depends-on-lsb-base): Change line number to "46".
-
- -- Teddy Hogeborn Sat, 10 Feb 2018 19:09:50 +0100
-
-mandos (1.7.16-1) unstable; urgency=medium
-
- * New upstream release.
- * debian/copyright (License): Use program name explicitly.
- (Format): Use https in URL.
- * debian/control (Priority): Change from "extra" to "optional".
- (Standards-Version): Update to "4.0.1".
-
- -- Teddy Hogeborn Sun, 20 Aug 2017 21:05:26 +0200
-
-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.
- * debian/copyright (Copyright): Join the two lines to a single line.
- * debian/mandos-client.README.Debian: Update to refer to the new
- location of the example network hooks, and the new feature of using
- all network interfaces.
- * debian/mandos-client.docs (network-hooks.d): Removed.
- * debian/mandos-client.examples (network-hooks.d): New.
- * debian/rules (binary-common): Added "dh_installexamples".
- (binary-common/dh_fixperms): Exclude new location of
- "network-hooks.d".
-
- -- Teddy Hogeborn Mon, 18 Jun 2012 00:15:23 +0200
-
-mandos (1.5.5-1) unstable; urgency=low
-
- * New upstream release.
- * debian/copyright (Format): Updated to
- "http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/".
- * debian/control (Build-Depends): Removed "man, locales-all".
-
- -- Teddy Hogeborn Fri, 01 Jun 2012 20:30:41 +0200
-
-mandos (1.5.4-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 20 May 2012 15:38:34 +0200
-
-mandos (1.5.3-1.2) unstable; urgency=low
-
- * Non-maintainer upload.
- * Set Architecture to linux-any. (Closes: #647670)
-
- -- Robert Millan Sun, 22 Apr 2012 16:22:01 +0200
-
-mandos (1.5.3-1.1) unstable; urgency=low
-
- * Non-maintainer upload.
- * Fix "mandos FTBFS on buildds": add build-dependency on locales-all and
- pass LC_ALL to dh_auto_build to make sure we have and use the en_US.UTF-8
- locale for manpage creation.
- (Closes: #656178)
-
- -- gregor herrmann Tue, 31 Jan 2012 17:56:05 +0100
-
-mandos (1.5.3-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 15 Jan 2012 22:05:54 +0100
-
-mandos (1.5.2-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 08 Jan 2012 11:17:20 +0100
-
-mandos (1.5.1-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 01 Jan 2012 21:53:31 +0100
-
-mandos (1.5.0-1) unstable; urgency=low
-
- * New upstream release.
- * debian/control (mandos-client/Depends): Added "initramfs-tools".
- * debian/mandos-client.README.Debian: Corrected mail address and adjust
- wording.
- * debian/rules (binary-common): Exclude new nework-hooks.d directory
- from dh_fixperms.
- * debian/mandos-client.README.Debian: Document network hook facility.
- * debian/mandos-client.docs (network-hooks.d): Added.
- * debian/mandos.dirs (var/lib/mandos): Added.
- * debian/mandos.postinst: Fix ownership of /var/lib/mandos.
- * debian/control (mandos/Depends): Added "python-gnupginterface".
-
- -- Teddy Hogeborn Sun, 01 Jan 2012 05:58:11 +0100
-
-mandos (1.4.1-1) unstable; urgency=low
-
- * New upstream release.
- * debian/control (Build-Depends): Added "man".
- * debian/control (Conflicts): Changed to "Breaks:".
- * debian/copyright: Updated format.
- * debian/mandos-client.postinst: Use "set -e" instead of "#!/bin/sh -e".
- * debian/mandos-client.postrm: - '' -
- * debian/mandos.postinst: - '' -
- * debian/mandos.prerm: Consistent magic.
-
- -- Björn Påhlsson Sat, 15 Oct 2011 18:18:52 +0200
-
-mandos (1.4.0-1) unstable; urgency=low
-
- * New upstream release.
- * Fix "FTBFS with binutils-gold": Added "-Xlinker --as-needed" to
- LDFLAGS in Makefile. (Closes: #632145)
- * Fix "/run transition: uses obsolete /dev/.initramfs": Try both old and
- new PID file locations. (Closes: #643554)
- * debian/source/local-options: New; contains "--single-debian-patch".
- * debian/control (Standards-Version): Upgraded to "3.9.2".
- (DM-Upload-Allowed): New; set to "yes".
- * debian/control: Changed domain from "fukt.bsnet.se" to "recompile.se".
- * debian/copyright: - '' -
- * debian/mandos-client.README.Debian: - '' -
- * debian/mandos.README.Debian: - '' -
- * debian/watch: - '' -
- * debian/control (mandos/Description): Fix language to placate lintian.
-
- -- Teddy Hogeborn Sun, 09 Oct 2011 19:15:08 +0200
-
-mandos (1.3.1-1) unstable; urgency=low
-
- * New upstream release.
- * Conflict with correct version of dropbear.
- * New version uses argparse; depend on python (<=2.7) | python-argparse.
-
- -- Teddy Hogeborn Wed, 27 Jul 2011 19:47:17 +0200
-
-mandos (1.3.0-1) unstable; urgency=low
-
- * New upstream release.
- * debian/control (mandos): Depend on Python 2.6, remove dependency on
- python-multiprocessing.
- (mandos-client): Conflict with dropbear (<< 0.52-5).
- * debian/mandos-client.postrm (purge): Bug fix: update initramfs also on
- purge.
- * debian/mandos-client.lintian-overrides: Added plugins.d/plymouth.
-
- -- Teddy Hogeborn Tue, 08 Mar 2011 20:22:57 +0100
-
-mandos (1.2.3-1) experimental; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Mon, 11 Oct 2010 19:37:31 +0200
-
-mandos (1.2.2-1) experimental; urgency=low
-
- * New upstream release.
- * plugins.d/splashy.c: Only use ELIBBAD if defined. (Closes: #599256)
-
- -- Teddy Hogeborn Thu, 07 Oct 2010 20:27:54 +0200
-
-mandos (1.2.1-3) experimental; urgency=low
-
- * debian/changelog: Include entry for NMU of version 1.0.14-1.1.
-
- -- Teddy Hogeborn Tue, 05 Oct 2010 20:58:38 +0200
-
-mandos (1.2.1-2) unstable; urgency=low
-
- * debian/source/format: New; contains "3.0 (quilt)". Really.
-
- -- Björn Påhlsson Sat, 02 Oct 2010 19:46:59 +0200
-
-mandos (1.2.1-1) unstable; urgency=low
-
- * New upstream release.
- * debian/source/format: New; contains "3.0 (quilt)".
-
- -- Björn Påhlsson Sat, 02 Oct 2010 19:03:58 +0200
-
-mandos (1.2-1) unstable; urgency=low
-
- * New upstream release.
- * Makefile (LINK_FORTIFY_LD): Remove "-fPIE". (Closes: #557076)
- * debian/control: Add gnupg dependency to "mandos-client" and removed it
- from "mandos". Added dependency on "python-urwid" "mandos" since the
- new "mandos-monitor" utility needs it, and on "python (>=2.6) |
- python-multiprocessing" since the Mandos server now uses it.
- * debian/rules: Set BROKEN_PIE on mips and mipsel if a known buggy
- version of binutils is used.
- * debian/mandos.docs: Also install "/usr/share/doc/mandos/DBUS-API".
- * debian/mandos.dirs: Added "etc/dbus-1/system.d".
- * debian/mandos-client.README.Debian: Update info about DEVICE setting
- of initramfs.conf.
- * debian/mandos-client.README.Debian: Remove warning about --connect not
- looping, since it now does.
-
- -- Teddy Hogeborn Tue, 28 Sep 2010 20:46:11 +0200
-
-mandos (1.0.14-1.1) unstable; urgency=low
-
- * Non-maintainer upload.
- * Rebuild against libavahi-core-dev (>= 0.6.26-1).
-
- -- Michael Biebl Mon, 12 Jul 2010 16:34:34 +0200
-
-mandos (1.0.14-1) unstable; urgency=low (HIGH on mips and mipsel)
-
- * New upstream release.
- * debian/rules: Build with BROKEN_PIE set on mips and mipsel
- architectures - fixes FTBFS there.
-
- -- Teddy Hogeborn Sun, 25 Oct 2009 20:10:09 +0100
-
-mandos (1.0.13-1) unstable; urgency=high
-
- * New upstream release.
- * Do not copy unnecessary files to initrd (Closes: #551907)
-
- -- Teddy Hogeborn Thu, 22 Oct 2009 00:53:21 +0200
-
-mandos (1.0.12-1) unstable; urgency=low
-
- * New upstream release.
- * init.d-mandos: Correct dependencies (Closes: #546928)
- * debian/control (Standards-Version): Changed to "3.8.3".
- * debian/mandos-client.README.Debian: Improved wording and formatting.
- Updated location of nfsroot.txt.
- * debian/mandos.README.Debian: Improved wording and formatting.
- * debian/mandos-client.postinst (configure): Don't look for user and
- group with the old name if upgrading from a new enough version.
- * debian/mandos.postinst (configure): - '' -
- * debian/mandos-client.README.Debian: Added text about non-usability of
- pseudo-network interfaces.
-
- -- Teddy Hogeborn Thu, 17 Sep 2009 15:03:59 +0200
-
-mandos (1.0.11-1) unstable; urgency=low
-
- * debian/control (Standards-Version): Changed to "3.8.1".
- * Makefile (GNUTLS_CFLAGS, GNUTLS_CFLAGS): Use "pkg-config" instead of
- the old "libgnutls-config" script. (Closes: #529836)
-
- -- Teddy Hogeborn Sat, 23 May 2009 07:12:20 +0200
-
-mandos (1.0.10-1) unstable; urgency=low
-
- * New upstream release.
- * debian/mandos-client.postinst (update_initramfs): Fix permissions of
- old initrd.img-*.bak files.
-
- -- Teddy Hogeborn Sun, 17 May 2009 04:56:35 +0200
-
-mandos (1.0.9-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 17 May 2009 02:59:45 +0200
-
-mandos (1.0.8-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Wed, 25 Feb 2009 02:26:57 +0100
-
-mandos (1.0.7-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Tue, 24 Feb 2009 12:58:06 +0100
-
-mandos (1.0.6-1) unstable; urgency=low
-
- * New upstream release.
- * debian/mandos-client.postinst: Converted to Bourne shell. Also
- minor message change.
- * debian/mandos-client.postrm: Minor message change.
- * debian/mandos.postinst: Converted to Bourne shell. Also minor
- message change.
- * debian/mandos.prerm: Minor message change.
- * debian/rules (install-indep): Removed "--no-start" from
- dh_installinit.
- * debian/mandos-client.lintian-overrides: Remove obsolete override for
- unbreakable line in plugin-runner manual page.
- * debian/control (mandos/Depends): Added "python-gobject".
- * debian/mandos-client.dirs: Change
- "usr/share/initramfs-tools/scripts/local-top" to
- "usr/share/initramfs-tools/scripts/init-premount".
- * debian/mandos-client.README.Debian: Add reference to initramfs.conf
- and nfsroot.txt. New section about the new non-local connection
- feature.
-
- -- Teddy Hogeborn Fri, 13 Feb 2009 09:27:25 +0100
-
-mandos (1.0.5-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sat, 17 Jan 2009 02:26:00 +0100
-
-mandos (1.0.4-1) unstable; urgency=low
-
- * New upstream release.
- * debian/watch: New file.
- * debian/mandos-client.README.Debian: Document new "mandos=off" kernel
- parameter.
-
- -- Teddy Hogeborn Thu, 15 Jan 2009 05:49:22 +0100
-
-mandos (1.0.3-2) unstable; urgency=low
-
- * Removed some now-unused debconf files.
- * Changed postinst scripts to not source debconf/confmodule.
- * Removed po-debconf from build-depends.
-
- -- Teddy Hogeborn Tue, 06 Jan 2009 21:28:20 +0100
-
-mandos (1.0.3-1) unstable; urgency=low
-
- * New upstream release.
- * Add -Xlinker to linker flags to fix FTBFS for some architectures.
- Thanks to Thiemo Seufer for the report and
- fix. (Closes: #509398)
- * Remove debconf use altogether, thereby stopping debconf abuse. Thanks
- to Christian Perrier . (Closes: #509653)
- * Add NEWS file to /usr/share/doc directories.
- * Use and create "_mandos" user+group. Rename old user+group created by
- older versions of this package.
- * Fix manual pages by adding build-depend on "docbook-xml".
-
- -- Teddy Hogeborn Tue, 06 Jan 2009 01:21:20 +0100
-
-mandos (1.0.2-1) unstable; urgency=low
-
- * New upstream release.
- * debian/copyright: Rewritten to conform to
- .
-
- -- Teddy Hogeborn Fri, 17 Oct 2008 20:42:12 +0200
-
-mandos (1.0.1-1) unstable; urgency=low
-
- * New upstream release.
- * Separate /usr/share/doc/mandos-client/README.Debian into sections with
- headlines. Add instructions on how to test the server and verify the
- password.
-
- -- Teddy Hogeborn Tue, 07 Oct 2008 23:07:23 +0200
-
-mandos (1.0-2) unstable; urgency=low
-
- * Added comments in debian/*.lintian-overrides files. Added Debian
- revison number to version number.
-
- -- Teddy Hogeborn Wed, 01 Oct 2008 17:23:35 +0200
-
-mandos (1.0-1) unstable; urgency=low
-
- * Initial Release. (Closes: #500727).
-
- -- Teddy Hogeborn Tue, 30 Sep 2008 21:58:43 +0200
=== removed file 'debian/compat'
--- debian/compat 2018-02-06 20:03:50 +0000
+++ debian/compat 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-10
=== removed file 'debian/control'
--- debian/control 2019-02-09 23:23:26 +0000
+++ debian/control 1970-01-01 00:00:00 +0000
@@ -1,67 +0,0 @@
-Source: mandos
-Section: admin
-Priority: optional
-Maintainer: Mandos Maintainers
-Uploaders: Teddy Hogeborn ,
- Björn Påhlsson
-Build-Depends: debhelper (>= 10), docbook-xml, docbook-xsl,
- libavahi-core-dev, libgpgme-dev | libgpgme11-dev,
- libgnutls28-dev (>= 3.3.0) | gnutls-dev (>= 3.3.0),
- libgnutls28-dev (<< 3.6.0) | libgnutls30 (<< 3.6.0)
- | libgnutls30 (>= 3.6.6),
- xsltproc, pkg-config, libnl-route-3-dev
-Build-Depends-Indep: systemd, python (>= 2.7), python (<< 3),
- python-dbus, python-gi
-Standards-Version: 4.2.1
-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
-Rules-Requires-Root: binary-targets
-
-Package: mandos
-Architecture: all
-Depends: ${misc:Depends}, python (>= 2.7), python (<< 3),
- libgnutls28-dev (>= 3.3.0) | libgnutls30 (>= 3.3.0),
- libgnutls28-dev (<< 3.6.0) | libgnutls30 (<< 3.6.0)
- | libgnutls30 (>= 3.6.6),
- python-dbus, python-gi, avahi-daemon, adduser, python-urwid,
- gnupg2 | gnupg, systemd-sysv | lsb-base (>= 3.0-6)
-Recommends: ssh-client | 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
- same time be capable of remote and/or unattended reboots.
- .
- The computers run a small client program in the initial RAM
- disk environment which will communicate with a server over a
- network. All network communication is encrypted using TLS.
- The clients are identified by the server using a TLS public
- key; each client has one unique to it. The server sends the
- clients an encrypted password. The encrypted password is
- decrypted by the clients using an OpenPGP key, and the
- password is then used to unlock the root file system,
- whereupon the computers can continue booting normally.
-
-Package: mandos-client
-Architecture: linux-any
-Depends: ${shlibs:Depends}, ${misc:Depends}, adduser,
- cryptsetup (<< 2:2.0.3-1) | cryptsetup-initramfs,
- initramfs-tools (>= 0.99), dpkg-dev (>=1.16.0),
- gnutls-bin (>= 3.6.6) | openssl (>= 1.1.0)
-Recommends: ssh
-Breaks: dropbear (<= 0.53.1-1)
-Enhances: cryptsetup
-Description: do unattended reboots with an encrypted root file system
- This is the client part of the Mandos system, which allows
- computers to have encrypted root file systems and at the
- same time be capable of remote and/or unattended reboots.
- .
- The computers run a small client program in the initial RAM
- disk environment which will communicate with a server over a
- network. All network communication is encrypted using TLS.
- The clients are identified by the server using a TLS public
- key; each client has one unique to it. The server sends the
- clients an encrypted password. The encrypted password is
- decrypted by the clients using an OpenPGP key, and the
- password is then used to unlock the root file system,
- whereupon the computers can continue booting normally.
=== removed file 'debian/copyright'
--- debian/copyright 2018-02-08 10:23:55 +0000
+++ debian/copyright 1970-01-01 00:00:00 +0000
@@ -1,26 +0,0 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: Mandos
-Upstream-Contact: Mandos
-Source:
-
-Files: *
-Copyright: Copyright © 2008-2018 Teddy Hogeborn
- Copyright © 2008-2018 Björn Påhlsson
-License: GPL-3+
- This file is part of Mandos.
- .
- Mandos 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.
- .
- Mandos 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 Mandos. If not, see .
- .
- On Debian systems, the complete text of the GNU General Public
- License can be found in "/usr/share/common-licenses/GPL-3".
=== removed file 'debian/mandos-client.README.Debian'
--- debian/mandos-client.README.Debian 2019-02-09 23:23:26 +0000
+++ debian/mandos-client.README.Debian 1970-01-01 00:00:00 +0000
@@ -1,111 +0,0 @@
-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
- boot time. This password must be a one which can be used to unlock
- the root file system device. On the *client*, run this command:
-
- mandos-keygen --password
-
- It will prompt for a password and output a config file section.
- This output should be copied to the Mandos server and added to the
- file "/etc/mandos/clients.conf" there.
-
-* Testing that it Works (Without Rebooting)
-
- After the server has been started with this client's key added, it
- 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 \
- --pubkey=/etc/keys/mandos/pubkey.txt \
- --seckey=/etc/keys/mandos/seckey.txt \
- --tls-privkey=/etc/keys/mandos/tls-privkey.pem \
- --tls-pubkey=/etc/keys/mandos/tls-pubkey.pem; echo
-
- This command should retrieve the password from the server, decrypt
- it, and output it to standard output. There it can be verified to
- be the correct password, before rebooting.
-
-* Emergency Escape
-
- If it ever should be necessary, the Mandos client can be temporarily
- prevented from running at startup by passing the parameter
- "mandos=off" to the kernel.
-
-* Specifying a Client Network Interface
-
- At boot time the network interfaces to use will by default be
- 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
- setting is changed, it will be necessary to update the initrd image
- by running this command:
-
- update-initramfs -k all -u
-
- The device can also be overridden at boot time on the Linux kernel
- command line using the sixth colon-separated field of the "ip="
- option; for exact syntax, read the documentation in the file
- "/usr/share/doc/linux-doc-*/Documentation/filesystems/nfs/nfsroot.txt",
- available in the "linux-doc-*" package.
-
- Note that since the network interfaces are used in the initial RAM
- disk environment, the network interfaces *must* exist at that stage.
- Thus, an interface can *not* be a pseudo-interface such as "br0" or
- "tun0"; instead, only real interfaces (such as "eth0") can be used.
- This can be overcome by writing a "network hook" program to create
- an interface (see mandos-client(8mandos)) and placing it in
- "/etc/mandos/network-hooks.d", from where it will be copied into the
- initial RAM disk. Example network hook scripts can be found in
- "/usr/share/doc/mandos-client/examples/network-hooks.d".
-
-* User-Supplied Plugins
-
- Any plugins found in "/etc/mandos/plugins.d" will override and add
- to the normal Mandos plugins. When adding or changing plugins, do
- not forget to update the initital RAM disk image:
-
- update-initramfs -k all -u
-
-* Do *NOT* Edit "/etc/crypttab"
-
- It is NOT necessary to edit "/etc/crypttab" to specify
- "/usr/lib/mandos/plugin-runner" as a keyscript for the root file
- system; if no keyscript is given for the root file system, the
- Mandos client will be the new default way for getting a password for
- the root file system when booting.
-
-* Non-local Connection (Not Using ZeroConf)
-
- If the "ip=" kernel command line option is used to specify a
- complete IP address and device name, as noted above, it then becomes
- possible to specify a specific IP address and port to connect to,
- instead of using ZeroConf. The syntax for doing this is
- "mandos=connect::" on the kernel command
- line.
-
- For very advanced users, it is 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 , Sat, 9 Feb 2019 15:08:04 +0100
=== removed file 'debian/mandos-client.dirs'
--- debian/mandos-client.dirs 2018-08-19 14:32:00 +0000
+++ debian/mandos-client.dirs 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-usr/share/man/man8
-usr/sbin
-usr/share/initramfs-tools/hooks
-usr/share/initramfs-tools/conf.d
-usr/share/initramfs-tools/scripts/init-premount
-usr/share/initramfs-tools/scripts/local-premount
=== removed file 'debian/mandos-client.docs'
--- debian/mandos-client.docs 2012-06-01 21:48:12 +0000
+++ debian/mandos-client.docs 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-NEWS
-README
-TODO
=== removed file 'debian/mandos-client.examples'
--- debian/mandos-client.examples 2012-06-01 21:48:12 +0000
+++ debian/mandos-client.examples 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-network-hooks.d
=== removed file 'debian/mandos-client.links'
--- debian/mandos-client.links 2008-09-19 13:50:22 +0000
+++ debian/mandos-client.links 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-usr/share/man/man8/plugin-runner.8mandos.gz usr/share/man/man5/plugin-runner.conf.5mandos.gz
=== removed file 'debian/mandos-client.lintian-overrides'
--- debian/mandos-client.lintian-overrides 2016-03-19 04:21:00 +0000
+++ debian/mandos-client.lintian-overrides 1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-# This directory contains secret client key files.
-#
-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
-# 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
-
-# These binaries must be setuid root, since they need root powers, but
-# are started by plugin-runner(8mandos), which runs all plugins as
-# user/group "_mandos". These binaries are not run in a running
-# 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
-
-# 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.
-#
-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
=== removed file 'debian/mandos-client.postinst'
--- debian/mandos-client.postinst 2019-02-09 23:23:26 +0000
+++ debian/mandos-client.postinst 1970-01-01 00:00:00 +0000
@@ -1,160 +0,0 @@
-#!/bin/sh
-# This script can be called in the following ways:
-#
-# After the package was installed:
-# configure
-#
-#
-# If prerm fails during upgrade or fails on failed upgrade:
-# abort-upgrade
-#
-# If prerm fails during deconfiguration of a package:
-# abort-deconfigure in-favour
-# removing
-#
-# If prerm fails during replacement due to conflict:
-# abort-remove in-favour
-
-set -e
-
-# Update the initial RAM file system image
-update_initramfs()
-{
- update-initramfs -u -k all
-
- if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then
- # Make old initrd.img files unreadable too, in case they were
- # created with mandos-client 1.0.8 or older.
- find /boot -maxdepth 1 -type f -name "initrd.img-*.bak" \
- -print0 | xargs --null --no-run-if-empty chmod o-r
- fi
-}
-
-# Add user and group
-add_mandos_user(){
- # Rename old "mandos" user and group
- if dpkg --compare-versions "$2" lt "1.0.3-1"; then
- case "`getent passwd mandos`" in
- *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
- usermod --login _mandos mandos
- groupmod --new-name _mandos mandos
- return
- ;;
- esac
- fi
- # Create new user and group
- if ! getent passwd _mandos >/dev/null; then
- adduser --system --force-badname --quiet --home /nonexistent \
- --no-create-home --group --disabled-password \
- --gecos "Mandos password system" _mandos
- fi
-}
-
-# Create client key pairs
-create_keys(){
- # If the OpenPGP key files do not exist, generate all keys using
- # mandos-keygen
- if ! [ -r /etc/keys/mandos/pubkey.txt \
- -a -r /etc/keys/mandos/seckey.txt ]; then
- mandos-keygen
- gpg-connect-agent KILLAGENT /bye || :
- return 0
- fi
-
- # If the TLS keys already exists, do nothing
- if [ -r /etc/keys/mandos/tls-privkey.pem \
- -a -r /etc/keys/mandos/tls-pubkey.pem ]; then
- return 0
- fi
-
- # If this is an upgrade from an old installation, the TLS keys
- # will not exist; create them.
-
- # First try certtool from GnuTLS
- if ! certtool --generate-privkey --password='' \
- --outfile /etc/keys/mandos/tls-privkey.pem \
- --sec-param ultra --key-type=ed25519 --pkcs8 --no-text \
- 2>/dev/null; then
- # Otherwise try OpenSSL
- if ! openssl genpkey -algorithm X25519 \
- -out /etc/keys/mandos/tls-privkey.pem; then
- rm --force /etc/keys/mandos/tls-privkey.pem
- # None of the commands succeded; give up
- return 1
- fi
- fi
-
- local umask=$(umask)
- umask 077
- # First try certtool from GnuTLS
- if ! certtool --password='' \
- --load-privkey=/etc/keys/mandos/tls-privkey.pem \
- --outfile=/etc/keys/mandos/tls-pubkey.pem --pubkey-info \
- --no-text 2>/dev/null; then
- # Otherwise try OpenSSL
- if ! openssl pkey -in /etc/keys/mandos/tls-privkey.pem \
- -out /etc/keys/mandos/tls-pubkey.pem -pubout; then
- rm --force /etc/keys/mandos/tls-pubkey.pem
- # None of the commands succeded; give up
- umask $umask
- return 1
- fi
- fi
- umask $umask
-}
-
-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"
-}
-
-case "$1" in
- configure)
- add_mandos_user "$@"
- create_keys "$@"
- 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)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed file 'debian/mandos-client.postrm'
--- debian/mandos-client.postrm 2019-02-09 23:23:26 +0000
+++ debian/mandos-client.postrm 1970-01-01 00:00:00 +0000
@@ -1,63 +0,0 @@
-#!/bin/sh
-# This script can be called in the following ways:
-#
-# After the package was removed:
-# remove
-#
-# After the package was purged:
-# purge
-#
-# After the package was upgraded:
-# upgrade
-# if that fails:
-# failed-upgrade
-#
-#
-# After all of the packages files have been replaced:
-# disappear
-#
-#
-# If preinst fails during install:
-# abort-install
-#
-# If preinst fails during upgrade of removed package:
-# abort-install
-#
-# If preinst fails during upgrade:
-# abort-upgrade
-
-set -e
-
-# Update the initial RAM file system image
-update_initramfs()
-{
- update-initramfs -u -k all || :
-}
-
-case "$1" in
- remove)
- update_initramfs
- ;;
-
- purge)
- 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/tls-privkey.pem \
- /etc/keys/mandos/tls-pubkey.pem \
- /etc/keys/mandos/dhparams.pem 2>/dev/null
- update_initramfs
- ;;
- upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed file 'debian/mandos.README.Debian'
--- debian/mandos.README.Debian 2011-10-05 16:00:56 +0000
+++ debian/mandos.README.Debian 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-The Mandos server is useless without at least one configured client in
-/etc/mandos/clients.conf. To create one, install the "mandos-client"
-package on a client computer, and, on the client, run the command
-
- # mandos-keygen --password
-
-to get a config file stanza. Append the output of that command to the
-file "/etc/mandos/clients.conf" on the Mandos server computer.
-
- -- Teddy Hogeborn , Wed, 5 Oct 2011 17:51:22 +0200
=== removed file 'debian/mandos.dirs'
--- debian/mandos.dirs 2016-03-19 12:10:15 +0000
+++ debian/mandos.dirs 1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
-usr/share/man/man5
-usr/share/man/man8
-etc/init.d
-etc/default
-etc/dbus-1/system.d
-usr/sbin
-var/lib/mandos
-lib/systemd/system
-usr/lib/tmpfiles.d
=== removed file 'debian/mandos.docs'
--- debian/mandos.docs 2010-09-12 03:00:40 +0000
+++ debian/mandos.docs 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
-NEWS
-README
-TODO
-DBUS-API
=== removed file 'debian/mandos.lintian-overrides'
--- debian/mandos.lintian-overrides 2018-02-10 18:58:32 +0000
+++ debian/mandos.lintian-overrides 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-# This config file will normally have encrypted secret client keys in
-# 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 46)
=== removed file 'debian/mandos.postinst'
--- debian/mandos.postinst 2016-03-19 03:48:56 +0000
+++ debian/mandos.postinst 1970-01-01 00:00:00 +0000
@@ -1,79 +0,0 @@
-#!/bin/sh
-# This script can be called in the following ways:
-#
-# After the package was installed:
-# configure
-#
-#
-# If prerm fails during upgrade or fails on failed upgrade:
-# abort-upgrade
-#
-# If prerm fails during deconfiguration of a package:
-# abort-deconfigure in-favour
-# removing
-#
-# If prerm fails during replacement due to conflict:
-# abort-remove in-favour
-
-set -e
-
-case "$1" in
- configure)
- # Rename old "mandos" user and group
- if dpkg --compare-versions "$2" lt "1.0.3-1"; then
- case "`getent passwd mandos`" in
- *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
- usermod --login _mandos mandos
- groupmod --new-name _mandos mandos
- ;;
- esac
- fi
- # Create new user and group
- if ! getent passwd _mandos >/dev/null; then
- adduser --system --force-badname --quiet \
- --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
- ;;
-
- abort-upgrade|abort-deconfigure|abort-remove)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-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
=== removed file 'debian/mandos.prerm'
--- debian/mandos.prerm 2015-07-12 01:57:54 +0000
+++ debian/mandos.prerm 1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-#!/bin/sh
-# prerm script for mandos
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * 'remove'
-# * 'upgrade'
-# * 'failed-upgrade'
-# * 'remove' 'in-favour'
-# * 'deconfigure' 'in-favour'
-# 'removing'
-#
-# for details, see /usr/share/doc/packaging-manual/
-
-case "$1" in
- remove|deconfigure)
- invoke-rc.d mandos stop || :
- ;;
- upgrade|failed-upgrade)
- ;;
- *)
- echo "prerm called with unknown argument '$1'" >&2
- exit 0
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed directory 'debian/po'
=== removed file 'debian/rules'
--- debian/rules 2018-02-08 12:25:31 +0000
+++ debian/rules 1970-01-01 00:00:00 +0000
@@ -1,45 +0,0 @@
-#!/usr/bin/make -f
-
-ifeq (,$(filter noopt,$(DEB_BUILD_OPTIONS)))
- MAKEFLAGS += OPTIMIZE=-O0
-endif
-
-ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
- NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
- MAKEFLAGS += -j$(NUMJOBS)
-endif
-
-%:
- dh $@
-
-override_dh_auto_build-arch:
- LC_ALL=en_US.utf8 dh_auto_build -- all doc
-
-override_dh_auto_build-indep:
- LC_ALL=en_US.utf8 dh_auto_build -- doc
-
-override_dh_installinit-indep:
- dh_installinit --onlyscripts \
- --update-rcd-params="defaults 25 15"
-
-override_dh_auto_install-indep:
- $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server
-
-override_dh_auto_install-arch:
- $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client \
- install-client-nokey
-
-override_dh_fixperms-arch:
- dh_fixperms --exclude etc/keys/mandos \
- --exclude etc/mandos/plugins.d \
- --exclude etc/mandos/plugin-helpers \
- --exclude usr/lib/$(DEB_HOST_MULTIARCH)/mandos/plugins.d \
- --exclude usr/lib/$(DEB_HOST_MULTIARCH)/mandos/plugin-helpers \
- --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/source'
=== removed file 'debian/source/format'
--- debian/source/format 2010-10-02 17:41:05 +0000
+++ debian/source/format 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-3.0 (quilt)
=== removed file 'debian/source/local-options'
--- debian/source/local-options 2011-10-08 21:13:46 +0000
+++ debian/source/local-options 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
---single-debian-patch
=== 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-----
=== removed file 'debian/watch'
--- debian/watch 2018-02-08 10:02:51 +0000
+++ debian/watch 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-version=4
-opts=pgpmode=auto \
- https://ftp.recompile.se/pub/@PACKAGE@/@PACKAGE@@ANY_VERSION@(?:\.orig)?@ARCHIVE_EXT@
=== removed file 'default-mandos'
--- default-mandos 2008-09-17 00:34:09 +0000
+++ default-mandos 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-# Directory where configuration files are located. Default is
-# "/etc/mandos".
-#
-#CONFIGDIR=/etc/mandos
-
-# Additional options that are passed to the Daemon.
-DAEMON_ARGS=""
=== removed file 'init.d-mandos'
--- init.d-mandos 2018-02-10 13:23:58 +0000
+++ init.d-mandos 1970-01-01 00:00:00 +0000
@@ -1,164 +0,0 @@
-#! /bin/sh
-### BEGIN INIT INFO
-# Provides: mandos
-# Required-Start: $remote_fs $syslog avahi-daemon
-# Required-Stop: $remote_fs $syslog avahi-daemon
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: Mandos server
-# Description: Server of encrypted passwords to Mandos clients
-### END INIT INFO
-
-# Author: Teddy Hogeborn
-# Author: Björn Påhlsson
-
-# Do NOT "set -e"
-
-# PATH should only include /usr/* if it runs after the mountnfs.sh script
-PATH=/sbin:/usr/sbin:/bin:/usr/bin
-DESC="Mandos root file system password server"
-NAME=mandos
-DAEMON=/usr/sbin/$NAME
-DAEMON_ARGS=""
-if [ -d /run/. ]; then
- PIDFILE=/run/$NAME.pid
-else
- PIDFILE=/var/run/$NAME.pid
-fi
-SCRIPTNAME=/etc/init.d/$NAME
-
-# Exit if the package is not installed
-[ -x "$DAEMON" ] || exit 0
-
-# Read configuration variable file if it is present
-[ -r /etc/default/$NAME ] && . /etc/default/$NAME
-
-if [ -n "$CONFIGDIR" ]; then
- DAEMON_ARGS="$DAEMON_ARGS --configdir $CONFIGDIR"
-fi
-
-# Load the VERBOSE setting and other rcS variables
-. /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.
-. /lib/lsb/init-functions
-
-#
-# Function that starts the daemon/service
-#
-do_start()
-{
- # Return
- # 0 if daemon has been started
- # 1 if daemon was already running
- # 2 if daemon could not be started
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
- || return 1
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
- $DAEMON_ARGS \
- || return 2
- # Add code here, if necessary, that waits for the process to be ready
- # to handle requests from services started subsequently which depend
- # on this one. As a last resort, sleep for some time.
-}
-
-#
-# Function that stops the daemon/service
-#
-do_stop()
-{
- # Return
- # 0 if daemon has been stopped
- # 1 if daemon was already stopped
- # 2 if daemon could not be stopped
- # other if a failure occurred
- start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
- RETVAL="$?"
- [ "$RETVAL" = 2 ] && return 2
- # Wait for children to finish too if this is a daemon that forks
- # and if the daemon is only ever run from this initscript.
- # If the above conditions are not satisfied then add some other code
- # that waits for the process to drop all resources that could be
- # needed by services started subsequently. A last resort is to
- # sleep for some time.
- start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
- [ "$?" = 2 ] && return 2
- # Many daemons don't delete their pidfiles when they exit.
- rm -f $PIDFILE
- return "$RETVAL"
-}
-
-#
-# Function that sends a SIGHUP to the daemon/service
-#
-do_reload() {
- #
- # If the daemon can reload its configuration without
- # restarting (for example, when it is sent a SIGHUP),
- # then implement that here.
- #
- start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
- return 0
-}
-
-case "$1" in
- start)
- [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
- do_start
- case "$?" in
- 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
- 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
- esac
- ;;
- stop)
- [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
- do_stop
- case "$?" in
- 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
- 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
- # and leave 'force-reload' as an alias for 'restart'.
- #
- #log_daemon_msg "Reloading $DESC" "$NAME"
- #do_reload
- #log_end_msg $?
- #;;
- restart|force-reload)
- #
- # If the "reload" option is implemented then remove the
- # 'force-reload' alias
- #
- log_daemon_msg "Restarting $DESC" "$NAME"
- do_stop
- case "$?" in
- 0|1)
- do_start
- case "$?" in
- 0) log_end_msg 0 ;;
- 1) log_end_msg 1 ;; # Old process is still running
- *) log_end_msg 1 ;; # Failed to start
- esac
- ;;
- *)
- # Failed to stop
- log_end_msg 1
- ;;
- esac
- ;;
- *)
- #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
- echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
- exit 3
- ;;
-esac
-
-:
=== removed file 'initramfs-tools-conf'
--- initramfs-tools-conf 2018-08-19 14:06:55 +0000
+++ initramfs-tools-conf 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
-# -*- shell-script -*-
-
-# Since the initramfs image will contain key files, we need to
-# restrict permissions on it by setting UMASK here.
-#
-# The proper place to set UMASK is (according to
-# /etc/cryptsetup-initramfs/conf-hook), in
-# /etc/initramfs-tools/initramfs.conf, which we shouldn't edit. The
-# corresponding directory for drop-in files from packages is
-# /usr/share/initramfs-tools/conf.d, and this file will be installed
-# there as "mandos-conf".
-#
-# This setting of UMASK will have unfortunate unintended side effects
-# on the files *inside* the initramfs, but these are later fixed by
-# "initramfs-tools-hook", installed as
-# "/usr/share/initramfs-tools/hooks/mandos".
-UMASK=0027
=== modified file 'initramfs-tools-hook'
--- initramfs-tools-hook 2018-08-19 14:06:55 +0000
+++ initramfs-tools-hook 2008-08-24 23:18:18 +0000
@@ -3,7 +3,7 @@
# This script will be run by 'mkinitramfs' when it creates the image.
# Its job is to decide which files to install, then install them into
# the staging area, where the initramfs is being created. This
-# happens when a new 'linux-image' package is installed, or when an
+# happens when a new 'linux-image' package is installed, or when the
# administrator runs 'update-initramfs' by hand to update an initramfs
# image.
@@ -29,38 +29,15 @@
. /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"
- break
- fi
-done
-if [ -z "$libdir" ]; then
+if [ -d /usr/lib/mandos ]; then
+ prefix=/usr
+elif [ -d /usr/local/lib/mandos ]; then
+ prefix=/usr/local
+else
# Mandos not found
exit 1
fi
-for d in /etc/keys/mandos /etc/mandos/keys; do
- if [ -d "$d" ]; then
- keydir="$d"
- break
- fi
-done
-if [ -z "$keydir" ]; then
- # Mandos key directory not found
- exit 1
-fi
-
-set `{ getent passwd _mandos \
- || getent passwd nobody \
- || echo ::65534:65534:::; } \
- | cut --delimiter=: --fields=3,4 --only-delimited \
- --output-delimiter=" "`
-mandos_user="$1"
-mandos_group="$2"
-
# The Mandos network client uses the network
auto_add_modules net
# The Mandos network client uses IPv6
@@ -70,50 +47,26 @@
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}"
-
-copy_exec "$libdir"/mandos/mandos-to-cryptroot-unlock "${MANDOSDIR}"
+mkdir --parents "${DESTDIR}${CONFDIR}"
+mkdir --parents "${DESTDIR}${PLUGINDIR}"
# Copy the Mandos plugin runner
-copy_exec "$libdir"/mandos/plugin-runner "${MANDOSDIR}"
+copy_exec "$prefix"/lib/mandos/plugin-runner "${DESTDIR}${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
continue
fi
case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
- : ;;
- "*") echo "W: Mandos client plugin directory is empty." >&2 ;;
- *) copy_exec "$file" "${PLUGINDIR}" ;;
- 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}" ;;
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;;
+ *) copy_exec "$file" "${PLUGINDIR}";;
esac
done
@@ -121,127 +74,31 @@
for file in /etc/mandos/plugins.d/*; do
base="`basename \"$file\"`"
case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
- : ;;
- "*") : ;;
- *) copy_exec "$file" "${PLUGINDIR}" ;;
- 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}"
- fi
-done
-export DEVICE
-
-# Copy network hooks
-for hook in /etc/mandos/network-hooks.d/*; do
- case "`basename \"$hook\"`" in
- "*") continue ;;
- *[!A-Za-z0-9_.-]*) continue ;;
- *) test -d "$hook" || copy_exec "$hook" "${HOOKDIR}" ;;
- esac
- 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
- if [ ! -e "${file}" ]; then
- echo "WARNING: file ${file} not found, requested by Mandos network hook '${hook##*/}'" >&2
- fi
- if [ -z "${target}" ]; then
- copy_exec "$file"
- else
- copy_exec "$file" "$target"
- fi
- 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"
- 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
- gpg=/usr/bin/gpg2
-fi
-if [ ! -e "${DESTDIR}$gpg" ]; then
- copy_exec "$gpg"
-fi
-unset gpg
-unset libgpgme11_version
-
-# Config files
-for file in /etc/mandos/plugin-runner.conf; do
+ *~|.*|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;;
+ *) copy_exec "$file" "${PLUGINDIR}";;
+ esac
+done
+
+# GPGME needs /usr/bin/gpg
+if ! [ -e "${DESTDIR}/usr/bin/gpg" ] \
+ && [ -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* 2>/dev/null`" ]; then
+ copy_exec /usr/bin/gpg
+fi
+
+# Key files
+for file in /etc/mandos/*; do
if [ -d "$file" ]; then
continue
fi
cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
done
-
-if [ ${mandos_user} != 65534 ]; then
- sed --in-place --expression="1i--userid=${mandos_user}" \
- "${DESTDIR}${CONFDIR}/plugin-runner.conf"
-fi
-
-if [ ${mandos_group} != 65534 ]; then
- sed --in-place --expression="1i--groupid=${mandos_group}" \
- "${DESTDIR}${CONFDIR}/plugin-runner.conf"
-fi
-
-# Key files
-for file in "$keydir"/*; do
- 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
-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
+# Create key ring files
+gpg --no-random-seed-file --quiet --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --homedir "${DESTDIR}${CONFDIR}" --no-permission-warning \
+ --trust-model always --import-options import-minimal \
+ --import "${DESTDIR}${CONFDIR}/seckey.txt"
+chown nobody "${DESTDIR}${CONFDIR}/secring.gpg"
# /lib/mandos/plugin-runner will drop priviliges, but needs access to
# its plugin directory and its config file. However, since almost all
@@ -252,10 +109,10 @@
# initrd; it is intended to affect the initrd.img file itself, since
# it now contains secret key files. There is, however, no other way
# to set the permission of the initrd.img file without a race
-# condition. This umask is set by "initramfs-tools-conf", installed
-# as "/usr/share/initramfs-tools/conf.d/mandos-conf".)
+# condition. This umask is set by "initramfs-tools-hook-conf",
+# installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".)
#
-for full in "${MANDOSDIR}" "${CONFDIR}"; do
+for full in "${PLUGINDIR}" "${CONFDIR}"; do
while [ "$full" != "/" ]; do
chmod a+rX "${DESTDIR}$full"
full="`dirname \"$full\"`"
@@ -265,13 +122,8 @@
# Reset some other things to sane permissions which we have
# inadvertently affected with our umask setting.
for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do
- if [ -d "${DESTDIR}$dir" ]; then
- chmod a+rX "${DESTDIR}$dir"
- fi
+ chmod a+rX "${DESTDIR}$dir"
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 --
- fi
+for dir in /lib /usr/lib; do
+ chmod --recursive a+rX "${DESTDIR}$dir"
done
=== added file 'initramfs-tools-hook-conf'
--- initramfs-tools-hook-conf 1970-01-01 00:00:00 +0000
+++ initramfs-tools-hook-conf 2008-08-12 19:22:34 +0000
@@ -0,0 +1,1 @@
+UMASK=027
=== modified file 'initramfs-tools-script'
--- initramfs-tools-script 2018-08-19 01:35:11 +0000
+++ initramfs-tools-script 2008-08-14 02:24:59 +0000
@@ -6,176 +6,69 @@
#
# This script should be installed as
-# "/usr/share/initramfs-tools/scripts/init-premount/mandos" which will
-# eventually be "/scripts/init-premount/mandos" in the initrd.img
-# file.
+# "/usr/share/initramfs-tools/scripts/local-top/mandos" which will
+# eventually be "/scripts/local-top/mandos" in the initrd.img file.
-PREREQ="udev"
+# No initramfs pre-requirements; we must instead run BEFORE cryptroot.
+# This is not a problem, since cryptroot forces itself to run LAST.
+PREREQ=""
prereqs()
{
- echo "$PREREQ"
+ echo "$PREREQ"
}
case $1 in
prereqs)
- prereqs
- exit 0
- ;;
-esac
-
-. /scripts/functions
-
-for param in `cat /proc/cmdline`; do
- case "$param" in
- ip=*) IPOPTS="${param#ip=}" ;;
- mandos=*)
- # Split option line on commas
- old_ifs="$IFS"
- IFS="$IFS,"
- for mpar in ${param#mandos=}; do
- IFS="$old_ifs"
- case "$mpar" in
- off) exit 0 ;;
- connect) connect="" ;;
- connect:*) connect="${mpar#connect:}" ;;
- *) log_warning_msg "$0: Bad option ${mpar}" ;;
- esac
- done
- unset mpar
- IFS="$old_ifs"
- unset old_ifs
- ;;
- esac
-done
-unset param
-
-chmod a=rwxt /tmp
-
-# Get DEVICE from /conf/initramfs.conf and other files
-. /conf/initramfs.conf
-for conf in /conf/conf.d/*; do
- [ -f "${conf}" ] && . "${conf}"
-done
-if [ -e /conf/param.conf ]; then
- . /conf/param.conf
-fi
-
-# Override DEVICE from sixth field of ip= kernel option, if passed
-case "$IPOPTS" in
- *:*:*:*:*:*) # At least six fields
- # Remove the first five fields
- device="${IPOPTS#*:*:*:*:*:}"
- # Remove all fields except the first one
- DEVICE="${device%%:*}"
- ;;
-esac
-
-# Add device setting (if any) to plugin-runner.conf
-if [ "${DEVICE+set}" = set ]; then
- # Did we get the device from an ip= option?
- if [ "${device+set}" = set ]; then
- # Let ip= option override local config; append:
- cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
-
- --options-for=mandos-client:--interface=${DEVICE}
-EOF
- else
- # Prepend device setting so any later options would override:
- sed -i -e \
- '1i--options-for=mandos-client:--interface='"${DEVICE}" \
- /conf/conf.d/mandos/plugin-runner.conf
- fi
-fi
-unset device
-
-# 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
-
- --options-for=mandos-client:--connect=${connect}
-EOF
- fi
-fi
-
-if [ -r /conf/conf.d/cryptroot ]; then
- test -w /conf/conf.d
-
- # Do not replace cryptroot file unless we need to.
- replace_cryptroot=no
-
- # Our keyscript
- mandos=/lib/mandos/plugin-runner
- test -x "$mandos"
-
- # parse /conf/conf.d/cryptroot. Format:
- # target=sda2_crypt,source=/dev/sda2,rootdev,key=none,keyscript=/foo/bar/baz
- # Is the root device specially marked?
- changeall=yes
- while read -r options; do
- case "$options" in
- rootdev,*|*,rootdev,*|*,rootdev)
- # If the root device is specially marked, don't change all
- # lines in crypttab by default.
- changeall=no
+ prereqs
+ exit 0
+ ;;
+esac
+
+test -w /conf/conf.d/cryptroot
+
+# Do not replace cryptroot file unless we need to.
+replace_cryptroot=no
+
+# Our keyscript
+mandos=/lib/mandos/plugin-runner
+
+# 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 options; do
+ newopts=""
+ # Split option line on commas
+ old_ifs="$IFS"
+ IFS="$IFS,"
+ for opt in $options; do
+ # Find the keyscript option, if any
+ case "$opt" in
+ keyscript=*)
+ keyscript="${opt#keyscript=}"
+ newopts="$newopts,$opt"
+ ;;
+ "") : ;;
+ *)
+ newopts="$newopts,$opt"
;;
esac
- done < /conf/conf.d/cryptroot
-
- exec 3>/conf/conf.d/cryptroot.mandos
- while read -r options; do
- newopts=""
- keyscript=""
- changethis="$changeall"
- # Split option line on commas
- old_ifs="$IFS"
- IFS="$IFS,"
- for opt in $options; do
- # Find the keyscript option, if any
- case "$opt" in
- keyscript=*)
- keyscript="${opt#keyscript=}"
- newopts="$newopts,$opt"
- ;;
- "") : ;;
- # Always use Mandos on the root device, if marked
- rootdev)
- changethis=yes
- newopts="$newopts,$opt"
- ;;
- # Don't use Mandos on resume device, if marked
- resumedev)
- changethis=no
- newopts="$newopts,$opt"
- ;;
- *)
- newopts="$newopts,$opt"
- ;;
- esac
- done
- IFS="$old_ifs"
- unset old_ifs
- # If there was no keyscript option, add one.
- if [ "$changethis" = yes ] && [ -z "$keyscript" ]; then
- replace_cryptroot=yes
- newopts="$newopts,keyscript=$mandos"
- fi
- newopts="${newopts#,}"
- echo "$newopts" >&3
- done < /conf/conf.d/cryptroot
- exec 3>&-
-
- # If we need to, replace the old cryptroot file with the new file.
- if [ "$replace_cryptroot" = yes ]; then
- mv /conf/conf.d/cryptroot /conf/conf.d/cryptroot.mandos-old
- mv /conf/conf.d/cryptroot.mandos /conf/conf.d/cryptroot
- else
- rm -f /conf/conf.d/cryptroot.mandos
+ done
+ IFS="$old_ifs"
+ unset old_ifs
+ # If there was no keyscript option, add one.
+ if [ -z "$keyscript" ]; then
+ replace_cryptroot=yes
+ newopts="$newopts,keyscript=$mandos"
fi
-elif [ -x /usr/bin/cryptroot-unlock ]; then
- setsid /lib/mandos/mandos-to-cryptroot-unlock &
+ newopts="${newopts#,}"
+ echo "$newopts" >&3
+done < /conf/conf.d/cryptroot
+exec 3>&-
+
+# If we need to, replace the old cryptroot file with the new file.
+if [ "$replace_cryptroot" = yes ]; then
+ mv /conf/conf.d/cryptroot /conf/conf.d/cryptroot.mandos-old
+ mv /conf/conf.d/cryptroot.mandos /conf/conf.d/cryptroot
+else
+ rm /conf/conf.d/cryptroot.mandos
fi
=== removed file 'initramfs-tools-script-stop'
--- initramfs-tools-script-stop 2018-08-19 14:58:40 +0000
+++ initramfs-tools-script-stop 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
-#!/bin/sh -e
-#
-# Script to wait for plugin-runner to exit before continuing boot
-#
-# Copyright © 2018 Teddy Hogeborn
-# Copyright © 2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos 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.
-#
-# Mandos 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 Mandos. If not, see .
-#
-# Contact the authors at .
-#
-# This script will run in the initrd environment at boot and remove
-# the file keeping the dummy plugin running, forcing plugin-runner to
-# exit if it is still running.
-
-# This script should be installed as
-# "/usr/share/initramfs-tools/scripts/local-premount/mandos" which will
-# eventually be "/scripts/local-premount/mandos" in the initrd.img
-# file.
-
-PREREQ=""
-prereqs()
-{
- echo "$PREREQ"
-}
-
-case $1 in
-prereqs)
- prereqs
- exit 0
- ;;
-esac
-
-. /scripts/functions
-
-pid=$(cat /run/mandos-plugin-runner.pid 2>/dev/null)
-
-# If the dummy plugin is running, removing this file should force the
-# dummy plugin to exit successfully, thereby making plugin-runner shut
-# down all its other plugins and then exit itself.
-rm -f /run/mandos-keep-running >/dev/null 2>&1
-
-# Wait for exit of plugin-runner, if still running
-if [ -n "$pid" ]; then
- while :; do
- case "$(readlink /proc/"$pid"/exe 2>/dev/null)" in
- */plugin-runner) sleep 1;;
- *) break;;
- esac
- done
- rm -f /run/mandos-plugin-runner.pid >/dev/null 2>&1
-fi
=== removed file 'initramfs-unpack'
--- initramfs-unpack 2018-02-08 10:23:55 +0000
+++ initramfs-unpack 1970-01-01 00:00:00 +0000
@@ -1,69 +0,0 @@
-#!/bin/bash
-#
-# Initramfs unpacker - unpacks initramfs images into /tmp
-#
-# Copyright © 2013-2018 Teddy Hogeborn
-# Copyright © 2013-2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos 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.
-#
-# Mandos 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 Mandos. If not, see
-# .
-#
-# Contact the authors at .
-
-cpio="cpio --extract --make-directories --unconditional --preserve-modification-time"
-
-if [ -z "$*" ]; then
- set -- /boot/initrd.img-*
-fi
-
-for imgfile in "$@"; do
- if ! [ -f "$imgfile" ]; then
- echo "Error: Not an existing file: $imgfile" >&2
- continue
- fi
- imgdir="${TMPDIR:-/tmp}/${imgfile##*/}"
- if [ -d "$imgdir" ]; then
- rm --recursive -- "$imgdir"
- fi
- mkdir --parents "$imgdir"
- # Does this image contain microcode?
- if $cpio --quiet --list --file="$imgfile" >/dev/null 2>&1; then
- # Number of bytes to skip to get to the compressed archive
- skip=$(($(LANG=C $cpio --io-size=1 --list --file="$imgfile" 2>&1 \
- | sed --quiet --expression='s/^\([0-9]\+\) blocks$/\1/p')+8))
- catimg="dd if=$imgfile bs=$skip skip=1 status=noxfer"
- else
- catimg="cat -- $imgfile"
- fi
- # Determine the compression method
- if { $catimg 2>/dev/null | zcat --test >/dev/null 2>&1;
- [ ${PIPESTATUS[-1]} -eq 0 ]; }; then
- decomp="zcat"
- elif { $catimg 2>/dev/null | bzip2 --test >/dev/null 2>&1;
- [ ${PIPESTATUS[-1]} -eq 0 ]; }; then
- decomp="bzip2 --stdout --decompress"
- elif { $catimg 2>/dev/null | lzop --test >/dev/null 2>&1;
- [ ${PIPESTATUS[-1]} -eq 0 ]; }; then
- decomp="lzop --stdout --decompress"
- else
- echo "Error: Could not determine type of $imgfile" >&2
- continue
- fi
- $catimg 2>/dev/null | $decomp | ( cd -- "$imgdir" && $cpio --quiet )
- if [ ${PIPESTATUS[-1]} -eq 0 ]; then
- echo "$imgfile unpacked into $imgdir"
- fi
-done
=== removed file 'intro.xml'
--- intro.xml 2019-02-09 23:23:26 +0000
+++ intro.xml 1970-01-01 00:00:00 +0000
@@ -1,439 +0,0 @@
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@recompile.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@recompile.se
-
-
-
-
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- intro
- 8mandos
-
-
-
- intro
-
- Introduction to the Mandos system
-
-
-
-
- DESCRIPTION
-
- This is the the Mandos system, which allows computers to have
- encrypted root file systems and at the same time be capable of
- remote and/or unattended reboots.
-
-
- The computers run a small client program in the initial RAM disk
- environment which will communicate with a server over a network.
- All network communication is encrypted using TLS. The clients
- are identified by the server using a TLS public key; each client
- has one unique to it. The server sends the clients an encrypted
- password. The encrypted password is decrypted by the clients
- using a separate OpenPGP key, and the password is then used to
- unlock the root file system, whereupon the computers can
- continue booting normally.
-
-
-
-
- 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
- of their neighbors, and their neighbors’ friends. The servers
- of people who owe them money. And like
- that, they’re gone. And you doubt you’ll
- ever see them again.
-
-
- That is why your servers have encrypted root file systems.
- However, there’s a downside. There’s no going around it:
- rebooting is a pain. Dragging out that rarely-used keyboard and
- screen and unraveling cables behind your servers to plug them in
- to type in that password is messy, especially if you have many
- servers. There are some people who do clever things like using
- serial line consoles and daisy-chain it to the next server, and
- keep all the servers connected in a ring with serial cables,
- which will work, if your servers are physically close enough.
- There are also other out-of-band management solutions, but with
- all these, you still have to be on hand and
- manually type in the password at boot time. Otherwise the
- server just sits there, waiting for a password.
-
-
- Wouldn’t it be great if you could have the security of encrypted
- root file systems and still have servers that could boot up
- automatically if there was a short power outage while you were
- asleep? That you could reboot at will, without having someone
- run over to the server to type in the password?
-
-
- Well, with Mandos, you (almost) can! The gain in convenience
- will only be offset by a small loss in security. The setup is
- as follows:
-
-
- The server will still have its encrypted root file system. The
- password to this file system will be stored on another computer
- (henceforth known as the Mandos server) on the same local
- network. The password will not be stored
- in plaintext, but encrypted with OpenPGP. To decrypt this
- password, a key is needed. This key (the Mandos client key)
- will not be stored there, but back on the original server
- (henceforth known as the Mandos client) in the initial RAM disk
- image. Oh, and all network Mandos client/server communications
- will be encrypted, using TLS (SSL).
-
-
- So, at boot time, the Mandos client will ask for its encrypted
- data over the network, decrypt it to get the password, use it to
- decrypt the root file, and continue booting.
-
-
- Now, of course the initial RAM disk image is not on the
- encrypted root file system, so anyone who had physical access
- could take the Mandos client computer offline and read the disk
- with their own tools to get the authentication keys used by a
- client. But, by then the Mandos server
- should notice that the original server has been offline for too
- long, and will no longer give out the encrypted key. The timing
- here is the only real weak point, and the method, frequency and
- timeout of the server’s checking can be adjusted to any desired
- level of paranoia
-
-
- (The encrypted keys on the Mandos server is on its normal file
- system, so those are safe, provided the root file system of
- that server is encrypted.)
-
-
-
-
- FREQUENTLY ASKED QUESTIONS
-
- Couldn’t the security be defeated by…
-
-
- Grabbing the Mandos client key from the
- initrd really quickly?
-
- This, as mentioned above, is the only real weak point. But if
- you set the timing values tight enough, this will be really
- difficult to do. An attacker would have to physically
- disassemble the client computer, extract the key from the
- initial RAM disk image, and then connect to a still
- online Mandos server to get the encrypted key, and do
- all this before the Mandos server timeout
- kicks in and the Mandos server refuses to give out the key to
- anyone.
-
-
- Now, as the typical procedure seems to be to barge in and turn
- off and grab all computers, to maybe look
- at them months later, this is not likely. If someone does that,
- the whole system will lock itself up
- completely, since Mandos servers are no longer running.
-
-
- For sophisticated attackers who could do
- the clever thing, and had physical access
- to the server for enough time, it would be simpler to get a key
- for an encrypted file system by using hardware memory scanners
- and reading it right off the memory bus.
-
-
-
-
- Replay attacks?
-
- Nope, the network stuff is all done over TLS, which provides
- protection against that.
-
-
-
-
- Man-in-the-middle?
-
- No. The server only gives out the passwords to clients which
- have in the TLS handshake proven that
- they do indeed hold the private key corresponding to that
- client.
-
-
-
-
- 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?
-
- You could protect that computer the
- old-fashioned way, with a must-type-in-the-password-at-boot
- method. Or you could have two computers be the Mandos server
- for each other.
-
-
- Multiple Mandos servers can coexist on a network without any
- trouble. They do not clash, and clients will try all
- available servers. This means that if just one reboots then
- the other can bring it back up, but if both reboot at the same
- time they will stay down until someone types in the password
- on one of them.
-
-
-
-
- Faking checker results?
-
- If the Mandos client does not have an SSH server, the default
- is for the Mandos server 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.
-
-
-
-
-
- SECURITY
-
- So, in summary: The only weakness in the Mandos system is from
- people who have:
-
-
-
-
- The power to come in and physically take your servers,
- and
-
-
-
-
- The cunning and patience to do it carefully, one at a time,
- and quickly, faking Mandos
- client/server responses for each one before the timeout.
-
-
-
-
- While there are some who may be threatened by people who have
- both these attributes, they do not,
- probably, constitute the majority.
-
-
- If you do face such opponents, you must
- figure that they could just as well open your servers and read
- the file system keys right off the memory by running wires to
- the memory bus.
-
-
- What Mandos is designed to protect against is
- not such determined, focused, and competent
- attacks, but against the early morning knock on your door and
- the sudden absence of all the servers in your server room.
- Which it does nicely.
-
-
-
-
- PLUGINS
-
- In the early designs, the
- mandos-client8mandos program (which
- retrieves a password from the Mandos server) also prompted for a
- password on the terminal, in case a Mandos server could not be
- found. Other ways of retrieving a password could easily be
- envisoned, but this multiplicity of purpose was seen to be too
- complex to be a viable way to continue. Instead, the original
- program was separated into mandos-client8mandos and password-prompt8mandos, and a plugin-runner8mandos exist to run them both in parallel, allowing
- the first successful plugin to provide the password. This
- opened up for any number of additional plugins to run, all
- competing to be the first to find a password and provide it to
- the plugin runner.
-
-
- Four additional plugins are provided:
-
-
-
-
- plymouth
- 8mandos
-
-
-
- This prompts for a password when using
- plymouth8.
-
-
-
-
-
- usplash
- 8mandos
-
-
-
- This prompts for a password when using
- usplash8.
-
-
-
-
-
- splashy
- 8mandos
-
-
-
- This prompts for a password when using
- splashy8.
-
-
-
-
-
- askpass-fifo
- 8mandos
-
-
-
- To provide compatibility with the "askpass" program from
- cryptsetup, this plugin listens to the same FIFO as
- askpass would do.
-
-
-
-
-
- More plugins can easily be written and added by the system
- administrator; see the section called "WRITING PLUGINS" in
- plugin-runner
- 8mandos to learn the
- plugin requirements.
-
-
-
-
- BUGS
-
-
-
-
- SEE ALSO
-
- mandos
- 8,
- mandos.conf
- 5,
- mandos-clients.conf
- 5,
- mandos-ctl
- 8,
- mandos-monitor
- 8,
- plugin-runner
- 8mandos,
- mandos-client
- 8mandos,
- password-prompt
- 8mandos,
- plymouth
- 8mandos,
- usplash
- 8mandos,
- splashy
- 8mandos,
- askpass-fifo
- 8mandos,
- mandos-keygen
- 8
-
-
-
-
- Mandos
-
-
-
- The Mandos home page.
-
-
-
-
-
-
-
-
-
-
-
=== removed file 'legalnotice.xml'
--- legalnotice.xml 2017-08-20 16:20:54 +0000
+++ legalnotice.xml 1970-01-01 00:00:00 +0000
@@ -1,28 +0,0 @@
-
-
-
-
- This manual page is part of Mandos.
-
-
-
- Mandos 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.
-
-
-
- Mandos 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 Mandos. If not, see http://www.gnu.org/licenses/.
-
-
=== modified file 'mandos'
--- mandos 2019-02-09 23:23:26 +0000
+++ mandos 2008-08-27 01:18:25 +0000
@@ -1,330 +1,103 @@
#!/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".
-#
+# methods "add" and "remove" in the "AvahiService" class, the
+# "server_state_changed" and "entry_group_state_changed" functions,
+# and some lines in "main".
+#
# Everything else is
-# Copyright © 2008-2018 Teddy Hogeborn
-# Copyright © 2008-2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
+# Copyright © 2007-2008 Teddy Hogeborn & 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.
#
-# Mandos is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
+# 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 Mandos. 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
-
-try:
- import SocketServer as socketserver
-except ImportError:
- import socketserver
+# along with this program. If not, see
+# .
+#
+# Contact the authors at .
+#
+
+from __future__ import division
+
+import SocketServer
import socket
-import argparse
+import select
+from optparse import OptionParser
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
import sys
import re
import os
import signal
+from sets import Set
import subprocess
import atexit
import stat
import logging
import logging.handlers
-import pwd
-import contextlib
-import struct
-import fcntl
-import functools
-try:
- import cPickle as pickle
-except ImportError:
- import 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.20"
-stored_state_file = "clients.pickle"
-
-logger = logging.getLogger()
-syslogger = None
-
-try:
- 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]
- 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'
- ' [%(process)d]:'
- ' %(levelname)s:'
- ' %(message)s'))
- logger.addHandler(console)
- logger.setLevel(level)
-
-
-class PGPError(Exception):
- """Exception if encryption/decryption fails"""
- pass
-
-
-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,
- '--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")
-
- 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):
- for filename in files:
- os.remove(os.path.join(root, filename))
- for dirname in dirs:
- os.rmdir(os.path.join(root, dirname))
- # 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
-
- def encrypt(self, data, password):
- passphrase = self.password_encode(password)
- with tempfile.NamedTemporaryFile(
- dir=self.tempdir) as passfile:
- passfile.write(passphrase)
- passfile.flush()
- proc = subprocess.Popen([self.gpg, '--symmetric',
- '--passphrase-file',
- passfile.name]
- + self.gnupgargs,
- 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:
- passfile.write(passphrase)
- passfile.flush()
- proc = subprocess.Popen([self.gpg, '--decrypt',
- '--passphrase-file',
- passfile.name]
- + self.gnupgargs,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- decrypted_plaintext, err = proc.communicate(input=data)
- 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()
-
+
+version = "1.0"
+
+logger = logging.Logger('mandos')
+syslogger = logging.handlers.SysLogHandler\
+ (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
+ address = "/dev/log")
+syslogger.setFormatter(logging.Formatter\
+ ('Mandos: %(levelname)s: %(message)s'))
+logger.addHandler(syslogger)
+
+console = logging.StreamHandler()
+console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
+ ' %(message)s'))
+logger.addHandler(console)
class AvahiError(Exception):
- def __init__(self, value, *args, **kwargs):
+ def __init__(self, value):
self.value = value
- return super(AvahiError, self).__init__(value, *args,
- **kwargs)
-
+ def __str__(self):
+ return 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.
name: string; Example: 'Mandos'
type: string; Example: '_mandos._tcp'.
- See
+ See
port: integer; what port to announce
TXT: list of strings; TXT record for the service
domain: string; Domain to publish on, default to .local if empty.
@@ -332,2524 +105,463 @@
max_renames: integer; maximum number of renames
rename_count: integer; counter so we only rename after collisions
a sensible number of times
- group: D-Bus Entry Group
- 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,
+ type = None, port = None, TXT = None, domain = "",
+ host = "", max_renames = 32768):
self.interface = interface
self.name = name
- self.type = servicetype
+ self.type = type
self.port = port
- self.TXT = TXT if TXT is not None else []
+ if TXT is None:
+ self.TXT = []
+ else:
+ self.TXT = TXT
self.domain = domain
self.host = host
self.rename_count = 0
self.max_renames = max_renames
- self.protocol = protocol
- self.group = None # our entry group
- 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)
+ logger.critical(u"No suitable Zeroconf service name found"
+ u" after %i retries, exiting.",
+ rename_count)
raise AvahiServiceError("Too many renames")
- self.name = str(
- self.server.GetAlternativeServiceName(self.name))
+ self.name = server.GetAlternativeServiceName(self.name)
+ logger.info(u"Changing Zeroconf service name to %r ...",
+ str(self.name))
+ syslogger.setFormatter(logging.Formatter\
+ ('Mandos (%s): %%(levelname)s:'
+ ' %%(message)s' % self.name))
+ self.remove()
+ self.add()
self.rename_count += 1
- logger.info("Changing Zeroconf service name to %r ...",
- self.name)
- if 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)
-
def remove(self):
"""Derived from the Avahi example code"""
- if self.entry_group_state_changed_match is not None:
- self.entry_group_state_changed_match.remove()
- self.entry_group_state_changed_match = None
- if self.group is not None:
- self.group.Reset()
-
+ if group is not None:
+ group.Reset()
def add(self):
"""Derived from the Avahi example code"""
- self.remove()
- if self.group is None:
- self.group = dbus.Interface(
- self.bus.get_object(avahi.DBUS_NAME,
- self.server.EntryGroupNew()),
- avahi.DBUS_INTERFACE_ENTRY_GROUP)
- self.entry_group_state_changed_match = (
- self.group.connect_to_signal(
- 'StateChanged', self.entry_group_state_changed))
- logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
- self.name, self.type)
- self.group.AddService(
- self.interface,
- self.protocol,
- dbus.UInt32(0), # flags
- self.name, self.type,
- self.domain, self.host,
- 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:
- logger.info("Zeroconf service name collision.")
- 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))
-
- def cleanup(self):
- """Derived from the Avahi example code"""
- if self.group is not None:
- try:
- self.group.Free()
- except (dbus.exceptions.UnknownMethodException,
- dbus.exceptions.DBusException):
- 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",
- }
- if state in bad_states:
- if bad_states[state] is not None:
- if error is None:
- logger.error(bad_states[state])
- else:
- 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)
- 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:
- self.server = dbus.Interface(
- self.bus.get_object(avahi.DBUS_NAME,
- avahi.DBUS_PATH_SERVER,
- 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.GetState())
-
-
-class AvahiServiceToSyslog(AvahiService):
- def rename(self, *args, **kwargs):
- """Add the new name to the syslog messages"""
- ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
- syslogger.setFormatter(logging.Formatter(
- 'Mandos ({}) [%(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"
- _tls_rawpk_version = b"3.6.6"
-
- 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
- CRT_RAWPK = 3
- CLIENT = 2
- SHUT_RDWR = 0
- CRD_CERTIFICATE = 1
- E_NO_CERTIFICATE_FOUND = -49
- X509_FMT_DER = 0
- NO_TICKETS = 1<<10
- ENABLE_RAWPK = 1<<18
- CTYPE_PEERS = 3
- KEYID_USE_SHA256 = 1 # gnutls/x509.h
- 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_flags = gnutls.CLIENT
- if gnutls.check_version("3.5.6"):
- gnutls_flags |= gnutls.NO_TICKETS
- if gnutls.has_rawpk:
- gnutls_flags |= gnutls.ENABLE_RAWPK
- gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
- del gnutls_flags
- 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
-
- has_rawpk = bool(check_version(_tls_rawpk_version))
-
- if has_rawpk:
- # Types
- class pubkey_st(ctypes.Structure):
- _fields = []
- pubkey_t = ctypes.POINTER(pubkey_st)
-
- x509_crt_fmt_t = ctypes.c_int
-
- # All the function declarations below are from gnutls/abstract.h
- pubkey_init = _library.gnutls_pubkey_init
- pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
- pubkey_init.restype = _error_code
-
- pubkey_import = _library.gnutls_pubkey_import
- pubkey_import.argtypes = [pubkey_t, ctypes.POINTER(datum_t),
- x509_crt_fmt_t]
- pubkey_import.restype = _error_code
-
- pubkey_get_key_id = _library.gnutls_pubkey_get_key_id
- pubkey_get_key_id.argtypes = [pubkey_t, ctypes.c_int,
- ctypes.POINTER(ctypes.c_ubyte),
- ctypes.POINTER(ctypes.c_size_t)]
- pubkey_get_key_id.restype = _error_code
-
- pubkey_deinit = _library.gnutls_pubkey_deinit
- pubkey_deinit.argtypes = [pubkey_t]
- pubkey_deinit.restype = None
- else:
- # 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
-
- if check_version("3.6.4"):
- certificate_type_get2 = _library.gnutls_certificate_type_get2
- certificate_type_get2.argtypes = [session_t, ctypes.c_int]
- certificate_type_get2.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()
+ global group
+ if group is None:
+ group = dbus.Interface\
+ (bus.get_object(avahi.DBUS_NAME,
+ server.EntryGroupNew()),
+ avahi.DBUS_INTERFACE_ENTRY_GROUP)
+ group.connect_to_signal('StateChanged',
+ entry_group_state_changed)
+ logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
+ service.name, service.type)
+ group.AddService(
+ self.interface, # interface
+ avahi.PROTO_INET6, # protocol
+ dbus.UInt32(0), # flags
+ self.name, self.type,
+ self.domain, self.host,
+ dbus.UInt16(self.port),
+ avahi.string_array_to_txt_array(self.TXT))
+ group.Commit()
+
+# From the Avahi example code:
+group = None # our entry group
+# End of Avahi example code
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
- approval_duration: datetime.timedelta(); Duration of one approval
- 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_command: string; External command which is run to check
- if client lives. %() expansions are done at
+ name: string; from the config file, used in log messages
+ fingerprint: string (40 or 32 hexadecimal digits); used to
+ uniquely identify the client
+ secret: bytestring; sent verbatim (over TLS) to client
+ host: string; available for use by the checker command
+ created: datetime.datetime(); object creation, not client host
+ last_checked_ok: datetime.datetime() or None if not yet checked OK
+ timeout: datetime.timedelta(); How long from last_checked_ok
+ until this client is invalid
+ interval: datetime.timedelta(); How often to start a new checker
+ stop_hook: If set, called by stop() as stop_hook(self)
+ checker: subprocess.Popen(); a running checker process used
+ to see if the client lives.
+ 'None' if no process is running.
+ checker_initiator_tag: a gobject event source tag, or None
+ stop_initiator_tag: - '' -
+ checker_callback_tag: - '' -
+ 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
- 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
- enabled: bool()
- fingerprint: string (40 or 32 hexadecimal digits); used to
- uniquely identify an OpenPGP client
- key_id: string (64 hexadecimal digits); used to uniquely identify
- a client using raw public keys
- host: string; available for use by the checker command
- interval: datetime.timedelta(); How often to start a new checker
- last_approval_request: datetime.datetime(); (UTC) or None
- last_checked_ok: datetime.datetime(); (UTC) or None
- 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
- secret: bytestring; sent verbatim (over TLS) to client
- timeout: datetime.timedelta(); How long from last_checked_ok
- until this client is disabled
- extended_timeout: extra long timeout when secret has been sent
- runtime_expansions: Allowed attributes for runtime expansion.
- expires: datetime.datetime(); time (UTC) when a client will be
- disabled, or None
- server_settings: The server_settings dict from main()
+ Private attibutes:
+ _timeout: Real variable for 'timeout'
+ _interval: Real variable for 'interval'
+ _timeout_milliseconds: Used when calling gobject.timeout_add()
+ _interval_milliseconds: - '' -
"""
-
- runtime_expansions = ("approval_delay", "approval_duration",
- "created", "enabled", "expires", "key_id",
- "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",
- }
-
- @staticmethod
- def config_parser(config):
- """Construct a new dict of client settings of this form:
- { client_name: {setting_name: value, ...}, ...}
- with exceptions for any special settings as defined above.
- NOTE: Must be a pure function. Must return the same result
- value given the same arguments.
- """
- settings = {}
- 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 key_id and fingerprint
- # for later comparison purposes with return value from the
- # key_id() and fingerprint() functions
- client["key_id"] = (section.get("key_id", "").upper()
- .replace(" ", ""))
- client["fingerprint"] = (section["fingerprint"].upper()
- .replace(" ", ""))
- if "secret" in section:
- client["secret"] = codecs.decode(section["secret"]
- .encode("utf-8"),
- "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 {}"
- .format(section))
- client["timeout"] = string_to_delta(section["timeout"])
- client["extended_timeout"] = string_to_delta(
- section["extended_timeout"])
- client["interval"] = string_to_delta(section["interval"])
- client["approval_delay"] = string_to_delta(
- section["approval_delay"])
- client["approval_duration"] = string_to_delta(
- section["approval_duration"])
- client["checker_command"] = section["checker"]
- 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 _set_timeout(self, timeout):
+ "Setter function for 'timeout' attribute"
+ self._timeout = timeout
+ self._timeout_milliseconds = ((self.timeout.days
+ * 24 * 60 * 60 * 1000)
+ + (self.timeout.seconds * 1000)
+ + (self.timeout.microseconds
+ // 1000))
+ timeout = property(lambda self: self._timeout,
+ _set_timeout)
+ del _set_timeout
+ def _set_interval(self, interval):
+ "Setter function for 'interval' attribute"
+ self._interval = interval
+ self._interval_milliseconds = ((self.interval.days
+ * 24 * 60 * 60 * 1000)
+ + (self.interval.seconds
+ * 1000)
+ + (self.interval.microseconds
+ // 1000))
+ interval = property(lambda self: self._interval,
+ _set_interval)
+ del _set_interval
+ def __init__(self, name = None, stop_hook=None, config={}):
+ """Note: the 'checker' key in 'config' sets the
+ 'checker_command' attribute and *not* the 'checker'
+ attribute."""
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():
- setattr(self, setting, value)
-
- if self.enabled:
- if not hasattr(self, "last_enabled"):
- self.last_enabled = datetime.datetime.utcnow()
- if not hasattr(self, "expires"):
- self.expires = (datetime.datetime.utcnow()
- + self.timeout)
+ logger.debug(u"Creating client %r", self.name)
+ # Uppercase and remove spaces from fingerprint for later
+ # comparison purposes with return value from the fingerprint()
+ # function
+ self.fingerprint = config["fingerprint"].upper()\
+ .replace(u" ", u"")
+ logger.debug(u" Fingerprint: %s", self.fingerprint)
+ if "secret" in config:
+ self.secret = config["secret"].decode(u"base64")
+ elif "secfile" in config:
+ sf = open(config["secfile"])
+ self.secret = sf.read()
+ sf.close()
else:
- self.last_enabled = None
- self.expires = None
-
- logger.debug("Creating client %r", self.name)
- logger.debug(" Key ID: %s", self.key_id)
- logger.debug(" Fingerprint: %s", self.fingerprint)
- self.created = settings.get("created",
- datetime.datetime.utcnow())
-
- # attributes specific for this server instance
+ raise TypeError(u"No secret or secfile for client %s"
+ % self.name)
+ self.host = config.get("host", "")
+ self.created = datetime.datetime.now()
+ self.last_checked_ok = None
+ self.timeout = string_to_delta(config["timeout"])
+ self.interval = string_to_delta(config["interval"])
+ self.stop_hook = stop_hook
self.checker = None
self.checker_initiator_tag = None
- self.disable_initiator_tag = None
+ self.stop_initiator_tag = None
self.checker_callback_tag = None
- 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()
- if not attr.startswith("_")]
- self.client_structure.append("client_structure")
-
- for name, t in inspect.getmembers(
- type(self), lambda obj: isinstance(obj, property)):
- if not name.startswith("_"):
- self.client_structure.append(name)
-
- # Send notice to process children that client state has changed
- def send_changedstate(self):
- with self.changedstate:
- self.changedstate.notify_all()
-
- def enable(self):
+ self.check_command = config["checker"]
+ def start(self):
"""Start this client's checker and timeout hooks"""
- if getattr(self, "enabled", False):
- # Already enabled
- return
- self.expires = datetime.datetime.utcnow() + self.timeout
- self.enabled = True
- 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):
- return False
- 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)
- 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)
- 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
- 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)
- # 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)
+ self.checker_initiator_tag = gobject.timeout_add\
+ (self._interval_milliseconds,
+ self.start_checker)
# Also start a new checker *right now*.
self.start_checker()
-
- def checker_callback(self, source, condition, connection,
- command):
+ # Schedule a stop() when 'timeout' has passed
+ self.stop_initiator_tag = gobject.timeout_add\
+ (self._timeout_milliseconds,
+ self.stop)
+ def stop(self):
+ """Stop this client.
+ The possibility that a client might be restarted is left open,
+ but not currently used."""
+ # If this client doesn't have a secret, it is already stopped.
+ if hasattr(self, "secret") and self.secret:
+ logger.info(u"Stopping client %s", self.name)
+ self.secret = None
+ else:
+ return False
+ if getattr(self, "stop_initiator_tag", False):
+ gobject.source_remove(self.stop_initiator_tag)
+ self.stop_initiator_tag = None
+ if getattr(self, "checker_initiator_tag", False):
+ gobject.source_remove(self.checker_initiator_tag)
+ self.checker_initiator_tag = None
+ self.stop_checker()
+ if self.stop_hook:
+ self.stop_hook(self)
+ # Do not run this again if called by a gobject.timeout_add
+ return False
+ def __del__(self):
+ self.stop_hook = None
+ self.stop()
+ def checker_callback(self, pid, condition):
"""The checker has completed, so take appropriate actions."""
+ now = datetime.datetime.now()
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 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))
- else:
- self.last_checker_status = -1
- self.last_checker_signal = -returncode
- logger.warning("Checker for %(name)s crashed?",
+ if os.WIFEXITED(condition) \
+ and (os.WEXITSTATUS(condition) == 0):
+ logger.info(u"Checker for %(name)s succeeded",
+ vars(self))
+ self.last_checked_ok = now
+ gobject.source_remove(self.stop_initiator_tag)
+ self.stop_initiator_tag = gobject.timeout_add\
+ (self._timeout_milliseconds,
+ self.stop)
+ elif not os.WIFEXITED(condition):
+ logger.warning(u"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)
- 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.expires = datetime.datetime.utcnow() + timeout
-
- def need_approval(self):
- self.last_approval_request = datetime.datetime.utcnow()
-
+ else:
+ logger.info(u"Checker for %(name)s failed",
+ vars(self))
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
- # did that, and if a checker (for some reason) started running
- # slowly and taking more than 'interval' time, then the client
- # would inevitably timeout, since no checker would get a
- # chance to run to completion. If we instead leave running
+ # did that, then if a checker (for some reason) started
+ # running slowly and taking more than 'interval' time, the
+ # client would inevitably timeout, since no checker would get
+ # a chance to run to completion. If we instead leave running
# 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
- # Start a new checker if needed
+ # than 'timeout' for the client to be declared invalid, which
+ # is as it should be.
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}
- try:
- command = self.checker_command % escaped_attrs
- except TypeError as error:
- logger.error('Could not format string "%s"',
- self.checker_command,
- 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
+ try:
+ # In case check_command has exactly one % operator
+ command = self.check_command % self.host
+ except TypeError:
+ # Escape attributes for the shell
+ escaped_attrs = dict((key, re.escape(str(val)))
+ for key, val in
+ vars(self).iteritems())
+ try:
+ command = self.check_command % escaped_attrs
+ except TypeError, error:
+ logger.error(u'Could not format string "%s":'
+ u' %s', self.check_command, error)
+ return True # Try again later
+ try:
+ logger.info(u"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.)
+ self.checker = subprocess.Popen(command,
+ close_fds=True,
+ shell=True, cwd="/")
+ self.checker_callback_tag = gobject.child_watch_add\
+ (self.checker.pid,
+ self.checker_callback)
+ except OSError, error:
+ logger.error(u"Failed to start subprocess: %s",
+ error)
+ # 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()
+ logger.debug(u"Stopping checker for %(name)s", vars(self))
+ try:
+ os.kill(self.checker.pid, signal.SIGTERM)
+ #os.sleep(0.5)
+ #if self.checker.poll() is None:
+ # os.kill(self.checker.pid, signal.SIGKILL)
+ except OSError, 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):
- """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.
- """
- # Encoding deeply encoded byte arrays is not supported yet by the
- # "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))
-
- def decorator(func):
- func._dbus_is_property = True
- func._dbus_interface = dbus_interface
- func._dbus_signature = signature
- func._dbus_access = access
- 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}
- 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")
- 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
-
-
-class DBusPropertyAccessException(DBusPropertyException):
- """A property's access permissions disallows an operation.
- """
- pass
-
-
-class DBusPropertyNotFound(DBusPropertyException):
- """An attempt was made to access a non-existing property.
- """
- 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.
- """
-
- @staticmethod
- def _is_dbus_thing(thing):
- """Returns a function testing if an attribute is a D-Bus thing
-
- If called like _is_dbus_thing("method") it returns a function
- suitable for use as predicate to inspect.getmembers().
- """
- return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
- False)
-
- def _get_all_dbus_things(self, thing):
- """Returns a generator of (name, attribute) pairs
- """
- 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.
- """
-
- 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")):
- 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",
- out_signature="v")
- def Get(self, interface_name, property_name):
- """Standard D-Bus property Get() method, see D-Bus standard.
- """
- prop = self._get_dbus_property(interface_name, property_name)
- if prop._dbus_access == "write":
- raise DBusPropertyAccessException(property_name)
- value = prop()
- 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.
- """
- prop = self._get_dbus_property(interface_name, property_name)
- if prop._dbus_access == "read":
- raise DBusPropertyAccessException(property_name)
- if prop._dbus_get_args_options["byte_arrays"]:
- # 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))
- value = dbus.ByteArray(b''.join(chr(byte)
- for byte in value))
- prop(value)
-
- @dbus.service.method(dbus.PROPERTIES_IFACE,
- in_signature="s",
- out_signature="a{sv}")
- def GetAll(self, interface_name):
- """Standard D-Bus property GetAll() method, see D-Bus
- standard.
-
- Note: Will not include properties with access="write".
- """
- properties = {}
- for name, prop in self._get_all_dbus_things("property"):
- if (interface_name
- and interface_name != prop._dbus_interface):
- # Interface non-empty but did not match
- continue
- # Ignore write-only properties
- if prop._dbus_access == "write":
- continue
- value = prop()
- if not hasattr(value, "variant_level"):
- properties[name] = value
- continue
- 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)
- 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)
- for name, prop
- in self._get_all_dbus_things("property")
- 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 the names to the return values for the
- # "org.freedesktop.DBus.Properties" methods
- if (if_tag.getAttribute("name")
- == "org.freedesktop.DBus.Properties"):
- for cn in if_tag.getElementsByTagName("method"):
- if cn.getAttribute("name") == "Get":
- for arg in cn.getElementsByTagName("arg"):
- if (arg.getAttribute("direction")
- == "out"):
- arg.setAttribute("name", "value")
- elif cn.getAttribute("name") == "GetAll":
- for arg in cn.getElementsByTagName("arg"):
- if (arg.getAttribute("direction")
- == "out"):
- arg.setAttribute("name", "props")
- 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
-
-
-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)
-
-
-def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
- """A class decorator; applied to a subclass of
- 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()):
- attr = {}
- interface_names = set()
- # Go though all attributes of the class
- for attrname, attribute in inspect.getmembers(cls):
- # 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)):
- 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)
- 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(
- 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)
- # 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))
- # Copy annotations, if any
- try:
- 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
- # the function is called
- def fixscope(func1, func2):
- """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
- attr[attrname] = fixscope(attribute, new_function)
- # Is this a D-Bus method?
- elif getattr(attribute, "_dbus_is_method", False):
- # Create a new, but exactly alike, function
- # 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)))
- # Copy annotations, if any
- try:
- attr[attrname]._dbus_annotations = dict(
- attribute._dbus_annotations)
- except AttributeError:
- pass
- # Is this a D-Bus property?
- elif getattr(attribute, "_dbus_is_property", False):
- # Create a new, but exactly alike, function
- # 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)))
- # Copy annotations, if any
- try:
- attr[attrname]._dbus_annotations = dict(
- attribute._dbus_annotations)
- except AttributeError:
- pass
- # Is this a D-Bus interface?
- elif getattr(attribute, "_dbus_is_interface", False):
- # Create a new, but exactly alike, function
- # 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)))
- if deprecate:
- # Deprecate all alternate interfaces
- iname = "_AlternateDBusNames_interface_annotation{}"
- for interface_name in interface_names:
-
- @dbus_interface_annotations(interface_name)
- def func(self):
- return {"org.freedesktop.DBus.Deprecated":
- "true"}
- # Find an unused name
- for aname in (iname.format(i)
- for i in itertools.count()):
- if aname not in attr:
- attr[aname] = func
- break
- 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)
- return cls
-
- return wrapper
-
-
-@alternate_dbus_interfaces({"se.recompile.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.service.Object doesn't use super(), so we can't either.
-
- 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(
- {ord("."): ord("_"),
- ord("-"): ord("_")})
- 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):
- """ 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
- type_func: Function that transform the value before sending it
- to the D-Bus. Default: no transform
- variant_level: D-Bus variant level. Default: 1
- """
- attrname = "_{}".format(dbus_name)
-
- def setter(self, value):
- if hasattr(self, "dbus_object_path"):
- if (not hasattr(self, attrname) or
- 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())
- 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)
- 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)
- last_checked_ok = notifychangeproperty(datetime_to_dbus,
- "LastCheckedOK")
- last_checker_status = notifychangeproperty(dbus.Int16,
- "LastCheckerStatus")
- last_approval_request = notifychangeproperty(
- 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_duration = notifychangeproperty(
- dbus.UInt64, "ApprovalDuration",
- type_func=lambda td: td.total_seconds() * 1000)
- host = notifychangeproperty(dbus.String, "Host")
- timeout = notifychangeproperty(
- dbus.UInt64, "Timeout",
- type_func=lambda td: td.total_seconds() * 1000)
- 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)
- 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()
- except LookupError:
- pass
- 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:
- # Emit D-Bus signal
- self.CheckerCompleted(dbus.Int16(exitstatus),
- # This is specific to GNU libC
- dbus.Int64(exitstatus << 8),
- 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.String(command))
- return ret
-
- def start_checker(self, *args, **kwargs):
- old_checker_pid = getattr(self.checker, "pid", None)
- r = Client.start_checker(self, *args, **kwargs)
- # Only if new checker process was started
- if (self.checker is not None
- and old_checker_pid != self.checker.pid):
- # Emit D-Bus signal
- self.CheckerStarted(self.current_checker_command)
- return r
-
- def _reset_approved(self):
- self.approved = None
- return False
-
- def approve(self, value=True):
- self.approved = value
- GLib.timeout_add(int(self.approval_duration.total_seconds()
- * 1000), self._reset_approved)
- self.send_changedstate()
-
- # D-Bus methods, signals & properties
-
- # Interfaces
-
- # Signals
-
- # 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):
- """D-Bus signal
- Is sent after a successful transfer of secret from the Mandos
- 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
-
- # 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
-
- # 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",
- 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",
- access="readwrite")
- def ApprovalDelay_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.approval_delay.total_seconds()
- * 1000)
- self.approval_delay = datetime.timedelta(0, 0, 0, value)
-
- # ApprovalDuration - property
- @dbus_service_property(_interface,
- signature="t",
- access="readwrite")
- def ApprovalDuration_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.approval_duration.total_seconds()
- * 1000)
- self.approval_duration = datetime.timedelta(0, 0, 0, value)
-
- # Name - property
- @dbus_annotations(
- {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
- @dbus_service_property(_interface, signature="s", access="read")
- def Name_dbus_property(self):
- return dbus.String(self.name)
-
- # KeyID - property
- @dbus_annotations(
- {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
- @dbus_service_property(_interface, signature="s", access="read")
- def KeyID_dbus_property(self):
- return dbus.String(self.key_id)
-
- # 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",
- access="readwrite")
- def Host_dbus_property(self, value=None):
- if value is None: # get
- return dbus.String(self.host)
- self.host = str(value)
-
- # Created - property
- @dbus_annotations(
- {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
- @dbus_service_property(_interface, signature="s", access="read")
- def Created_dbus_property(self):
- return datetime_to_dbus(self.created)
-
- # LastEnabled - property
- @dbus_service_property(_interface, signature="s", access="read")
- def LastEnabled_dbus_property(self):
- return datetime_to_dbus(self.last_enabled)
-
- # Enabled - property
- @dbus_service_property(_interface,
- signature="b",
- access="readwrite")
- def Enabled_dbus_property(self, value=None):
- if value is None: # get
- return dbus.Boolean(self.enabled)
- if value:
- self.enable()
- else:
- self.disable()
-
- # LastCheckedOK - property
- @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")
- 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",
- access="readwrite")
- def Timeout_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.timeout.total_seconds() * 1000)
- old_timeout = self.timeout
- self.timeout = datetime.timedelta(0, 0, 0, value)
- # Reschedule disabling
- if self.enabled:
- now = datetime.datetime.utcnow()
- self.expires += self.timeout - old_timeout
- if self.expires <= now:
- # The timeout has passed
- self.disable()
- else:
- 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)
-
- # ExtendedTimeout - property
- @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)
- self.extended_timeout = datetime.timedelta(0, 0, 0, value)
-
- # Interval - property
- @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)
- 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
-
- # Checker - property
- @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)
-
- # CheckerRunning - property
- @dbus_service_property(_interface,
- signature="b",
- access="readwrite")
- def CheckerRunning_dbus_property(self, value=None):
- if value is None: # get
- return dbus.Boolean(self.checker is not None)
- if value:
- 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
-
- # Secret = property
- @dbus_annotations(
- {"org.freedesktop.DBus.Property.EmitsChangedSignal":
- "invalidates"})
- @dbus_service_property(_interface,
- signature="ay",
- access="write",
- byte_arrays=True)
- def Secret_dbus_property(self, value):
- self.secret = bytes(value)
-
- del _interface
-
-
-class ProxyClient(object):
- def __init__(self, child_pipe, key_id, fpr, address):
- self._pipe = child_pipe
- self._pipe.send(('init', key_id, fpr, address))
- if not self._pipe.recv():
- raise KeyError(key_id or fpr)
-
- def __getattribute__(self, name):
- if name == '_pipe':
- return super(ProxyClient, self).__getattribute__(name)
- self._pipe.send(('getattr', name))
- data = self._pipe.recv()
- 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)
- self._pipe.send(('setattr', name, value))
-
-
-class ClientHandler(socketserver.BaseRequestHandler, object):
- """A class to handle client connections.
-
- Instantiated once for each connection to handle it.
+ def still_valid(self):
+ """Has the timeout not yet passed for this client?"""
+ now = datetime.datetime.now()
+ if self.last_checked_ok is None:
+ return now < (self.created + self.timeout)
+ else:
+ return now < (self.last_checked_ok + self.timeout)
+
+
+def peer_certificate(session):
+ "Return the peer's OpenPGP certificate as a bytestring"
+ # If not an OpenPGP certificate...
+ 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()
+ cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
+ (session._c_object, ctypes.byref(list_size))
+ if list_size.value == 0:
+ return None
+ cert = cert_list[0]
+ return ctypes.string_at(cert.data, cert.size)
+
+
+def fingerprint(openpgp):
+ "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
+ # New GnuTLS "datum" with the OpenPGP public key
+ 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.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.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.library.functions.gnutls_openpgp_crt_verify_self\
+ (crt, 0, ctypes.byref(crtverify))
+ if crtverify.value != 0:
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ raise gnutls.errors.CertificateSecurityError("Verify failed")
+ # New buffer for the fingerprint
+ buffer = ctypes.create_string_buffer(20)
+ buffer_length = ctypes.c_size_t()
+ # Get the fingerprint from the certificate into the buffer
+ gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
+ (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
+ # Deinit the certificate
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ # Convert the buffer to a Python bytestring
+ fpr = ctypes.string_at(buffer, buffer_length.value)
+ # Convert the bytestring to hexadecimal notation
+ hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
+ return hex_fpr
+
+
+class tcp_handler(SocketServer.BaseRequestHandler, object):
+ """A TCP request handler class.
+ Instantiated by IPv6_TCPServer for each request to handle it.
Note: This will run in its own forked process."""
-
+
def handle(self):
- with contextlib.closing(self.server.child_pipe) as child_pipe:
- logger.info("TCP connection from: %s",
- str(self.client_address))
- logger.debug("Pipe FD: %d",
- self.server.child_pipe.fileno())
-
- session = gnutls.ClientSession(self.request)
-
- # priority = ':'.join(("NONE", "+VERS-TLS1.1",
- # "+AES-256-CBC", "+SHA1",
- # "+COMP-NULL", "+CTYPE-OPENPGP",
- # "+DHE-DSS"))
- # 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)
-
- # 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)
- 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:
- 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:
- if gnutls.has_rawpk:
- fpr = ""
- try:
- key_id = self.key_id(
- self.peer_certificate(session))
- except (TypeError, gnutls.Error) as error:
- logger.warning("Bad certificate: %s", error)
- return
- logger.debug("Key ID: %s", key_id)
-
- else:
- key_id = ""
- try:
- fpr = self.fingerprint(
- self.peer_certificate(session))
- except (TypeError, gnutls.Error) as error:
- logger.warning("Bad certificate: %s", error)
- return
- logger.debug("Fingerprint: %s", fpr)
-
- try:
- client = ProxyClient(child_pipe, key_id, 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)
- 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
- break
- elif client.approved is None:
- logger.info("Client %s needs approval",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.NeedApproval(
- client.approval_delay.total_seconds()
- * 1000, client.approved_by_default)
- else:
- logger.warning("Client %s was not approved",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.Rejected("Denied")
- return
-
- # wait until timeout or approved
- time = datetime.datetime.now()
- client.changedstate.acquire()
- client.changedstate.wait(delay.total_seconds())
- client.changedstate.release()
- time2 = datetime.datetime.now()
- if (time2 - time) >= delay:
- if not client.approved_by_default:
- logger.warning("Client %s timed out while"
- " waiting for approval",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.Rejected("Approval timed out")
- return
- else:
- break
- else:
- delay -= time2 - time
-
- try:
- session.send(client.secret)
- except gnutls.Error as error:
- logger.warning("gnutls send failed",
- exc_info=error)
- return
-
- logger.info("Sending secret to %s", client.name)
- # bump the timeout using extended_timeout
- client.bump_timeout(client.extended_timeout)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.GotSecret()
-
- finally:
- if approval_required:
- client.approvals_pending -= 1
- try:
- session.bye()
- except gnutls.Error as error:
- logger.warning("GnuTLS bye failed",
- exc_info=error)
-
- @staticmethod
- def peer_certificate(session):
- "Return the peer's certificate as a bytestring"
- try:
- cert_type = gnutls.certificate_type_get2(session._c_object,
- gnutls.CTYPE_PEERS)
- except AttributeError:
- cert_type = gnutls.certificate_type_get(session._c_object)
- if gnutls.has_rawpk:
- valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
- else:
- valid_cert_types = frozenset((gnutls.CRT_OPENPGP,))
- # If not a valid certificate type...
- if cert_type not in valid_cert_types:
- logger.info("Cert type %r not in %r", cert_type,
- valid_cert_types)
- # ...return invalid data
- return b""
- list_size = ctypes.c_uint(1)
- cert_list = (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")
- if list_size.value == 0:
- return None
- cert = cert_list[0]
- return ctypes.string_at(cert.data, cert.size)
-
- @staticmethod
- def key_id(certificate):
- "Convert a certificate bytestring to a hexdigit key ID"
- # New GnuTLS "datum" with the public key
- datum = gnutls.datum_t(
- ctypes.cast(ctypes.c_char_p(certificate),
- ctypes.POINTER(ctypes.c_ubyte)),
- ctypes.c_uint(len(certificate)))
- # XXX all these need to be created in the gnutls "module"
- # New empty GnuTLS certificate
- pubkey = gnutls.pubkey_t()
- gnutls.pubkey_init(ctypes.byref(pubkey))
- # Import the raw public key into the certificate
- gnutls.pubkey_import(pubkey,
- ctypes.byref(datum),
- gnutls.X509_FMT_DER)
- # New buffer for the key ID
- buf = ctypes.create_string_buffer(32)
- buf_len = ctypes.c_size_t(len(buf))
- # Get the key ID from the raw public key into the buffer
- gnutls.pubkey_get_key_id(pubkey,
- gnutls.KEYID_USE_SHA256,
- ctypes.cast(ctypes.byref(buf),
- ctypes.POINTER(ctypes.c_ubyte)),
- ctypes.byref(buf_len))
- # Deinit the certificate
- gnutls.pubkey_deinit(pubkey)
-
- # Convert the buffer to a Python bytestring
- key_id = ctypes.string_at(buf, buf_len.value)
- # Convert the bytestring to hexadecimal notation
- hex_key_id = binascii.hexlify(key_id).upper()
- return hex_key_id
-
- @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)))
- # New empty GnuTLS certificate
- crt = gnutls.openpgp_crt_t()
- 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)
- # Verify the self signature in the key
- crtverify = ctypes.c_uint()
- gnutls.openpgp_crt_verify_self(crt, 0,
- ctypes.byref(crtverify))
- if crtverify.value != 0:
- gnutls.openpgp_crt_deinit(crt)
- raise gnutls.CertificateSecurityError(code
- =crtverify.value)
- # 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))
- # Deinit the certificate
- 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
- hex_fpr = binascii.hexlify(fpr).upper()
- return hex_fpr
-
-
-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.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()
-
-
-class IPv6_TCPServer(MultiprocessingMixInWithPipe,
- socketserver.TCPServer, object):
- """IPv6-capable TCP server. Accepts 'None' as address and/or port
-
+ logger.info(u"TCP connection from: %s",
+ unicode(self.client_address))
+ session = gnutls.connection.ClientSession\
+ (self.request, gnutls.connection.X509Credentials())
+
+ line = self.request.makefile().readline()
+ logger.debug(u"Protocol version: %r", line)
+ try:
+ if int(line.strip().split()[0]) > 1:
+ raise RuntimeError
+ except (ValueError, IndexError, RuntimeError), error:
+ logger.error(u"Unknown protocol version: %s", error)
+ return
+
+ # 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"))
+ priority = "NORMAL" # Fallback default, since this
+ # MUST be set.
+ if self.server.settings["priority"]:
+ priority = self.server.settings["priority"]
+ gnutls.library.functions.gnutls_priority_set_direct\
+ (session._c_object, priority, None);
+
+ try:
+ session.handshake()
+ except gnutls.errors.GNUTLSError, error:
+ logger.warning(u"Handshake failed: %s", error)
+ # Do not run session.bye() here: the session is not
+ # established. Just abandon the request.
+ return
+ try:
+ fpr = fingerprint(peer_certificate(session))
+ except (TypeError, gnutls.errors.GNUTLSError), error:
+ logger.warning(u"Bad certificate: %s", error)
+ session.bye()
+ return
+ logger.debug(u"Fingerprint: %s", fpr)
+ client = None
+ for c in self.server.clients:
+ if c.fingerprint == fpr:
+ client = c
+ break
+ if not client:
+ logger.warning(u"Client not found for fingerprint: %s",
+ fpr)
+ session.bye()
+ return
+ # Have to check if client.still_valid(), since it is possible
+ # that the client timed out while establishing the GnuTLS
+ # session.
+ if not client.still_valid():
+ logger.warning(u"Client %(name)s is invalid",
+ vars(client))
+ session.bye()
+ return
+ sent_size = 0
+ while sent_size < len(client.secret):
+ sent = session.send(client.secret[sent_size:])
+ logger.debug(u"Sent: %d, remaining: %d",
+ sent, len(client.secret)
+ - (sent_size + sent))
+ sent_size += sent
+ session.bye()
+
+
+class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
+ """IPv6 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
+ settings: Server settings
+ clients: Set() of Client objects
"""
-
- def __init__(self, server_address, RequestHandlerClass,
- interface=None,
- use_ipv6=True,
- socketfd=None):
- """If socketfd is set, use that file descriptor instead of
- creating a new one with socket.socket().
- """
- self.interface = interface
- if use_ipv6:
- self.address_family = socket.AF_INET6
- if socketfd is not None:
- # Save the file descriptor
- 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):
- # Restore original function so subsequent calls are
- # not affected.
- socket.socket = self.socket_socket
- del self.socket_socket
- # This time only, return a new socket object from the
- # saved file descriptor.
- return socket.fromfd(self.socketfd, *args, **kwargs)
- # Replace socket.socket() function with wrapper
- socket.socket = socket_wrapper
- # The socketserver.TCPServer.__init__ will call
- # socket.socket(), which might be our replacement,
- # socket_wrapper(), if socketfd was set.
- socketserver.TCPServer.__init__(self, server_address,
- RequestHandlerClass)
-
+ address_family = socket.AF_INET6
+ def __init__(self, *args, **kwargs):
+ if "settings" in kwargs:
+ self.settings = kwargs["settings"]
+ del kwargs["settings"]
+ if "clients" in kwargs:
+ self.clients = kwargs["clients"]
+ del kwargs["clients"]
+ return super(type(self), self).__init__(*args, **kwargs)
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
+ if self.settings["interface"]:
+ # 25 is from /usr/include/asm-i486/socket.h
+ SO_BINDTODEVICE = getattr(socket, "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)
+ self.socket.setsockopt(socket.SOL_SOCKET,
+ SO_BINDTODEVICE,
+ self.settings["interface"])
+ except socket.error, error:
+ if error[0] == errno.EPERM:
+ logger.error(u"No permission to"
+ u" bind to interface %s",
+ self.settings["interface"])
else:
- raise
+ raise error
# 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
- else:
- any_address = "0.0.0.0" # INADDR_ANY
- self.server_address = (any_address,
+ in6addr_any = "::"
+ self.server_address = (in6addr_any,
self.server_address[1])
elif not self.server_address[1]:
- self.server_address = (self.server_address[0], 0)
-# if self.interface:
+ self.server_address = (self.server_address[0],
+ 0)
+# if self.settings["interface"]:
# self.server_address = (self.server_address[0],
# 0, # port
# 0, # flowinfo
# if_nametoindex
-# (self.interface))
- return socketserver.TCPServer.server_bind(self)
-
-
-class MandosServer(IPv6_TCPServer):
- """Mandos server.
-
- Attributes:
- clients: set of Client objects
- gnutls_priority GnuTLS priority string
- use_dbus: Boolean; to emit D-Bus signals or not
-
- Assumes a GLib.MainLoop event loop.
- """
-
- def __init__(self, server_address, RequestHandlerClass,
- interface=None,
- use_ipv6=True,
- clients=None,
- gnutls_priority=None,
- use_dbus=True,
- socketfd=None):
- self.enabled = False
- self.clients = clients
- if self.clients is None:
- self.clients = {}
- self.use_dbus = use_dbus
- self.gnutls_priority = gnutls_priority
- IPv6_TCPServer.__init__(self, server_address,
- RequestHandlerClass,
- 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):
- # error, or the other end of multiprocessing.Pipe has closed
- if condition & (GLib.IO_ERR | GLib.IO_HUP):
- # Wait for other process to exit
- proc.join()
- return False
-
- # Read a request from the child
- request = parent_pipe.recv()
- command = request[0]
-
- if command == 'init':
- key_id = request[1].decode("ascii")
- fpr = request[2].decode("ascii")
- address = request[3]
-
- for c in self.clients.values():
- if key_id and c.key_id == key_id:
- client = c
- break
- if fpr and c.fingerprint == fpr:
- client = c
- break
- else:
- logger.info("Client not found for key ID: %s, address"
- ": %s", key_id or fpr, address)
- if self.use_dbus:
- # Emit D-Bus signal
- mandos_dbus_service.ClientNotFound(key_id or fpr,
- address[0])
- parent_pipe.send(False)
- return False
-
- GLib.io_add_watch(
- parent_pipe.fileno(),
- GLib.IO_IN | GLib.IO_HUP,
- functools.partial(self.handle_ipc,
- parent_pipe=parent_pipe,
- proc=proc,
- client_object=client))
- parent_pipe.send(True)
- # remove the old hook in favor of the new above hook on
- # same fileno
- return False
- if command == 'funcall':
- funcname = request[1]
- args = request[2]
- kwargs = request[3]
-
- parent_pipe.send(('data', getattr(client_object,
- funcname)(*args,
- **kwargs)))
-
- if command == 'getattr':
- attrname = request[1]
- if isinstance(client_object.__getattribute__(attrname),
- collections.Callable):
- parent_pipe.send(('function', ))
- else:
- parent_pipe.send((
- 'data', client_object.__getattribute__(attrname)))
-
- if command == 'setattr':
- attrname = request[1]
- value = request[2]
- setattr(client_object, attrname, value)
-
- return True
-
-
-def rfc3339_duration_to_delta(duration):
- """Parse an RFC 3339 "duration" and return a datetime.timedelta
-
- >>> rfc3339_duration_to_delta("P7D")
- datetime.timedelta(7)
- >>> rfc3339_duration_to_delta("PT60S")
- datetime.timedelta(0, 60)
- >>> rfc3339_duration_to_delta("PT60M")
- datetime.timedelta(0, 3600)
- >>> rfc3339_duration_to_delta("PT24H")
- datetime.timedelta(1)
- >>> rfc3339_duration_to_delta("P1W")
- datetime.timedelta(7)
- >>> rfc3339_duration_to_delta("PT5M30S")
- datetime.timedelta(0, 330)
- >>> 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
- # 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, )))
- token_minute = Token(re.compile(r"(\d+)M"),
- datetime.timedelta(minutes=1),
- frozenset((token_second, token_end)))
- token_hour = Token(re.compile(r"(\d+)H"),
- datetime.timedelta(hours=1),
- frozenset((token_minute, token_end)))
- token_time = Token(re.compile(r"T"),
- None,
- frozenset((token_hour, token_minute,
- token_second)))
- token_day = Token(re.compile(r"(\d+)D"),
- datetime.timedelta(days=1),
- frozenset((token_time, token_end)))
- token_month = Token(re.compile(r"(\d+)M"),
- datetime.timedelta(weeks=4),
- frozenset((token_day, token_end)))
- token_year = Token(re.compile(r"(\d+)Y"),
- datetime.timedelta(weeks=52),
- frozenset((token_month, token_end)))
- token_week = Token(re.compile(r"(\d+)W"),
- datetime.timedelta(weeks=1),
- 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()
- found_token = None
- # Following valid tokens
- followers = frozenset((token_duration, ))
- # String left to parse
- s = duration
- # Loop until end token is found
- while found_token is not token_end:
- # Search for any currently valid tokens
- for token in followers:
- match = token.regexp.match(s)
- if match is not None:
- # Token found
- if token.value is not None:
- # Value found, parse digits
- factor = int(match.group(1), 10)
- # Add to value so far
- value += factor * token.value
- # Strip token from string
- s = token.regexp.sub("", s, 1)
- # Go to found token
- found_token = token
- # Set valid next tokens
- followers = found_token.followers
- break
- else:
- # No currently valid tokens were found
- raise ValueError("Invalid RFC 3339 duration: {!r}"
- .format(duration))
- # End token found
- return value
+# (self.settings
+# ["interface"]))
+ return super(type(self), self).server_bind()
def string_to_delta(interval):
@@ -2863,43 +575,84 @@
datetime.timedelta(0, 3600)
>>> string_to_delta('24h')
datetime.timedelta(1)
- >>> string_to_delta('1w')
+ >>> string_to_delta(u'1w')
datetime.timedelta(7)
>>> 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]
- value = int(s[:-1])
- if suffix == "d":
+ suffix=unicode(s[-1])
+ value=int(s[:-1])
+ if suffix == u"d":
delta = datetime.timedelta(value)
- elif suffix == "s":
+ elif suffix == u"s":
delta = datetime.timedelta(0, value)
- elif suffix == "m":
+ elif suffix == u"m":
delta = datetime.timedelta(0, 0, 0, 0, value)
- elif suffix == "h":
+ elif suffix == u"h":
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
- elif suffix == "w":
+ elif suffix == u"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(*(e.args))
+ raise ValueError
+ except (ValueError, IndexError):
+ raise ValueError
timevalue += delta
return timevalue
-def daemon(nochdir=False, noclose=False):
+def server_state_changed(state):
+ """Derived from the Avahi example code"""
+ if state == avahi.SERVER_COLLISION:
+ logger.error(u"Zeroconf server name collision")
+ service.remove()
+ elif state == avahi.SERVER_RUNNING:
+ service.add()
+
+
+def entry_group_state_changed(state, error):
+ """Derived from the Avahi example code"""
+ logger.debug(u"Avahi state change: %i", state)
+
+ if state == avahi.ENTRY_GROUP_ESTABLISHED:
+ logger.debug(u"Zeroconf service established.")
+ elif state == avahi.ENTRY_GROUP_COLLISION:
+ logger.warning(u"Zeroconf service name collision.")
+ service.rename()
+ elif state == avahi.ENTRY_GROUP_FAILURE:
+ logger.critical(u"Avahi: Error in group state changed %s",
+ unicode(error))
+ raise AvahiGroupError("State changed: %s", str(error))
+
+def if_nametoindex(interface):
+ """Call the C function if_nametoindex(), or equivalent"""
+ global if_nametoindex
+ try:
+ if "ctypes.util" not in sys.modules:
+ import ctypes.util
+ if_nametoindex = ctypes.cdll.LoadLibrary\
+ (ctypes.util.find_library("c")).if_nametoindex
+ except (OSError, AttributeError):
+ if "struct" not in sys.modules:
+ import struct
+ if "fcntl" not in sys.modules:
+ import fcntl
+ def if_nametoindex(interface):
+ "Get an interface index the hard way, i.e. using fcntl()"
+ SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
+ s = socket.socket()
+ ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
+ struct.pack("16s16x", interface))
+ s.close()
+ interface_index = struct.unpack("I", ifreq[16:20])[0]
+ return interface_index
+ return if_nametoindex(interface)
+
+
+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()
@@ -2910,11 +663,10 @@
sys.exit()
if not noclose:
# Close all standard open file descriptors
- null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
+ null = os.open(os.path.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"
- .format(os.devnull))
+ "/dev/null not a character device")
os.dup2(null, sys.stdin.fileno())
os.dup2(null, sys.stdout.fileno())
os.dup2(null, sys.stderr.fileno())
@@ -2923,701 +675,207 @@
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),
- help="show version number and exit")
- parser.add_argument("-i", "--interface", metavar="IF",
- help="Bind to interface IF")
- parser.add_argument("-a", "--address",
- help="Address to listen for requests on")
- parser.add_argument("-p", "--port", type=int,
- help="Port number to receive requests on")
- parser.add_argument("--check", action="store_true",
- help="Run self-test")
- parser.add_argument("--debug", action="store_true",
- help="Debug mode; run in foreground and log"
- " to terminal", default=None)
- parser.add_argument("--debuglevel", metavar="LEVEL",
- help="Debug level for stdout output")
- parser.add_argument("--priority", help="GnuTLS"
- " priority string (see GnuTLS documentation)")
- parser.add_argument("--servicename",
- metavar="NAME", help="Zeroconf service name")
- parser.add_argument("--configdir",
- default="/etc/mandos", metavar="DIR",
- help="Directory to search for configuration"
- " files")
- parser.add_argument("--no-dbus", action="store_false",
- dest="use_dbus", help="Do not provide D-Bus"
- " system bus interface", default=None)
- parser.add_argument("--no-ipv6", action="store_false",
- dest="use_ipv6", help="Do not use IPv6",
- default=None)
- parser.add_argument("--no-restore", action="store_false",
- dest="restore", help="Do not restore stored"
- " state", default=None)
- parser.add_argument("--socket", type=int,
- help="Specify a file descriptor to a network"
- " socket to use instead of creating one")
- parser.add_argument("--statedir", metavar="DIR",
- 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()
-
+ global main_loop_started
+ main_loop_started = False
+
+ parser = OptionParser(version = "%%prog %s" % version)
+ parser.add_option("-i", "--interface", type="string",
+ metavar="IF", help="Bind to interface IF")
+ parser.add_option("-a", "--address", type="string",
+ help="Address to listen for requests on")
+ parser.add_option("-p", "--port", type="int",
+ help="Port number to receive requests on")
+ parser.add_option("--check", action="store_true", default=False,
+ help="Run self-test")
+ parser.add_option("--debug", action="store_true",
+ help="Debug mode; run in foreground and log to"
+ " terminal")
+ parser.add_option("--priority", type="string", help="GnuTLS"
+ " priority string (see GnuTLS documentation)")
+ parser.add_option("--servicename", type="string", metavar="NAME",
+ help="Zeroconf service name")
+ parser.add_option("--configdir", type="string",
+ default="/etc/mandos", metavar="DIR",
+ help="Directory to search for configuration"
+ " files")
+ (options, args) = 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
- if gnutls.has_rawpk:
- priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
- ":!VERS-ALL:+VERS-TLS1.3:%PROFILE_ULTRA")
- else:
- priority = ("SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
- ":+SIGN-DSA-SHA256")
- server_defaults = {"interface": "",
- "address": "",
- "port": "",
- "debug": "False",
- "priority": priority,
- "servicename": "Mandos",
- "use_dbus": "True",
- "use_ipv6": "True",
- "debuglevel": "",
- "restore": "True",
- "socket": "",
- "statedir": "/var/lib/mandos",
- "foreground": "False",
- "zeroconf": "True",
- }
- del priority
-
+ server_defaults = { "interface": "",
+ "address": "",
+ "port": "",
+ "debug": "False",
+ "priority":
+ "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
+ "servicename": "Mandos",
+ }
+
# Parse config file for server-global settings
- server_config = configparser.SafeConfigParser(server_defaults)
+ server_config = ConfigParser.SafeConfigParser(server_defaults)
del server_defaults
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"):
- server_settings[option] = server_config.getboolean("DEFAULT",
- option)
- if server_settings["port"]:
- server_settings["port"] = server_config.getint("DEFAULT",
- "port")
- if server_settings["socket"]:
- server_settings["socket"] = server_config.getint("DEFAULT",
- "socket")
- # Later, stdin will, and stdout and stderr might, be dup'ed
- # over with an opened os.devnull. But we don't want this to
- # happen with a supplied network socket.
- if 0 <= server_settings["socket"] <= 2:
- server_settings["socket"] = os.dup(server_settings
- ["socket"])
+ # Use getboolean on the boolean config option
+ server_settings["debug"] = server_config.getboolean\
+ ("DEFAULT", "debug")
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"):
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"))
- # Force all boolean options to be boolean
- for option in ("debug", "use_dbus", "use_ipv6", "restore",
- "foreground", "zeroconf"):
- 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"]
- use_dbus = server_settings["use_dbus"]
- use_ipv6 = server_settings["use_ipv6"]
- 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:
- if not debuglevel:
- initlogger(debug)
- else:
- level = getattr(logging, debuglevel.upper())
- initlogger(debug, level)
-
+
+ if not debug:
+ syslogger.setLevel(logging.WARNING)
+ console.setLevel(logging.WARNING)
+
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 (%s): %%(levelname)s:'
+ ' %%(message)s'
+ % server_settings["servicename"]))
+
# Parse config file with clients
- client_config = configparser.SafeConfigParser(Client
- .client_defaults)
+ client_defaults = { "timeout": "1h",
+ "interval": "5m",
+ "checker": "fping -q -- %(host)s",
+ "host": "",
+ }
+ client_config = ConfigParser.SafeConfigParser(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)
- if not foreground:
- pidfilename = "/run/mandos.pid"
- if not os.path.isdir("/run/."):
- pidfilename = "/var/run/mandos.pid"
- pidfile = None
- try:
- pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
- 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")):
- try:
- uid = pwd.getpwnam(name).pw_uid
- gid = pwd.getpwnam(group).pw_gid
- break
- except KeyError:
- continue
- else:
- uid = 65534
- gid = 65534
- 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
-
- if debug:
- # Enable all possible GnuTLS debugging
-
- # "Use a log level over 10 to enable all debugging options."
- # - GnuTLS manual
- gnutls.global_set_log_level(11)
-
- @gnutls.log_func
- def debug_gnutls(level, string):
- logger.debug("GnuTLS: %s", string[:-1])
-
- gnutls.global_set_log_function(debug_gnutls)
-
- # Redirect stdin so all checkers get /dev/null
- null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
- os.dup2(null, sys.stdin.fileno())
- if null > 2:
- os.close(null)
-
- # Need to fork before connecting to D-Bus
- if not foreground:
- # Close all input and output, do double fork, etc.
- daemon()
-
- # multiprocessing will use threads, so before we use GLib we need
- # to inform GLib that threads will be used.
- GLib.threads_init()
-
+
+ global service
+ service = AvahiService(name = server_settings["servicename"],
+ type = "_mandos._tcp", );
+ if server_settings["interface"]:
+ service.interface = if_nametoindex\
+ (server_settings["interface"])
+
global main_loop
+ global bus
+ global server
# From the Avahi example code
- DBusGMainLoop(set_as_default=True)
- main_loop = GLib.MainLoop()
+ DBusGMainLoop(set_as_default=True )
+ main_loop = gobject.MainLoop()
bus = dbus.SystemBus()
+ server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
+ avahi.DBUS_PATH_SERVER),
+ avahi.DBUS_INTERFACE_SERVER)
# 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:
- 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"))
-
- global multiprocessing_manager
- multiprocessing_manager = multiprocessing.Manager()
-
- client_class = Client
- if use_dbus:
- 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
- # 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")
- if not value.has_key("key_id"):
- value["key_id"] = ""
- elif not value.has_key("fingerprint"):
- value["fingerprint"] = ""
- # 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"))
- 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)))
- 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)
-
- with PGPEngine() as pgp:
- for client_name, client in clients_data.items():
- # Skip removed clients
- if client_name not in client_settings:
- continue
-
- # Decide which value to use after restoring saved state.
- # We have three different values: Old config file,
- # new config file, and saved state.
- # New config value takes precedence if it differs from old
- # config value, otherwise use saved state.
- for name, value in client_settings[client_name].items():
- try:
- # 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])):
- client[name] = value
- except KeyError:
- pass
-
- # Clients who has passed its expire date can still be
- # enabled if its last checker was successful. A Client
- # whose checker succeeded before we stored its state is
- # assumed to have successfully run all checkers during
- # downtime.
- if client["enabled"]:
- 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))
- 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"]))
- client["enabled"] = False
- else:
- client["expires"] = (
- datetime.datetime.utcnow()
- + client["timeout"])
- logger.debug("Last checker succeeded,"
- " keeping {} enabled".format(
- client_name))
- try:
- 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"])
-
- # Add/remove clients based on new changes made to config
- for client_name in (set(old_client_settings)
- - set(client_settings)):
- del clients_data[client_name]
- for client_name in (set(client_settings)
- - set(old_client_settings)):
- clients_data[client_name] = client_settings[client_name]
-
- # Create all client objects
- for client_name, client in clients_data.items():
- tcp_server.clients[client_name] = client_class(
- name=client_name,
- settings=client,
- server_settings=server_settings)
-
- 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)
- except IOError:
- logger.error("Could not write to file %r with PID %d",
- pidfilename, pid)
+
+ clients = Set()
+ def remove_from_clients(client):
+ clients.remove(client)
+ if not clients:
+ logger.critical(u"No clients left, exiting")
+ sys.exit()
+
+ clients.update(Set(Client(name = section,
+ stop_hook = remove_from_clients,
+ config
+ = dict(client_config.items(section)))
+ for section in client_config.sections()))
+ if not clients:
+ logger.critical(u"No clients defined")
+ sys.exit(1)
+
+ if debug:
+ # Redirect stdin so all checkers get /dev/null
+ null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
+ os.dup2(null, sys.stdin.fileno())
+ if null > 2:
+ os.close(null)
+ else:
+ # No console logging
+ logger.removeHandler(console)
+ # Close all input and output, do double fork, etc.
+ daemon()
+
+ pidfilename = "/var/run/mandos/mandos.pid"
+ pid = os.getpid()
+ try:
+ pidfile = open(pidfilename, "w")
+ pidfile.write(str(pid) + "\n")
+ pidfile.close()
del pidfile
- del pidfilename
-
- for termsig in (signal.SIGHUP, signal.SIGTERM):
- GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
- lambda: main_loop.quit() and False)
-
- if use_dbus:
-
- @alternate_dbus_interfaces(
- {"se.recompile.Mandos": "se.bsnet.fukt.Mandos"})
- class MandosDBusService(DBusObjectWithObjectManager):
- """A D-Bus proxy object"""
-
- def __init__(self):
- dbus.service.Object.__init__(self, bus, "/")
-
- _interface = "se.recompile.Mandos"
-
- @dbus.service.signal(_interface, signature="o")
- def ClientAdded(self, objpath):
- "D-Bus signal"
- pass
-
- @dbus.service.signal(_interface, signature="ss")
- def ClientNotFound(self, key_id, 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"})
- @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()},
- 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():
- if c.dbus_object_path == object_path:
- del tcp_server.clients[c.name]
- c.remove_from_connection()
- # Don't signal the disabling
- c.disable(quiet=True)
- # Emit D-Bus signal for removal
- self.client_removed_signal(c)
- return
- raise KeyError(object_path)
-
- del _interface
-
- @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
- out_signature="a{oa{sa{sv}}}")
- 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
-
+ except IOError, err:
+ logger.error(u"Could not write %s file with PID %d",
+ pidfilename, os.getpid())
+
def cleanup():
"Cleanup function; run on exit"
- if zeroconf:
- service.cleanup()
-
- mp.active_children()
- wn.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():
- 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.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
- os.rename(tempname, stored_state_path)
- except (IOError, OSError) as e:
- if not debug:
- try:
- os.remove(tempname)
- except NameError:
- pass
- if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
- logger.warning("Could not save persistent state: {}"
- .format(os.strerror(e.errno)))
- else:
- logger.warning("Could not save persistent state:",
- exc_info=e)
- raise
-
- # Delete all clients, and settings from config
- while tcp_server.clients:
- name, client = tcp_server.clients.popitem()
- if use_dbus:
- client.remove_from_connection()
- # Don't signal the disabling
- client.disable(quiet=True)
- # Emit D-Bus signal for removal
- if use_dbus:
- mandos_dbus_service.client_removed_signal(client)
- client_settings.clear()
-
+ global group
+ # From the Avahi example code
+ if not group is None:
+ group.Free()
+ group = None
+ # End of Avahi example code
+
+ while clients:
+ client = clients.pop()
+ client.stop_hook = None
+ client.stop()
+
atexit.register(cleanup)
-
- for client in tcp_server.clients.values():
- if use_dbus:
- # Emit D-Bus signal for adding
- mandos_dbus_service.client_added_signal(client)
- # Need to initiate checking of clients
- if client.enabled:
- client.init_checker()
-
- tcp_server.enable()
- tcp_server.server_activate()
-
+
+ if not debug:
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
+ signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
+
+ for client in clients:
+ client.start()
+
+ tcp_server = IPv6_TCPServer((server_settings["address"],
+ server_settings["port"]),
+ tcp_handler,
+ settings=server_settings,
+ clients=clients)
# Find out what port we got
- if zeroconf:
- service.port = tcp_server.socket.getsockname()[1]
- if use_ipv6:
- logger.info("Now listening on address %r, port %d,"
- " flowinfo %d, scope_id %d",
- *tcp_server.socket.getsockname())
- else: # IPv4
- logger.info("Now listening on address %r, port %d",
- *tcp_server.socket.getsockname())
-
- # service.interface = tcp_server.socket.getsockname()[3]
-
+ service.port = tcp_server.socket.getsockname()[1]
+ logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
+ u" scope_id %d" % tcp_server.socket.getsockname())
+
+ #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))
-
- logger.debug("Starting main loop")
+ # From the Avahi example code
+ server.connect_to_signal("StateChanged", server_state_changed)
+ try:
+ server_state_changed(server.GetState())
+ except dbus.exceptions.DBusException, error:
+ logger.critical(u"DBusException: %s", error)
+ 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(u"Starting main loop")
+ main_loop_started = True
main_loop.run()
- except AvahiError as error:
- logger.critical("Avahi Error", exc_info=error)
- cleanup()
+ except AvahiError, error:
+ logger.critical(u"AvahiError: %s" + unicode(error))
sys.exit(1)
except KeyboardInterrupt:
if debug:
- print("", file=sys.stderr)
- logger.debug("Server received KeyboardInterrupt")
- logger.debug("Server exiting")
- # Must run before the D-Bus bus name gets deregistered
- cleanup()
-
+ print
if __name__ == '__main__':
main()
=== modified file 'mandos-clients.conf.xml'
--- mandos-clients.conf.xml 2019-02-09 23:23:26 +0000
+++ mandos-clients.conf.xml 2008-08-25 07:52:35 +0000
@@ -1,54 +1,63 @@
-
+
/etc/mandos/clients.conf">
-
-
-%common;
]>
-
+
- Mandos Manual
+ &CONFNAME;
- Mandos
- &version;
- &TIMESTAMP;
+ &CONFNAME;
+ &VERSION;
Björn
Påhlsson
- belorn@recompile.se
+ belorn@fukt.bsnet.se
Teddy
Hogeborn
- teddy@recompile.se
+ teddy@fukt.bsnet.se
2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
Teddy Hogeborn
Björn Påhlsson
-
+
+
+ This manual page 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 manual page 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
+ .
+
+
-
+
&CONFNAME;
5
@@ -60,11 +69,13 @@
Configuration file for the Mandos server
-
+
- &CONFPATH;
+
+ &CONFPATH;
+
-
+
DESCRIPTION
@@ -72,13 +83,9 @@
>mandos
8, read by it at startup.
The file needs to list all clients that should be able to use
- the service. The settings in this file can be overridden by
- runtime changes to the server, which it saves across restarts.
- (See the section called PERSISTENT STATE
in
- mandos8.) However, any changes to this file (including adding and removing
- clients) will, at startup, override changes done during runtime.
+ the service. All clients listed will be regarded as valid, even
+ if a client was declared invalid in a previous run of the
+ server.
The format starts with a [section
@@ -108,88 +115,79 @@
start time expansion, see .
- Unknown options are ignored. The used options are as follows:
+ Uknown options are ignored. The used options are as follows:
-
+
-
-
-
-
-
- This option is optional.
-
-
- How long to wait for external approval before resorting to
- use the value. The
- default is PT0S
, i.e. not to wait.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- How long an external approval lasts. The default is 1
- second.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- Whether to approve a client by default after
- the . The default
- is True
.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- This option overrides the default shell command that the
- server will use to check if the client is still up. Any
- output of the command will be ignored, only the exit code
- is checked: If the exit code of the command is zero, the
- client is considered up. The command will be run using
- /bin/sh
+
+
+ timeout
+
+ timeout = TIME
+
+
+ The timeout is how long the server will wait for a
+ successful checker run until a client is considered
+ invalid - that is, ineligible to get the data this server
+ holds. By default Mandos will use 1 hour.
+
+
+ The TIME is specified as a
+ space-separated number of values, each of which is a
+ number and a one-character suffix. The suffix must be one
+ of d
, s
, m
,
+ h
, and w
for days, seconds,
+ minutes, hours, and weeks, respectively. The values are
+ added together to give the total time value, so all of
+ 330s
,
+ 110s 110s 110s
, and
+ 5m 30s
will give a value
+ of five minutes and thirty seconds.
+
+
+
+
+
+ interval
+
+ interval = TIME
+
+
+ How often to run the checker to confirm that a client is
+ still up. Note: a new checker will
+ not be started if an old one is still running. The server
+ will wait for a checker to complete until the above
+ timeout
occurs, at which
+ time the client will be marked invalid, and any running
+ checker killed. The default interval is 5 minutes.
+
+
+ The format of TIME is the same
+ as for timeout above.
+
+
+
+
+
+ checker
+
+ checker = COMMAND
+
+
+ This option allows you to override the default shell
+ command that the server will use to check if the client is
+ still up. Any output of the command will be ignored, only
+ the exit code is checked: If the exit code of the command
+ is zero, the client is considered up. The command will be
+ run using /bin/sh
, so
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
@@ -200,139 +198,32 @@
-
-
-
- This option is optional.
-
-
- Extended timeout is an added timeout that is given once
- after a password has been sent successfully to a client.
- The timeout is by default longer than the normal timeout,
- and is used for handling the extra long downtime while a
- machine is booting up. Time to take into consideration
- when changing this value is file system checks and quota
- checks. The default value is 15 minutes.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- This option is required.
-
+ fingerprint
+
+ fingerprint = HEXSTRING
+
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,
- but spaces or upper/lower case are not significant.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- This option sets the certificate key ID that identifies
- the public key that clients authenticate themselves with
- through TLS. The string needs to be in hexadecimal form,
- but spaces or upper/lower case are not significant.
-
-
-
-
-
-
-
-
- This option is optional, but highly
- recommended unless the
- option is modified to a
- non-standard value without %%(host)s
in it.
-
-
- Host name for this client. This is not used by the server
- directly, but can be, and is by default, used by the
- checker. See the option.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- How often to run the checker to confirm that a client is
- still up. Note: a new checker will
- not be started if an old one is still running. The server
- will wait for a checker to complete until the below
- timeout
occurs, at which
- time the client will be disabled, and any running checker
- killed. The default interval is 2 minutes.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- This option is only used if is not
- specified, in which case this option is
- required.
-
-
- Similar to the , except the secret
- data is in an external file. The contents of the file
- should not be base64-encoded, but
- will be sent to clients verbatim.
-
-
- File names of the form ~user/foo/bar
- and $ENVVAR/foo/bar
- are supported.
-
-
-
-
-
-
-
-
- If this option is not specified, the option is required
- to be present.
-
+ through TLS. The string needs to be in hexidecimal form,
+ but spaces or upper/lower case are not significant.
+
+
+
+
+
+ secret
+
+ secret = BASE64_ENCODED_DATA
+
If present, this option must be set to a string of
base64-encoded binary data. It will be decoded and sent
- to the client matching the above
- or . This should, of course,
- be OpenPGP encrypted data, decryptable only by the client.
+ to the client matching the above
+ . This should, of course, be
+ OpenPGP encrypted data, decryptable only by the client.
The program mandos-keygen8 can, using its
@@ -345,50 +236,49 @@
lines is that a line beginning with white space adds to
the value of the previous line, RFC 822-style.
-
-
-
-
-
-
-
- This option is optional.
-
-
- The timeout is how long the server will wait, after a
- successful checker run, until a client is disabled and not
- allowed to get the data this server holds. By default
- Mandos will use 5 minutes. See also the
- option.
-
-
- The TIME is specified as an RFC
- 3339 duration; for example
- P1Y2M3DT4H5M6S
meaning
- one year, two months, three days, four hours, five
- minutes, and six seconds. Some values can be omitted, see
- RFC 3339 Appendix A for details.
-
-
-
-
-
-
-
-
- Whether this client should be enabled by default. The
- default is true
.
+
+ If this option is not specified, the option is used instead, but one of them
+ must be present.
+
+
+
+
+
+ secfile
+
+ secfile = FILENAME
+
+
+ The same as , but the secret data
+ is in an external file. The contents of the file should
+ not be base64-encoded, but will be
+ sent to clients verbatim.
+
+
+ This option is only used, and must be
+ present, if is not specified.
+
+
+
+
+
+ host
+
+ host = STRING
+
+
+ Host name for this client. This is not used by the server
+ directly, but can be, and is by default, used by the
+ checker. See the option.
-
+
EXPANSION
@@ -425,30 +315,10 @@
%%(foo)s
will be replaced by the value of the attribute
foo of the internal
- Client
object in the
- Mandos server. The currently allowed values for
- foo are:
- approval_delay
,
- approval_duration
,
- created
,
- enabled
,
- expires
,
- key_id
,
- fingerprint
,
- host
,
- interval
,
- last_approval_request
,
- last_checked_ok
,
- last_enabled
,
- name
,
- timeout
, and, if using
- D-Bus, dbus_object_path
.
- See the source code for details. Currently, none of these attributes
- except host
are guaranteed
- to be valid in future versions. Therefore, please
- let the authors know of any attributes that are useful so they
- may be preserved to any new versions of this software.
+ Client
object. See the
+ source code for details, and let the authors know of any
+ attributes that are useful so they may be preserved to any new
+ versions of this software.
Note that this means that, in order to include an actual
@@ -457,11 +327,11 @@
percent characters in a row (%%%%
) must be
entered. Also, a bad format here will lead to an immediate
but silent run-time fatal exit; debug
- mode is needed to expose an error of this kind.
+ mode is needed to track down an error of this kind.
-
-
+
+
FILES
@@ -482,7 +352,6 @@
%(foo)s is
obscure.
-
@@ -490,13 +359,12 @@
[DEFAULT]
-timeout = PT5M
-interval = PT2M
-checker = fping -q -- %%(host)s
+timeout = 1h
+interval = 5m
+checker = fping -q -- %(host)s
# Client "foo"
[foo]
-key_id = 788cd77115cd0bb7b2d5e0ae8496f6b48149d5e712c652076b1fd2d957ef7c1f
fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920
secret =
hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234
@@ -515,52 +383,28 @@
4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O
QlnHIvPzEArRQLo=
host = foo.example.org
-interval = PT1M
+interval = 1m
# Client "bar"
[bar]
-key_id = F90C7A81D72D1EA69A51031A91FF8885F36C8B46D155C8C58709A4C99AE9E361
fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27
secfile = /etc/mandos/bar-secret
-timeout = PT15M
-approved_by_default = False
-approval_delay = PT30S
+timeout = 15m
+
-
+
SEE ALSO
- intro
- 8mandos,
- mandos-keygen
- 8,
- mandos.conf
- 5,
- mandos
- 8,
- fping
- 8
+
+ mandos
+ 8,
+ mandos-keygen
+ 8,
+ mandos.conf
+ 5
-
-
-
- RFC 3339: Date and Time on the Internet:
- Timestamps
-
-
-
- The time intervals are in the "duration" format, as
- specified in ABNF in Appendix A of RFC 3339.
-
-
-
-
-
-
-
-
-
=== removed file 'mandos-ctl'
--- mandos-ctl 2019-02-09 23:23:26 +0000
+++ mandos-ctl 1970-01-01 00:00:00 +0000
@@ -1,498 +0,0 @@
-#!/usr/bin/python
-# -*- mode: python; coding: utf-8 -*-
-#
-# Mandos Monitor - Control and monitor the Mandos server
-#
-# Copyright © 2008-2018 Teddy Hogeborn
-# Copyright © 2008-2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos 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.
-#
-# Mandos 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 Mandos. 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
-
-import sys
-import argparse
-import locale
-import datetime
-import re
-import os
-import collections
-import json
-
-import dbus
-
-if sys.version_info.major == 2:
- str = unicode
-
-locale.setlocale(locale.LC_ALL, "")
-
-tablewords = {
- "Name": "Name",
- "Enabled": "Enabled",
- "Timeout": "Timeout",
- "LastCheckedOK": "Last Successful Check",
- "LastApprovalRequest": "Last Approval Request",
- "Created": "Created",
- "Interval": "Interval",
- "Host": "Host",
- "Fingerprint": "Fingerprint",
- "KeyID": "Key ID",
- "CheckerRunning": "Check Is Running",
- "LastEnabled": "Last Enabled",
- "ApprovalPending": "Approval Is Pending",
- "ApprovedByDefault": "Approved By Default",
- "ApprovalDelay": "Approval Delay",
- "ApprovalDuration": "Approval Duration",
- "Checker": "Checker",
- "ExtendedTimeout": "Extended Timeout",
- "Expires": "Expires",
- "LastCheckerStatus": "Last Checker Status",
-}
-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.20"
-
-
-try:
- dbus.OBJECT_MANAGER_IFACE
-except AttributeError:
- dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
-
-
-def milliseconds_to_string(ms):
- td = datetime.timedelta(0, 0, 0, ms)
- return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
- .format(days="{}T".format(td.days) if td.days else "",
- hours=td.seconds // 3600,
- minutes=(td.seconds % 3600) // 60,
- seconds=td.seconds % 60))
-
-
-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")
- datetime.timedelta(0, 60)
- >>> rfc3339_duration_to_delta("PT60M")
- datetime.timedelta(0, 3600)
- >>> rfc3339_duration_to_delta("PT24H")
- datetime.timedelta(1)
- >>> rfc3339_duration_to_delta("P1W")
- datetime.timedelta(7)
- >>> rfc3339_duration_to_delta("PT5M30S")
- datetime.timedelta(0, 330)
- >>> 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
- # 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, )))
- token_minute = Token(re.compile(r"(\d+)M"),
- datetime.timedelta(minutes=1),
- frozenset((token_second, token_end)))
- token_hour = Token(re.compile(r"(\d+)H"),
- datetime.timedelta(hours=1),
- frozenset((token_minute, token_end)))
- token_time = Token(re.compile(r"T"),
- None,
- frozenset((token_hour, token_minute,
- token_second)))
- token_day = Token(re.compile(r"(\d+)D"),
- datetime.timedelta(days=1),
- frozenset((token_time, token_end)))
- token_month = Token(re.compile(r"(\d+)M"),
- datetime.timedelta(weeks=4),
- frozenset((token_day, token_end)))
- token_year = Token(re.compile(r"(\d+)Y"),
- datetime.timedelta(weeks=52),
- frozenset((token_month, token_end)))
- token_week = Token(re.compile(r"(\d+)W"),
- datetime.timedelta(weeks=1),
- 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()
- found_token = None
- # Following valid tokens
- followers = frozenset((token_duration, ))
- # String left to parse
- s = duration
- # Loop until end token is found
- while found_token is not token_end:
- # Search for any currently valid tokens
- for token in followers:
- match = token.regexp.match(s)
- if match is not None:
- # Token found
- if token.value is not None:
- # Value found, parse digits
- factor = int(match.group(1), 10)
- # Add to value so far
- value += factor * token.value
- # Strip token from string
- s = token.regexp.sub("", s, 1)
- # Go to found token
- found_token = token
- # Set valid next tokens
- followers = found_token.followers
- break
- else:
- # No currently valid tokens were found
- raise ValueError("Invalid RFC 3339 duration: {!r}"
- .format(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')
- datetime.timedelta(0, 60)
- >>> string_to_delta('60m')
- datetime.timedelta(0, 3600)
- >>> string_to_delta('24h')
- datetime.timedelta(1)
- >>> string_to_delta('1w')
- datetime.timedelta(7)
- >>> 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))
- elif suffix == "s":
- value += datetime.timedelta(0, int(num))
- elif suffix == "m":
- value += datetime.timedelta(0, 0, 0, 0, int(num))
- elif suffix == "h":
- value += datetime.timedelta(0, 0, 0, 0, 0, int(num))
- elif suffix == "w":
- value += datetime.timedelta(0, 0, 0, 0, 0, 0, int(num))
- elif suffix == "":
- 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:
- return "Yes" if value else "No"
- if keyword in ("Timeout", "Interval", "ApprovalDelay",
- "ApprovalDuration", "ExtendedTimeout"):
- return milliseconds_to_string(value)
- return str(value)
-
- # Create format string to print table rows
- format_string = " ".join("{{{key}:{width}}}".format(
- width=max(len(tablewords[key]),
- max(len(valuetostring(client[key], key))
- for client in clients)),
- key=key)
- 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}))
-
-
-def has_actions(options):
- return any((options.enable,
- options.disable,
- options.bump_timeout,
- options.start_checker,
- options.stop_checker,
- options.is_enabled,
- options.remove,
- options.checker is not None,
- options.timeout is not None,
- options.extended_timeout is not None,
- options.interval is not None,
- options.approved_by_default is not None,
- options.approval_delay is not None,
- options.approval_duration is not None,
- options.host is not None,
- options.secret is not None,
- options.approve,
- options.deny))
-
-
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("--version", action="version",
- version="%(prog)s {}".format(version),
- help="show version number and exit")
- parser.add_argument("-a", "--all", action="store_true",
- help="Select all clients")
- parser.add_argument("-v", "--verbose", action="store_true",
- help="Print all fields")
- parser.add_argument("-j", "--dump-json", action="store_true",
- help="Dump client data in JSON format")
- parser.add_argument("-e", "--enable", action="store_true",
- help="Enable client")
- parser.add_argument("-d", "--disable", action="store_true",
- help="disable client")
- parser.add_argument("-b", "--bump-timeout", action="store_true",
- help="Bump timeout for client")
- parser.add_argument("--start-checker", action="store_true",
- help="Start checker for client")
- parser.add_argument("--stop-checker", action="store_true",
- help="Stop checker for client")
- parser.add_argument("-V", "--is-enabled", action="store_true",
- help="Check if client is enabled")
- parser.add_argument("-r", "--remove", action="store_true",
- help="Remove client")
- parser.add_argument("-c", "--checker",
- help="Set checker command for client")
- parser.add_argument("-t", "--timeout",
- help="Set timeout for client")
- parser.add_argument("--extended-timeout",
- help="Set extended timeout for client")
- parser.add_argument("-i", "--interval",
- help="Set checker interval for client")
- parser.add_argument("--approve-by-default", action="store_true",
- default=None, dest="approved_by_default",
- help="Set client to be approved by default")
- parser.add_argument("--deny-by-default", action="store_false",
- dest="approved_by_default",
- help="Set client to be denied by default")
- parser.add_argument("--approval-delay",
- help="Set delay before client approve/deny")
- 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"),
- help="Set password blob (file) for client")
- parser.add_argument("-A", "--approve", action="store_true",
- help="Approve any current client request")
- parser.add_argument("-D", "--deny", action="store_true",
- help="Deny any current client request")
- parser.add_argument("--check", action="store_true",
- 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.")
- if options.all and not has_actions(options):
- parser.error("--all requires an action.")
-
- if options.check:
- import doctest
- fail_count, test_count = doctest.testmod()
- sys.exit(os.EX_OK if fail_count == 0 else 1)
-
- try:
- bus = dbus.SystemBus()
- mandos_dbus_objc = bus.get_object(busname, server_path)
- except dbus.exceptions.DBusException:
- print("Could not connect to Mandos server", file=sys.stderr)
- sys.exit(1)
-
- mandos_serv = dbus.Interface(mandos_dbus_objc,
- dbus_interface=server_interface)
- mandos_serv_object_manager = dbus.Interface(
- mandos_dbus_objc, dbus_interface=dbus.OBJECT_MANAGER_IFACE)
-
- # block stderr since dbus library prints to stderr
- null = os.open(os.path.devnull, os.O_RDWR)
- stderrcopy = os.dup(sys.stderr.fileno())
- os.dup2(null, sys.stderr.fileno())
- os.close(null)
- try:
- try:
- mandos_clients = {path: ifs_and_props[client_interface]
- for path, ifs_and_props in
- mandos_serv_object_manager
- .GetManagedObjects().items()
- if client_interface in ifs_and_props}
- finally:
- # restore stderr
- os.dup2(stderrcopy, sys.stderr.fileno())
- os.close(stderrcopy)
- except dbus.exceptions.DBusException as e:
- print("Access denied: "
- "Accessing mandos server through D-Bus: {}".format(e),
- file=sys.stderr)
- sys.exit(1)
-
- # Compile dict of (clients: properties) to process
- clients = {}
-
- if options.all or not options.client:
- clients = {bus.get_object(busname, path): properties
- for path, properties in mandos_clients.items()}
- else:
- for name in options.client:
- for path, client in mandos_clients.items():
- if client["Name"] == name:
- client_objc = bus.get_object(busname, path)
- clients[client_objc] = client
- break
- else:
- print("Client not found on server: {!r}"
- .format(name), file=sys.stderr)
- sys.exit(1)
-
- if not has_actions(options) and clients:
- if options.verbose or options.dump_json:
- keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
- "Created", "Interval", "Host", "KeyID",
- "Fingerprint", "CheckerRunning",
- "LastEnabled", "ApprovalPending",
- "ApprovedByDefault", "LastApprovalRequest",
- "ApprovalDelay", "ApprovalDuration",
- "Checker", "ExtendedTimeout", "Expires",
- "LastCheckerStatus")
- 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)
- else:
- # Process each client in the list by all selected options
- for client in clients:
-
- def set_client_prop(prop, value):
- """Set a Client D-Bus property"""
- client.Set(client_interface, prop, value,
- dbus_interface=dbus.PROPERTIES_IFACE)
-
- def set_client_prop_ms(prop, value):
- """Set a Client D-Bus property, converted
- from a string to milliseconds."""
- set_client_prop(prop,
- string_to_delta(value).total_seconds()
- * 1000)
-
- if options.remove:
- mandos_serv.RemoveClient(client.__dbus_object_path__)
- if options.enable:
- set_client_prop("Enabled", dbus.Boolean(True))
- if options.disable:
- set_client_prop("Enabled", dbus.Boolean(False))
- if options.bump_timeout:
- set_client_prop("LastCheckedOK", "")
- if options.start_checker:
- set_client_prop("CheckerRunning", dbus.Boolean(True))
- 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)
- if options.checker is not None:
- set_client_prop("Checker", options.checker)
- if options.host is not None:
- set_client_prop("Host", options.host)
- if options.interval is not None:
- set_client_prop_ms("Interval", options.interval)
- if options.approval_delay is not None:
- set_client_prop_ms("ApprovalDelay",
- options.approval_delay)
- if options.approval_duration is not None:
- set_client_prop_ms("ApprovalDuration",
- options.approval_duration)
- if options.timeout is not None:
- set_client_prop_ms("Timeout", options.timeout)
- if options.extended_timeout is not None:
- set_client_prop_ms("ExtendedTimeout",
- options.extended_timeout)
- if options.secret is not None:
- set_client_prop("Secret",
- dbus.ByteArray(options.secret.read()))
- if options.approved_by_default is not None:
- set_client_prop("ApprovedByDefault",
- dbus.Boolean(options
- .approved_by_default))
- if options.approve:
- client.Approve(dbus.Boolean(True),
- dbus_interface=client_interface)
- elif options.deny:
- client.Approve(dbus.Boolean(False),
- dbus_interface=client_interface)
-
-
-if __name__ == "__main__":
- main()
=== removed file 'mandos-ctl.xml'
--- mandos-ctl.xml 2018-02-08 10:23:55 +0000
+++ mandos-ctl.xml 1970-01-01 00:00:00 +0000
@@ -1,626 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@recompile.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@recompile.se
-
-
-
-
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Control or query the operation of the Mandos server
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- CLIENT
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
- CLIENT
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- CLIENT
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a program to control or
- query the operation of the Mandos server
- mandos8.
-
-
- This program can be used to change client settings, approve or
- deny client requests, and to remove clients from the server.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
-
-
-
-
-
-
- Show a help message and exit
-
-
-
-
-
-
-
-
-
- Enable client(s). An enabled client will be eligble to
- receive its secret.
-
-
-
-
-
-
-
-
-
- Disable client(s). A disabled client will not be eligble
- to receive its secret, and no checkers will be started for
- it.
-
-
-
-
-
-
-
-
- Bump the timeout of the specified client(s), just as if a
- checker had completed successfully for it/them.
-
-
-
-
-
-
-
-
- Start a new checker now for the specified client(s).
-
-
-
-
-
-
-
-
- Stop any running checker for the specified client(s).
-
-
-
-
-
-
-
-
-
- Remove the specified client(s) from the server.
-
-
-
-
-
-
-
-
-
- Set the checker option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the timeout option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
- Set the extended_timeout option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the interval option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the approved_by_default option of
- the specified client(s) to True or
- False, respectively; see
- mandos-clients.conf5.
-
-
-
-
-
-
-
-
- Set the approval_delay option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
- Set the approval_duration option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the host option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the secfile option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Approve client(s) if currently waiting for approval.
-
-
-
-
-
-
-
-
-
- Deny client(s) if currently waiting for approval.
-
-
-
-
-
-
-
-
-
- Make the client-modifying options modify all clients.
-
-
-
-
-
-
-
-
-
- Show all client settings, not just a subset.
-
-
-
-
-
-
-
-
-
- Dump client settings as JSON to standard output.
-
-
-
-
-
-
-
-
-
- Check if a single client is enabled or not, and exit with
- a successful exit status only if the client is enabled.
-
-
-
-
-
-
-
-
- Run self-tests. This includes any unit tests, etc.
-
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program is a small utility to generate new OpenPGP keys for
- new Mandos clients, and to generate sections for inclusion in
- clients.conf on the server.
-
-
-
-
- EXIT STATUS
-
- If the option is used, the exit
- status will be 0 only if the specified client is enabled.
-
-
-
-
- BUGS
-
-
-
-
- EXAMPLE
-
-
- To list all clients:
-
-
- &COMMANDNAME;
-
-
-
-
-
- To list all settings for the clients
- named foo1.example.org
and foo2.example.org
:
-
-
-
-
-&COMMANDNAME; --verbose foo1.example.org foo2.example.org
-
-
-
-
-
-
- To enable all clients:
-
-
- &COMMANDNAME; --enable --all
-
-
-
-
-
- To change timeout and interval value for the clients
- named foo1.example.org
and foo2.example.org
:
-
-
-
-
-&COMMANDNAME; --timeout="5m" --interval="1m" foo1.example.org foo2.example.org
-
-
-
-
-
-
- To approve all clients currently waiting for it:
-
-
- &COMMANDNAME; --approve --all
-
-
-
-
-
- SECURITY
-
- This program must be permitted to access the Mandos server via
- the D-Bus interface. This normally requires the root user, but
- could be configured otherwise by reconfiguring the D-Bus server.
-
-
-
-
- SEE ALSO
-
- intro
- 8mandos,
- mandos
- 8,
- mandos-clients.conf
- 5,
- mandos-monitor
- 8
-
-
-
-
-
-
-
-
-
=== modified file 'mandos-keygen'
--- mandos-keygen 2019-02-09 23:23:26 +0000
+++ mandos-keygen 2008-08-25 03:53:42 +0000
@@ -1,56 +1,47 @@
#!/bin/sh -e
#
-# Mandos key generator - create new keys for a Mandos client
-#
-# Copyright © 2008-2018 Teddy Hogeborn
-# Copyright © 2008-2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
+# Mandos key generator - create a new OpenPGP key for a Mandos client
+#
+# Copyright © 2007-2008 Teddy Hogeborn & 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.
#
-# Mandos is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
+# 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 Mandos. If not, see .
-#
-# Contact the authors at .
-#
-
-VERSION="1.7.20"
-
-KEYDIR="/etc/keys/mandos"
-KEYTYPE=RSA
-KEYLENGTH=4096
-SUBKEYTYPE=RSA
-SUBKEYLENGTH=4096
-KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
+# along with this program. If not, see .
+#
+# Contact the authors at .
+#
+
+VERSION="1.0"
+
+KEYDIR="/etc/mandos"
+KEYTYPE=DSA
+KEYLENGTH=2048
+SUBKEYTYPE=ELG-E
+SUBKEYLENGTH=2048
+KEYNAME="`hostname --fqdn`"
KEYEMAIL=""
-KEYCOMMENT=""
+KEYCOMMENT="Mandos client key"
KEYEXPIRE=0
-TLS_KEYTYPE=ed25519
FORCE=no
-SSH=yes
KEYCOMMENT_ORIG="$KEYCOMMENT"
mode=keygen
-if [ ! -d "$KEYDIR" ]; then
- KEYDIR="/etc/mandos/keys"
-fi
-
# Parse options
-TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:T:fS \
- --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,tls-keytype:,force,no-ssh \
+TEMP=`getopt --options vhd:t:l:n:e:c:x:f \
+ --longoptions version,help,password,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
SECKEYFILE="$KEYDIR/seckey.txt"
PUBKEYFILE="$KEYDIR/pubkey.txt"
-TLS_PRIVKEYFILE="$KEYDIR/tls-privkey.pem"
-TLS_PUBKEYFILE="$KEYDIR/tls-pubkey.pem"
# Check for some invalid values
-if [ ! -d "$KEYDIR" ]; then
+if [ -d "$KEYDIR" ]; then :; else
echo "$KEYDIR not a directory" >&2
exit 1
fi
-if [ ! -r "$KEYDIR" ]; then
- echo "Directory $KEYDIR not readable" >&2
+if [ -w "$KEYDIR" ]; then :; else
+ echo "Directory $KEYDIR not writeable" >&2
+ exit 1
+fi
+
+if [ "$mode" = password -a -e "$KEYDIR/trustdb.gpg.lock" ]; then
+ echo "Key directory has locked trustdb; aborting." >&2
exit 1
fi
if [ "$mode" = keygen ]; then
- if [ ! -w "$KEYDIR" ]; then
- echo "Directory $KEYDIR not writeable" >&2
- exit 1
- fi
if [ -z "$KEYTYPE" ]; then
echo "Empty key type" >&2
exit 1
@@ -157,7 +137,7 @@
echo "Invalid key length" >&2
exit 1
fi
-
+
if [ -z "$KEYEXPIRE" ]; then
echo "Empty key expiration" >&2
exit 1
@@ -169,9 +149,7 @@
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
esac
- if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \
- || [ -e "$TLS_PRIVKEYFILE" ] \
- || [ -e "$TLS_PUBKEYFILE" ]; } \
+ if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ]; } \
&& [ "$FORCE" -eq 0 ]; then
echo "Refusing to overwrite old key files; use --force" >&2
exit 1
@@ -184,120 +162,65 @@
if [ -n "$KEYEMAIL" ]; then
KEYEMAILLINE="Name-Email: $KEYEMAIL"
fi
-
+
# Create temporary gpg batch file
- BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
+ BATCHFILE="`mktemp -t mandos-gpg-batch.XXXXXXXXXX`"
fi
if [ "$mode" = password ]; then
# Create temporary encrypted password file
- SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`"
-fi
-
-# Create temporary key ring directory
-RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`"
+ SECFILE="`mktemp -t mandos-gpg-secfile.XXXXXXXXXX`"
+fi
+
+# Create temporary key rings
+SECRING="`mktemp -t mandos-gpg-secring.XXXXXXXXXX`"
+PUBRING="`mktemp -t mandos-gpg-pubring.XXXXXXXXXX`"
+
+if [ "$mode" = password ]; then
+ # If a trustdb.gpg file does not already exist, schedule it for
+ # deletion when we are done.
+ if ! [ -e "$KEYDIR/trustdb.gpg" ]; then
+ TRUSTDB="$KEYDIR/trustdb.gpg"
+ fi
+fi
# Remove temporary files on exit
trap "
set +e; \
-test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
-shred --remove \"$RINGDIR\"/sec* 2>/dev/null;
-test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
-rm --recursive --force \"$RINGDIR\";
-tty --quiet && stty echo; \
+rm --force $PUBRING ${PUBRING}~ $BATCHFILE $TRUSTDB; \
+shred --remove $SECRING $SECFILE; \
+stty echo; \
" EXIT
-set -e
-
-umask 077
+umask 027
if [ "$mode" = keygen ]; then
# Create batch file for GnuPG
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
Expire-Date: $KEYEXPIRE
#Preferences:
#Handle:
- #%pubring pubring.gpg
- #%secring secring.gpg
- %no-protection
+ %pubring $PUBRING
+ %secring $SECRING
%commit
EOF
- if tty --quiet; then
- cat <<-EOF
- Note: Due to entropy requirements, key generation could take
- anything from a few minutes to SEVERAL HOURS. Please be
- patient and/or supply the system with more entropy if needed.
- EOF
- echo -n "Started: "
- date
- fi
-
- # Backup any old key files
- if cp --backup=numbered --force "$TLS_PRIVKEYFILE" "$TLS_PRIVKEYFILE" \
- 2>/dev/null; then
- shred --remove "$TLS_PRIVKEYFILE"
- fi
- if cp --backup=numbered --force "$TLS_PUBKEYFILE" "$TLS_PUBKEYFILE" \
- 2>/dev/null; then
- rm --force "$TLS_PUBKEYFILE"
- fi
-
- ## Generate TLS private key
-
- # First try certtool from GnuTLS
- if ! certtool --generate-privkey --password='' \
- --outfile "$TLS_PRIVKEYFILE" --sec-param ultra \
- --key-type="$TLS_KEYTYPE" --pkcs8 --no-text 2>/dev/null; then
- # Otherwise try OpenSSL
- if ! openssl genpkey -algorithm X25519 -out \
- /etc/keys/mandos/tls-privkey.pem; then
- rm --force /etc/keys/mandos/tls-privkey.pem
- # None of the commands succeded; give up
- return 1
- fi
- fi
-
- ## TLS public key
-
- # First try certtool from GnuTLS
- if ! certtool --password='' --load-privkey="$TLS_PRIVKEYFILE" \
- --outfile="$TLS_PUBKEYFILE" --pubkey-info --no-text \
- 2>/dev/null; then
- # Otherwise try OpenSSL
- if ! openssl pkey -in "$TLS_PRIVKEYFILE" \
- -out "$TLS_PUBKEYFILE" -pubout; then
- rm --force "$TLS_PUBKEYFILE"
- # None of the commands succeded; give up
- return 1
- fi
- 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 \
+ gpg --no-random-seed-file --quiet --batch --no-tty \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
--gen-key "$BATCHFILE"
rm --force "$BATCHFILE"
-
- if tty --quiet; then
- echo -n "Finished: "
- date
- fi
-
+
# Backup any old key files
if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
2>/dev/null; then
@@ -317,113 +240,64 @@
FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
fi
- # Export key from key rings to key files
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --armor --export-options export-minimal \
- --comment "$FILECOMMENT" --output "$SECKEYFILE" \
- --export-secret-keys
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --armor --export-options export-minimal \
- --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export
+ # Export keys from key rings to key files
+ gpg --no-random-seed-file --quiet --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
+ --export-options export-minimal --comment "$FILECOMMENT" \
+ --output "$SECKEYFILE" --export-secret-keys
+ gpg --no-random-seed-file --quiet --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
+ --export-options export-minimal --comment "$FILECOMMENT" \
+ --output "$PUBKEYFILE" --export
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 \
- --import "$SECKEYFILE"
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always --armor \
- --import "$PUBKEYFILE"
-
+ # Import keys into temporary key rings
+ gpg --no-random-seed-file --quiet --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --homedir "$KEYDIR" --no-permission-warning \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
+ --trust-model always --import "$SECKEYFILE"
+ gpg --no-random-seed-file --quiet --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --homedir "$KEYDIR" --no-permission-warning \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
+ --trust-model always --import "$PUBKEYFILE"
+
# Get fingerprint of key
- FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
- --enable-dsa2 --homedir "$RINGDIR" --trust-model always \
- --fingerprint --with-colons \
- | sed --quiet \
- --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
+ FINGERPRINT="`gpg --no-random-seed-file --quiet --batch --no-tty \
+ --armor --no-default-keyring --no-options --enable-dsa2 \
+ --homedir \"$KEYDIR\" --no-permission-warning \
+ --secret-keyring \"$SECRING\" --keyring \"$PUBRING\" \
+ --trust-model always --fingerprint --with-colons \
+ | sed -n -e '/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
test -n "$FINGERPRINT"
- KEY_ID="$(certtool --key-id --hash=sha256 \
- --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)"
-
- if [ -z "$KEY_ID" ]; then
- KEY_ID=$(openssl pkey -pubin -in /tmp/tls-pubkey.pem \
- -outform der \
- | openssl sha256 \
- | sed --expression='s/^.*[^[:xdigit:]]//')
- fi
- test -n "$KEY_ID"
-
FILECOMMENT="Encrypted password for a Mandos client"
- while [ ! -s "$SECFILE" ]; do
- if [ -n "$PASSFILE" ]; then
- cat "$PASSFILE"
- else
- tty --quiet && stty -echo
- echo -n "Enter passphrase: " >/dev/tty
- read -r first
- tty --quiet && echo >&2
- echo -n "Repeat passphrase: " >/dev/tty
- read -r second
- if tty --quiet; then
- echo >&2
- stty echo
- fi
- if [ "$first" != "$second" ]; then
- echo "Passphrase mismatch" >&2
- touch "$RINGDIR"/mismatch
- else
- echo -n "$first"
- fi
- fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always --armor \
- --encrypt --sign --recipient "$FINGERPRINT" --comment \
- "$FILECOMMENT" > "$SECFILE"
- if [ -e "$RINGDIR"/mismatch ]; then
- rm --force "$RINGDIR"/mismatch
- if tty --quiet; then
- > "$SECFILE"
- else
- exit 1
- fi
- fi
- done
+ stty -echo
+ echo -n "Enter passphrase: " >&2
+ sed -e '1q' \
+ | gpg --no-random-seed-file --batch --no-tty --armor \
+ --no-default-keyring --no-options --enable-dsa2 \
+ --homedir "$KEYDIR" --no-permission-warning \
+ --secret-keyring "$SECRING" --keyring "$PUBRING" \
+ --trust-model always --encrypt --recipient "$FINGERPRINT" \
+ --comment "$FILECOMMENT" \
+ > "$SECFILE"
+ echo >&2
+ stty echo
cat <<-EOF
[$KEYNAME]
host = $KEYNAME
- key_id = $KEY_ID
fingerprint = $FINGERPRINT
secret =
- EOF
- sed --quiet --expression='
+EOF
+ sed -n -e '
/^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
/^$/,${
# Remove 24-bit Radix-64 checksum
@@ -432,10 +306,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
@@ -446,5 +316,9 @@
shred --remove "$SECFILE"
fi
# Remove the key rings
-shred --remove "$RINGDIR"/sec* 2>/dev/null
-rm --recursive --force "$RINGDIR"
+shred --remove "$SECRING"
+rm --force "$PUBRING" "${PUBRING}~"
+# Remove the trustdb, if one did not exist when we started
+if [ -n "$TRUSTDB" ]; then
+ rm --force "$TRUSTDB"
+fi
=== modified file 'mandos-keygen.xml'
--- mandos-keygen.xml 2019-02-09 23:34:15 +0000
+++ mandos-keygen.xml 2008-08-25 03:53:42 +0000
@@ -1,53 +1,62 @@
-
-
-%common;
]>
- Mandos Manual
+ &COMMANDNAME;
- Mandos
- &version;
- &TIMESTAMP;
+ &COMMANDNAME;
+ &VERSION;
Björn
Påhlsson
- belorn@recompile.se
+ belorn@fukt.bsnet.se
Teddy
Hogeborn
- teddy@recompile.se
+ teddy@fukt.bsnet.se
2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
Teddy Hogeborn
Björn Påhlsson
-
+
+
+ This manual page 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 manual page 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
+ .
+
+
-
+
&COMMANDNAME;
8
@@ -56,268 +65,247 @@
&COMMANDNAME;
- Generate key and password for Mandos client and server.
+ Generate keys for password-request
+ 8mandos
-
+
&COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ directory
+
+
+
+ type
+
+
+
+ bits
+
+
+
+ type
+
+
+
+ bits
+
+
+
+ NAME
+
+
+
+ EMAIL
+
+
+
+ COMMENT
+
+
+
+ TIME
+
+
+
+
+
+ &COMMANDNAME;
+
+
+ directory
+
+
+
+ type
+
+
+
+ bits
+
+
+
+ type
+
+
+
+ bits
+
+
+
+ NAME
+
+
+
+ EMAIL
+
+
+
+ COMMENT
+
+
+
+ TIME
+
+
&COMMANDNAME;
+
-
-
-
- FILE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ directory
+
+
+
+ NAME
&COMMANDNAME;
+
-
&COMMANDNAME;
+
-
-
+
DESCRIPTION
&COMMANDNAME; is a program to generate the
- TLS and OpenPGP keys used by
- mandos-client
+ OpenPGP keys used by
+ password-request
8mandos. The keys are
- normally written to /etc/keys/mandos for later installation into
- the initrd image, but this, and most other things, can be
- changed with command line options.
+ normally written to /etc/mandos for later installation into the
+ initrd image, but this, like most things, can be changed with
+ command line options.
- This program can also be used with the
- or
- options to generate a ready-made section for
- clients.conf (see
+ It can also be used to generate ready-made sections for
mandos-clients.conf
- 5).
+ 5 using the
+ option.
PURPOSE
+
The purpose of this is to enable remote and unattended
rebooting of client host computer with an
encrypted root file system. See for details.
+
OPTIONS
-
+
-
-
+ -h, --help
Show a help message and exit
-
-
-
-
-
-
- Target directory for key files. Default is /etc/keys/mandos.
-
-
-
-
-
-
-
-
-
- OpenPGP key type. Default is RSA
.
-
-
-
-
-
-
-
-
-
- OpenPGP key length in bits. Default is 4096.
-
-
-
-
-
-
-
-
-
- OpenPGP subkey type. Default is RSA
-
-
-
-
-
-
-
-
-
- OpenPGP subkey length in bits. Default is 4096.
-
-
-
-
-
-
-
+
+
+ -d, --dir
+ directory
+
+
+ Target directory for key files. Default is
+ /etc/mandos.
+
+
+
+
+
+ -t, --type
+ type
+
+
+ Key type. Default is DSA
.
+
+
+
+
+
+ -l, --length
+ bits
+
+
+ Key length in bits. Default is 2048.
+
+
+
+
+
+ -s, --subtype
+ type
+
+
+ Subkey type. Default is ELG-E
(Elgamal
+ encryption-only).
+
+
+
+
+
+ -L, --sublength
+ bits
+
+
+ Subkey length in bits. Default is 2048.
+
+
+
+
+
+ -e, --email
+ address
Email address of key. Default is empty.
-
+
-
-
+ -c, --comment
+ comment
- Comment field for key. Default is empty.
+ Comment field for key. The default value is
+ Mandos client key
.
-
+
-
-
+ -x, --expire
+ time
Key expire time. Default is no expiration. See
@@ -326,93 +314,50 @@
-
-
-
-
-
-
- TLS key type. Default is ed25519
-
-
-
-
-
-
-
-
-
- Force overwriting old key.
-
-
-
-
-
-
+
+
+ -f, --force
+
+
+ Force overwriting old keys.
+
+
+
+
+ -p, --password
Prompt for a password and encrypt it with the key already
- present in either /etc/keys/mandos or
- the directory specified with the
+ present in either /etc/mandos or the
+ directory specified with the
option. Outputs, on standard output, a section suitable
for inclusion in mandos-clients.conf8. The host name or the name
specified with the option is used
for the section header. All other options are ignored,
- and no key is created.
-
-
-
-
-
-
-
-
- The same as , but read from
- FILE, not the terminal.
-
-
-
-
-
-
-
-
- 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.
+ and no keys are created.
-
+
OVERVIEW
- This program is a small utility to generate new TLS and OpenPGP
- keys for new Mandos clients, and to generate sections for
- inclusion in clients.conf on the server.
+ This program is a small utility to generate new OpenPGP keys for
+ new Mandos clients.
-
+
EXIT STATUS
- The exit status will be 0 if a new key (or password, if the
- option was used) was successfully
- created, otherwise not.
+ The exit status will be 0 if new keys were successfully created,
+ otherwise not.
@@ -420,7 +365,7 @@
ENVIRONMENT
- TMPDIR
+ TMPDIR
If set, temporary files will be created here. See
@@ -432,7 +377,7 @@
-
+
FILES
Use the option to change where
@@ -441,7 +386,7 @@
- /etc/keys/mandos/seckey.txt
+ /etc/mandos/seckey.txt
OpenPGP secret key file which will be created or
@@ -450,7 +395,7 @@
- /etc/keys/mandos/pubkey.txt
+ /etc/mandos/pubkey.txt
OpenPGP public key file which will be created or
@@ -459,23 +404,7 @@
- /etc/keys/mandos/tls-privkey.pem
-
-
- Private key file which will be created or overwritten.
-
-
-
-
- /etc/keys/mandos/tls-pubkey.pem
-
-
- Public key file which will be created or overwritten.
-
-
-
-
- /tmp
+ /tmp
Temporary files will be written here if
@@ -485,12 +414,14 @@
-
+
BUGS
-
+
+ None are known at this time.
+
-
+
EXAMPLE
@@ -498,82 +429,48 @@
Normal invocation needs no options:
- &COMMANDNAME;
+ mandos-keygen
- Create key in another directory and of another type. Force
+ Create keys in another directory and of another type. Force
overwriting old key files:
-&COMMANDNAME; --dir ~/keydir --type RSA --force
-
-
-
-
-
- Prompt for a password, encrypt it with the keys in /etc/keys/mandos and output a
- section suitable for clients.conf.
-
-
- &COMMANDNAME; --password
-
-
-
-
- Prompt for a password, encrypt it with the keys in the
- client-key directory and output a section
- suitable for clients.conf.
-
-
-
-
-&COMMANDNAME; --password --dir client-key
+mandos-keygen --dir ~/keydir --type RSA --force
-
+
SECURITY
The , ,
, and
- options can be used to create keys of low security. If in
- doubt, leave them to the default values.
+ options can be used to create keys of insufficient security. If
+ in doubt, leave them to the default values.
- The key expire time is not guaranteed to be
- honored by mandos
+ The key expire time is not guaranteed to be honored by
+ mandos
8.
-
+
SEE ALSO
- intro
+ password-request
8mandos,
+ mandos
+ 8,
gpg
- 1,
- mandos-clients.conf
- 5,
- mandos
- 8,
- mandos-client
- 8mandos,
- ssh-keyscan
1
-
-
-
-
-
=== removed file 'mandos-monitor'
--- mandos-monitor 2018-08-19 20:17:48 +0000
+++ mandos-monitor 1970-01-01 00:00:00 +0000
@@ -1,746 +0,0 @@
-#!/usr/bin/python
-# -*- mode: python; coding: utf-8 -*-
-#
-# Mandos Monitor - Control and monitor the Mandos server
-#
-# Copyright © 2009-2018 Teddy Hogeborn
-# Copyright © 2009-2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos 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.
-#
-# Mandos 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 Mandos. 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
-
-import sys
-import os
-
-import datetime
-
-import urwid.curses_display
-import urwid
-
-from dbus.mainloop.glib import DBusGMainLoop
-from gi.repository import GLib
-
-import dbus
-
-import locale
-
-import logging
-
-if sys.version_info.major == 2:
- str = unicode
-
-locale.setlocale(locale.LC_ALL, '')
-
-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.20"
-
-try:
- dbus.OBJECT_MANAGER_IFACE
-except AttributeError:
- dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
-
-
-def isoformat_to_datetime(iso):
- "Parse an ISO 8601 date string to a datetime.datetime()"
- if not iso:
- return None
- d, t = iso.split("T", 1)
- year, month, day = d.split("-", 2)
- hour, minute, second = t.split(":", 2)
- second, fraction = divmod(float(second), 1)
- return datetime.datetime(int(year),
- int(month),
- int(day),
- int(hour),
- int(minute),
- int(second), # Whole seconds
- int(fraction*1000000)) # Microseconds
-
-
-class MandosClientPropertyCache(object):
- """This wraps a Mandos Client D-Bus proxy object, caches the
- properties and calls a hook function when any of them are
- changed.
- """
- def __init__(self, proxy_object=None, properties=None, **kwargs):
- self.proxy = proxy_object # Mandos Client proxy object
- self.properties = dict() if properties is None else properties
- self.property_changed_match = (
- self.proxy.connect_to_signal("PropertiesChanged",
- self.properties_changed,
- dbus.PROPERTIES_IFACE,
- byte_arrays=True))
-
- if properties is None:
- self.properties.update(self.proxy.GetAll(
- client_interface,
- dbus_interface=dbus.PROPERTIES_IFACE))
-
- 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.
- """
- # Update properties dict with new value
- if interface == client_interface:
- self.properties.update(properties)
-
- def delete(self):
- self.property_changed_match.remove()
-
-
-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
- self.update_hook = update_hook
- # Called on delete
- self.delete_hook = delete_hook
- # Mandos Server proxy object
- 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
- self._focus_text_widget = urwid.Text("")
- super(MandosClientWidget, self).__init__(**kwargs)
- self.update()
- self.opened = False
-
- self.match_objects = (
- self.proxy.connect_to_signal("CheckerCompleted",
- self.checker_completed,
- client_interface,
- byte_arrays=True),
- self.proxy.connect_to_signal("CheckerStarted",
- self.checker_started,
- client_interface,
- byte_arrays=True),
- self.proxy.connect_to_signal("GotSecret",
- self.got_secret,
- client_interface,
- byte_arrays=True),
- self.proxy.connect_to_signal("NeedApproval",
- self.need_approval,
- client_interface,
- byte_arrays=True),
- self.proxy.connect_to_signal("Rejected",
- self.rejected,
- client_interface,
- byte_arrays=True))
- self.logger('Created client {}'
- .format(self.properties["Name"]), level=0)
-
- def using_timer(self, flag):
- """Call this method with True or False when timer should be
- activated or deactivated.
- """
- 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
- (1000,
- self.update_timer))
- elif not (flag or self._update_timer_callback_tag is None):
- GLib.source_remove(self._update_timer_callback_tag)
- self._update_timer_callback_tag = None
-
- def checker_completed(self, exitstatus, condition, command):
- if exitstatus == 0:
- self.logger('Checker for client {} (command "{}")'
- ' 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 {}'
- .format(self.properties["Name"], command,
- os.WEXITSTATUS(condition)))
- elif os.WIFSIGNALED(condition):
- self.logger('Checker for client {} (command "{}") was'
- ' killed by signal {}'
- .format(self.properties["Name"], command,
- os.WTERMSIG(condition)))
- self.update()
-
- def checker_started(self, command):
- """Server signals that a checker started."""
- self.logger('Client {} started checker "{}"'
- .format(self.properties["Name"],
- command), level=0)
-
- def got_secret(self):
- self.logger('Client {} received its secret'
- .format(self.properties["Name"]))
-
- def need_approval(self, timeout, default):
- if not default:
- message = 'Client {} needs approval within {} seconds'
- else:
- message = 'Client {} will get its secret in {} seconds'
- self.logger(message.format(self.properties["Name"],
- timeout/1000))
-
- def rejected(self, reason):
- self.logger('Client {} was rejected; reason: {}'
- .format(self.properties["Name"], reason))
-
- def selectable(self):
- """Make this a "selectable" widget.
- This overrides the method from urwid.FlowWidget."""
- return True
-
- def rows(self, maxcolrow, focus=False):
- """How many rows this widget will occupy might depend on
- whether we have focus or not.
- This overrides the method from urwid.FlowWidget"""
- return self.current_widget(focus).rows(maxcolrow, focus=focus)
-
- def current_widget(self, focus=False):
- if focus or self.opened:
- return self._focus_widget
- return self._widget
-
- def update(self):
- "Called when what is visible on the screen should be updated."
- # How to add standout mode to a style
- with_standout = {"normal": "standout",
- "bold": "bold-standout",
- "underline-blink":
- "underline-blink-standout",
- "bold-underline-blink":
- "bold-underline-blink-standout",
- }
-
- # 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"])
- last_approval_request = isoformat_to_datetime(
- self.properties["LastApprovalRequest"])
- if last_approval_request is not None:
- timer = max(timeout - (datetime.datetime.utcnow()
- - last_approval_request),
- datetime.timedelta())
- else:
- timer = datetime.timedelta()
- if self.properties["ApprovedByDefault"]:
- message = "Approval in {}. (d)eny?"
- else:
- message = "Denial in {}. (a)pprove?"
- message = message.format(str(timer).rsplit(".", 1)[0])
- self.using_timer(True)
- elif self.properties["LastCheckerStatus"] != 0:
- # When checker has failed, show timer until client expires
- expires = self.properties["Expires"]
- if expires == "":
- timer = datetime.timedelta(0)
- else:
- expires = (datetime.datetime.strptime
- (expires, '%Y-%m-%dT%H:%M:%S.%f'))
- timer = max(expires - datetime.datetime.utcnow(),
- datetime.timedelta())
- message = ('A checker has failed! Time until client'
- ' gets disabled: {}'
- .format(str(timer).rsplit(".", 1)[0]))
- self.using_timer(True)
- else:
- message = "enabled"
- self.using_timer(False)
- self._text = "{}{}".format(base, message)
-
- if not urwid.supports_unicode():
- self._text = self._text.encode("ascii", "replace")
- textlist = [("normal", self._text)]
- self._text_widget.set_text(textlist)
- self._focus_text_widget.set_text([(with_standout[text[0]],
- text[1])
- if isinstance(text, tuple)
- else text
- for text in textlist])
- self._widget = self._text_widget
- self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
- "standout")
- # Run update hook, if any
- if self.update_hook is not None:
- self.update_hook()
-
- def update_timer(self):
- """called by GLib. Will indefinitely loop until
- GLib.source_remove() on tag is called
- """
- self.update()
- return True # Keep calling this
-
- def delete(self, **kwargs):
- if self._update_timer_callback_tag is not None:
- GLib.source_remove(self._update_timer_callback_tag)
- self._update_timer_callback_tag = None
- for match in self.match_objects:
- match.remove()
- self.match_objects = ()
- 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)
- elif key == "-":
- self.proxy.Set(client_interface, "Enabled", False,
- ignore_reply=True,
- dbus_interface=dbus.PROPERTIES_IFACE)
- elif key == "a":
- self.proxy.Approve(dbus.Boolean(True, variant_level=1),
- dbus_interface=client_interface,
- ignore_reply=True)
- elif key == "d":
- self.proxy.Approve(dbus.Boolean(False, variant_level=1),
- 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)
- elif key == "S":
- self.proxy.Set(client_interface, "CheckerRunning",
- dbus.Boolean(False), ignore_reply=True,
- dbus_interface=dbus.PROPERTIES_IFACE)
- elif key == "C":
- self.proxy.CheckedOK(dbus_interface=client_interface,
- ignore_reply=True)
- # xxx
-# elif key == "p" or key == "=":
-# self.proxy.pause()
-# elif key == "u" or key == ":":
-# self.proxy.unpause()
-# elif key == "RET":
-# self.open()
- else:
- return key
-
- def properties_changed(self, interface, properties, invalidated):
- """Call self.update() if any properties changed.
- This overrides the method from MandosClientPropertyCache"""
- old_values = {key: self.properties.get(key)
- for key in properties.keys()}
- super(MandosClientWidget, self).properties_changed(
- interface, properties, invalidated)
- if any(old_values[key] != self.properties.get(key)
- for key in old_values):
- self.update()
-
-
-class ConstrainedListBox(urwid.ListBox):
- """Like a normal urwid.ListBox, but will consume all "up" or
- "down" key presses, thus not allowing any containing widgets to
- use them as an excuse to shift focus away from this widget.
- """
- def keypress(self, *args, **kwargs):
- ret = (super(ConstrainedListBox, self)
- .keypress(*args, **kwargs))
- if ret in ("up", "down"):
- return
- return ret
-
-
-class UserInterface(object):
- """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):
- DBusGMainLoop(set_as_default=True)
-
- self.screen = urwid.curses_display.Screen()
-
- self.screen.register_palette((
- ("normal",
- "default", "default", None),
- ("bold",
- "bold", "default", "bold"),
- ("underline-blink",
- "underline,blink", "default", "underline,blink"),
- ("standout",
- "standout", "default", "standout"),
- ("bold-underline-blink",
- "bold,underline,blink", "default",
- "bold,underline,blink"),
- ("bold-standout",
- "bold,standout", "default", "bold,standout"),
- ("underline-blink-standout",
- "underline,blink,standout", "default",
- "underline,blink,standout"),
- ("bold-underline-blink-standout",
- "bold,underline,blink,standout", "default",
- "bold,underline,blink,standout"),
- ))
-
- if urwid.supports_unicode():
- self.divider = "─" # \u2500
- else:
- self.divider = "_" # \u005f
-
- self.screen.start()
-
- self.size = self.screen.get_cols_rows()
-
- self.clients = urwid.SimpleListWalker([])
- self.clients_dict = {}
-
- # We will add Text widgets to this list
- self.log = []
- self.max_log_length = max_log_length
-
- self.log_level = log_level
-
- # We keep a reference to the log widget so we can remove it
- # from the ListWalker without it getting destroyed
- self.logbox = ConstrainedListBox(self.log)
-
- # This keeps track of whether self.uilist currently has
- # self.logbox in it or not
- self.log_visible = True
- self.log_wrap = "any"
-
- self.rebuild()
- self.log_message_raw(("bold",
- "Mandos Monitor version " + version))
- self.log_message_raw(("bold",
- "q: Quit ?: Help"))
-
- self.busname = domain + '.Mandos'
- self.main_loop = GLib.MainLoop()
-
- def client_not_found(self, fingerprint, address):
- self.log_message("Client with address {} and fingerprint {}"
- " could not be found"
- .format(address, fingerprint))
-
- def rebuild(self):
- """This rebuilds the User Interface.
- Call this when the widget layout needs to change"""
- self.uilist = []
- # self.uilist.append(urwid.ListBox(self.clients))
- self.uilist.append(urwid.Frame(ConstrainedListBox(self.
- clients),
- # header=urwid.Divider(),
- header=None,
- footer=urwid.Divider(
- div_char=self.divider)))
- if self.log_visible:
- self.uilist.append(self.logbox)
- self.topwidget = urwid.Pile(self.uilist)
-
- def log_message(self, message, level=1):
- """Log message formatted with timestamp"""
- if level < self.log_level:
- return
- timestamp = datetime.datetime.now().isoformat()
- self.log_message_raw("{}: {}".format(timestamp, message),
- level=level)
-
- def log_message_raw(self, markup, level=1):
- """Add a log message to the log buffer."""
- if level < self.log_level:
- return
- self.log.append(urwid.Text(markup, wrap=self.log_wrap))
- if self.max_log_length:
- if len(self.log) > self.max_log_length:
- del self.log[0:len(self.log)-self.max_log_length-1]
- self.logbox.set_focus(len(self.logbox.body.contents),
- coming_from="above")
- self.refresh()
-
- def toggle_log_display(self):
- """Toggle visibility of the log buffer."""
- self.log_visible = not self.log_visible
- self.rebuild()
- self.log_message("Log visibility changed to: {}"
- .format(self.log_visible), level=0)
-
- def change_log_display(self):
- """Change type of log display.
- Currently, this toggles wrapping of text lines."""
- if self.log_wrap == "clip":
- self.log_wrap = "any"
- else:
- 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):
- """Find a client by its object path and remove it.
-
- This is connected to the InterfacesRemoved signal from the
- Mandos server object."""
- if client_interface not in interfaces:
- # 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))
- 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
- 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])),
- path=path)
-
- def add_client(self, client, path=None):
- self.clients.append(client)
- if path is None:
- path = client.proxy.object_path
- 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)
- try:
- mandos_clients = (self.mandos_serv
- .GetAllClientsWithProperties())
- if not mandos_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."))
- mandos_clients = dbus.Dictionary()
-
- (self.mandos_serv
- .connect_to_signal("InterfacesRemoved",
- self.find_and_remove_client,
- dbus_interface=dbus.OBJECT_MANAGER_IFACE,
- byte_arrays=True))
- (self.mandos_serv
- .connect_to_signal("InterfacesAdded",
- self.add_new_client,
- dbus_interface=dbus.OBJECT_MANAGER_IFACE,
- byte_arrays=True))
- (self.mandos_serv
- .connect_to_signal("ClientNotFound",
- self.client_not_found,
- dbus_interface=server_interface,
- byte_arrays=True))
- 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),
- path=path)
-
- self.refresh()
- self._input_callback_tag = (GLib.io_add_watch
- (sys.stdin.fileno(),
- GLib.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)
- 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
- }
- 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()
- self.refresh()
- elif key == "l" or key == "D":
- self.toggle_log_display()
- self.refresh()
- elif key == "w" or key == "i":
- self.change_log_display()
- self.refresh()
- elif key == "?" or key == "f1" or key == "esc":
- if not self.log_visible:
- self.log_visible = True
- self.rebuild()
- self.log_message_raw(("bold",
- " ".
- join(("q: Quit",
- "?: Help",
- "l: Log window toggle",
- "TAB: Switch window",
- "w: Wrap (log lines)",
- "v: Toggle verbose log",
- ))))
- self.log_message_raw(("bold",
- " "
- .join(("Clients:",
- "+: Enable",
- "-: Disable",
- "R: Remove",
- "s: Start new checker",
- "S: Stop checker",
- "C: Checker OK",
- "a: Approve",
- "d: Deny"))))
- self.refresh()
- elif key == "tab":
- if self.topwidget.get_focus() is self.logbox:
- self.topwidget.set_focus(0)
- 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 self.topwidget.selectable():
- self.topwidget.keypress(self.size, key)
- self.refresh()
- return True
-
-
-ui = UserInterface()
-try:
- ui.run()
-except KeyboardInterrupt:
- ui.screen.stop()
-except Exception as e:
- ui.log_message(str(e))
- ui.screen.stop()
- raise
=== removed file 'mandos-monitor.xml'
--- mandos-monitor.xml 2018-02-08 10:23:55 +0000
+++ mandos-monitor.xml 1970-01-01 00:00:00 +0000
@@ -1,248 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@recompile.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@recompile.se
-
-
-
-
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Text-based GUI to control the Mandos server.
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is an interactive program to
- monitor and control the operations of the Mandos server (see
- mandos8).
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OVERVIEW
-
-
- This program is used to monitor and control the Mandos server.
- In particular, it can be used to approve Mandos clients which
- have been configured to require approval. It also shows all
- significant events reported by the Mandos server.
-
-
-
-
- KEYS
-
- This program is used to monitor and control the Mandos server.
- In particular, it can be used to approve Mandos clients which
- have been configured to require approval. It also shows all
- significant events reported by the Mandos server.
-
-
- Global Keys
-
- Keys
- Function
-
-
-
- q, Q
- Quit
-
-
- Ctrl-L
- Redraw screen
-
-
- ?, F1
- Show help
-
-
- l, D
- Toggle log window
-
-
- TAB
- Switch window
-
-
- w, i
- Toggle log window line wrap
-
-
- v
- Toggle verbose logging
-
-
- Up, Ctrl-P, k
- Move up a line
-
-
- Down, Ctrl-N, j
- Move down a line
-
-
- PageUp, Meta-V, b
- Move up a page
-
-
- PageDown, Ctrl-V, SPACE, f
- Move down a page
-
-
-
- Client List Keys
-
- Keys
- Function
-
-
-
- +
- Enable client
-
-
- -
- Disable client
-
-
- a
- Approve client
-
-
- d
- Deny client
-
-
- R, _, Ctrl-K
- Remove client
-
-
- s
- Start checker for client
-
-
- S
- Stop checker for client
-
-
- C
- Force a successful check for this client.
-
-
-
-
-
- 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
.
-
-
-
-
-
- EXAMPLE
-
-
- This program takes no options:
-
-
- &COMMANDNAME;
-
-
-
-
-
- SECURITY
-
- This program must be permitted to access the Mandos server via
- the D-Bus interface. This normally requires the root user, but
- could be configured otherwise by reconfiguring the D-Bus server.
-
-
-
-
- SEE ALSO
-
- intro
- 8mandos,
- mandos
- 8,
- mandos-ctl
- 8
-
-
-
-
-
-
-
-
-
=== modified file 'mandos-options.xml'
--- mandos-options.xml 2019-02-09 23:23:26 +0000
+++ mandos-options.xml 2008-08-25 10:41:16 +0000
@@ -5,8 +5,6 @@
@@ -17,8 +15,8 @@
and listen to requests on the specified network interface.
Default is to use all available interfaces. Note: a failure to bind to the specified
- interface is not considered critical, and the server will not
- exit, but instead continue normally.
+ interface is not considered critical, and the server does not
+ exit, but will instead continue normally.
@@ -26,12 +24,10 @@
specified IPv6 address. If a link-local address is specified, an
interface should be set, since a link-local address is only valid
on a single interface. By default, the server will listen to all
- available addresses. If set, this must normally be an IPv6
- address; an IPv4 address can only be specified using IPv4-mapped
- IPv6 address syntax: ::FFFF:192.0.2.3
. (Only if IPv6 usage is
- disabled (see below) must this be an IPv4
- address.)
+ available addresses. If set, this must be an IPv6 address; an
+ IPv4 address can only be specified using IPv4-mapped IPv6 address
+ syntax: ::FFFF:192.0.2.3
.
@@ -47,78 +43,24 @@
- GnuTLS priority string for the TLS handshake.
+ GnuTLS priority string for the TLS handshake with the clients.
The default is
- SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3:%PROFILE_ULTRA
- when using raw public keys in TLS, and
- SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256
- when using OpenPGP keys in TLS,. 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.
+ SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP
.
+ See gnutls_priority_init
+ 3 for the
+ syntax. Warning: changing this may make the
+ TLS handshake fail, making communication with clients impossible.
Zeroconf service name. The default is
Mandos
. This only needs to be
- changed if for some reason is would be necessary to run more than
- one server on the same host. This would not
+ changed this if it, for some reason, is necessary to run more than
+ one server on the same host, which would not
normally be useful. If there are name collisions on the same
network, the newer server will automatically
rename itself to Mandos #2
, and
so on; therefore, this option is not needed in that case.
-
- This option controls whether the server will provide a D-Bus
- system bus interface. The default is to provide such an
- interface.
-
-
-
- This option controls whether the server will use IPv6 sockets and
- addresses. The default is to use IPv6. This option should
- never normally be turned off, even in
- IPv4-only environments. This is because
- mandos-client
- 8mandos will normally use
- IPv6 link-local addresses, and will not be able to find or connect
- to the server if this option is turned off. Only
- advanced users should consider changing this option.
-
-
-
- This option controls whether the server will restore its state
- from the last time it ran. Default is to restore last state.
-
-
-
- Directory to save (and restore) state in. Default is
- /var/lib/mandos
.
-
-
-
- If this option is used, the server will not create a new network
- socket, but will instead use the supplied file descriptor. By
- default, the server will create a new network socket.
-
-
-
- This option will make the server run in the foreground and not
- write a PID file. The default is to not run
- in the foreground, except in mode, which
- 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.
-
-
=== removed file 'mandos-to-cryptroot-unlock'
--- mandos-to-cryptroot-unlock 2018-08-19 14:58:40 +0000
+++ mandos-to-cryptroot-unlock 1970-01-01 00:00:00 +0000
@@ -1,82 +0,0 @@
-#!/bin/sh
-#
-# Script to get password from plugin-runner to cryptroot-unlock
-#
-# Copyright © 2018 Teddy Hogeborn
-# Copyright © 2018 Björn Påhlsson
-#
-# This file is part of Mandos.
-#
-# Mandos 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.
-#
-# Mandos 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 Mandos. If not, see .
-#
-# Contact the authors at .
-
-# This script is made to run in the initramfs, and must not be run in
-# the normal system environment.
-
-# Temporary file for the password
-passfile=$(mktemp -p /run -t mandos.XXXXXX)
-trap "rm -f -- $passfile 2>/dev/null" EXIT
-
-# Disable the plugins which conflict with "askpass" as distributed by
-# cryptsetup.
-cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
-
- --disable=askpass-fifo
- --disable=password-prompt
- --disable=plymouth
-EOF
-
-# In case a password is retrieved by other means than by plugin-runner
-# (such as typing it on the console into the prompt given by the
-# "askpass" program), this dummy plugin will be made to exit
-# successfully, thereby forcing plugin-runner to stop all its plugins
-# and also exit itself.
-cat <<-EOF > /lib/mandos/plugins.d/dummy
- #!/bin/sh
-
- while [ -e /run/mandos-keep-running ]; do
- sleep 1
- done
-
- # exit successfully to force plugin-runner to finish
- exit 0
-EOF
-chmod u=rwx,go=rx /lib/mandos/plugins.d/dummy
-
-# This file is the flag which keeps the dummy plugin running
-touch /run/mandos-keep-running
-
-# Keep running plugin-runner and trying any password, until either a
-# password is accepted by cryptroot-unlock, or plugin-runner fails, or
-# the file /run/mandos-keep-running has been removed.
-while type cryptroot-unlock >/dev/null 2>&1; do
- /lib/mandos/plugin-runner > "$passfile" &
- echo $! > /run/mandos-plugin-runner.pid
- wait %% || break
-
- # Try this password ten times (or ten seconds)
- for loop in 1 2 3 4 5 6 7 8 9 10; do
- if [ -e /run/mandos-keep-running ]; then
- cryptroot-unlock < "$passfile" >/dev/null 2>&1 && break 2
- sleep 1
- else
- break 2
- fi
- done
-done
-
-exec >/dev/null 2>&1
-
-rm -f /run/mandos-plugin-runner.pid /run/mandos-keep-running
=== modified file 'mandos.conf'
--- mandos.conf 2015-07-20 03:03:33 +0000
+++ mandos.conf 2008-08-18 23:55:28 +0000
@@ -4,50 +4,35 @@
# These are the default values for the server, uncomment and change
# them if needed.
+
# If "interface" is set, the server will only listen to a specific
# network interface.
;interface =
+
# If "address" is set, the server will only listen to a specific
# address. This must currently be an IPv6 address; an IPv4 address
# can be specified using the "::FFFF:192.0.2.3" syntax. Also, if this
# is a link-local address, an interface should be set above.
;address =
+
# If "port" is set, the server to bind to that port. By default, the
# server will listen to an arbitrary port.
;port =
+
# If "debug" is true, the server will run in the foreground and print
# a lot of debugging information.
;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
+
# Zeroconf service name. You need to change this if you for some
# reason want to run more than one server on the same *host*.
# If there are name collisions on the same *network*, the server will
# rename itself to "Mandos #2", etc.
;servicename = Mandos
-
-# Whether to provide a D-Bus system bus interface or not
-;use_dbus = True
-
-# Whether to use IPv6. (Changing this is NOT recommended.)
-;use_ipv6 = True
-
-# Whether to restore saved state on startup
-;restore = True
-
-# The directory where state is saved
-;statedir = /var/lib/mandos
-
-# 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 2018-02-08 10:23:55 +0000
+++ mandos.conf.xml 2008-08-25 10:41:16 +0000
@@ -1,54 +1,63 @@
-
+
/etc/mandos/mandos.conf">
-
-
-%common;
]>
- Mandos Manual
+ &CONFNAME;
- Mandos
- &version;
- &TIMESTAMP;
+ &CONFNAME;
+ &VERSION;
Björn
Påhlsson
- belorn@recompile.se
+ belorn@fukt.bsnet.se
Teddy
Hogeborn
- teddy@recompile.se
+ teddy@fukt.bsnet.se
2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
Teddy Hogeborn
Björn Påhlsson
-
+
+
+ This manual page 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 manual page 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
+ .
+
+
-
+
&CONFNAME;
5
@@ -60,11 +69,13 @@
Configuration file for the Mandos server
-
+
- &CONFPATH;
+
+ &CONFPATH;
+
-
+
DESCRIPTION
@@ -82,113 +93,76 @@
#
or ;
are ignored and may be used
to provide comments.
-
+
OPTIONS
-
+ interface
+ interface = NAME
+
-
+
-
+ address
+ address = ADDRESS
+
-
+
-
+ port
+ port = NUMBER
+
-
+
-
+
+ debug = { 1 | yes | true | on | 0 | no | false | off }
-
+ >false | off }
+
-
+
-
+ priority
+ priority = STRING
+
-
+
-
+ servicename
+ servicename = NAME
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -204,9 +178,8 @@
The [DEFAULT] is necessary because the Python
built-in module ConfigParser
- requires it.
+ requres it.
-
@@ -227,15 +200,11 @@
[DEFAULT]
# A configuration example
interface = eth0
-address = fe80::aede:48ff:fe71:f6f2
+address = 2001:db8:f983:bd0b:30de:ae4a:71f2:f672
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
-restore = True
-statedir = /var/lib/mandos
@@ -243,16 +212,15 @@
SEE ALSO
- intro
- 8mandos,
- gnutls_priority_init3,
- mandos
- 8,
- mandos-clients.conf
- 5
+
+ mandos
+ 8,
+ mandos-clients.conf
+ 5,
+ gnutls_priority_init
+ 3
-
+
@@ -278,7 +246,7 @@
The clients use IPv6 link-local addresses, which are
immediately usable since a link-local addresses is
- automatically assigned to a network interface when it
+ automatically assigned to a network interfaces when it
is brought up.
@@ -300,8 +268,3 @@
-
-
-
-
-
=== removed file 'mandos.lsm'
--- mandos.lsm 2018-08-19 20:17:48 +0000
+++ mandos.lsm 1970-01-01 00:00:00 +0000
@@ -1,23 +0,0 @@
-Begin4
-Title: Mandos
-Version: 1.7.20
-Entered-date: 2018-08-19
-Description: The Mandos system allows computers to have encrypted
- root file systems and at the same time be capable of
- remote and/or unattended reboots.
-Keywords: boot, encryption, luks, cryptsetup, network, openpgp,
- 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
- 177K mandos_1.7.20.orig.tar.gz
-Alternate-site: ftp://ftp.recompile.se/pub/mandos
- 177K mandos_1.7.20.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
-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 2019-02-09 23:23:26 +0000
+++ mandos.xml 2008-08-25 10:41:16 +0000
@@ -1,53 +1,62 @@
-
-
-%common;
]>
-
- Mandos Manual
+
+ &COMMANDNAME;
- Mandos
- &version;
- &TIMESTAMP;
+ &COMMANDNAME;
+ &VERSION;
Björn
Påhlsson
- belorn@recompile.se
+ belorn@fukt.bsnet.se
Teddy
Hogeborn
- teddy@recompile.se
+ teddy@fukt.bsnet.se
2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
Teddy Hogeborn
Björn Påhlsson
-
+
+
+ This manual page 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 manual page 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
+ .
+
+
-
+
&COMMANDNAME;
8
@@ -56,154 +65,116 @@
&COMMANDNAME;
- Gives encrypted passwords to authenticated Mandos clients
+ Sends encrypted passwords to authenticated Mandos clients
-
+
&COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ --interfaceNAME
+ --addressADDRESS
+ --portPORT
+ --priorityPRIORITY
+ --servicenameNAME
+ --configdirDIRECTORY
+ --debug
+
+
+ &COMMANDNAME;
+ -iNAME
+ -aADDRESS
+ -pPORT
+ --priorityPRIORITY
+ --servicenameNAME
+ --configdirDIRECTORY
+ --debug
&COMMANDNAME;
-
-
+ -h
+ --help
&COMMANDNAME;
-
+ --version
&COMMANDNAME;
-
+ --check
-
+
DESCRIPTION
&COMMANDNAME; is a server daemon which
handles incoming request for passwords for a pre-defined list of
- client host computers. For an introduction, see
- intro
- 8mandos. The Mandos server
- uses Zeroconf to announce itself on the local network, and uses
- TLS to communicate securely with and to authenticate the
- clients. The Mandos server uses IPv6 to allow Mandos clients to
- use IPv6 link-local addresses, since the clients will probably
- not have any other addresses configured (see ). Any authenticated client is then given
- the stored pre-encrypted password for that specific client.
+ client host computers. The Mandos server uses Zeroconf to
+ announce itself on the local network, and uses TLS to
+ communicate securely with and to authenticate the clients. The
+ Mandos server uses IPv6 to allow Mandos clients to use IPv6
+ link-local addresses, since the clients will probably not have
+ any other addresses configured (see ).
+ Any authenticated client is then given the stored pre-encrypted
+ password for that specific client.
+
PURPOSE
+
The purpose of this is to enable remote and unattended
rebooting of client host computer with an
encrypted root file system. See for details.
+
OPTIONS
+
-
-
+ -h, --help
Show a help message and exit
-
+
-
- NAME
-
- NAME
+ -i, --interface NAME
-
+
-
-
+ -a, --address
+ ADDRESS
-
+
-
-
+ -p, --port
+ PORT
-
+
-
+ --check
Run the server’s self-tests. This includes any unit
@@ -211,52 +182,34 @@
-
+
-
+ --debug
-
-
-
-
-
- Set the debugging log level.
- LEVEL is a string, one of
- CRITICAL
,
- ERROR
,
- WARNING
,
- INFO
, or
- DEBUG
, in order of
- increasing verbosity. The default level is
- WARNING
.
-
-
-
-
-
-
+
+
+ --priority
+ PRIORITY
-
+
-
+ --servicename NAME
+
-
+
-
+ --configdir DIR
+
Directory to search for configuration files. Default is
@@ -268,87 +221,28 @@
-
+
-
+ --version
Prints the program version and exit.
-
-
-
-
-
-
- See also .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- See also .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
OVERVIEW
This program is the server part. It is a normal server program
and will run in a normal system environment, not in an initial
- RAM disk environment.
+ RAM disk environment.
-
+
NETWORK PROTOCOL
@@ -361,11 +255,11 @@
start a TLS protocol handshake with a slight quirk: the Mandos
server program acts as a TLS client
while the
connecting Mandos client acts as a TLS server
.
- The Mandos client must supply a TLS public key, and the key ID
- of this public key is used by the Mandos server to look up (in a
- list read from clients.conf at start time)
- which binary blob to give the client. No other authentication
- or authorization is done by the server.
+ The Mandos client must supply an OpenPGP certificate, and the
+ fingerprint of this certificate is used by the Mandos server to
+ look up (in a list read from clients.conf
+ at start time) which binary blob to give the client. No other
+ authentication or authorization is done by the server.
Mandos Protocol (Version 1)
@@ -391,7 +285,7 @@
- Public key (part of TLS handshake)
+ OpenPGP public key (part of TLS handshake)
->
@@ -406,74 +300,31 @@
-
+
CHECKING
The server will, by default, continually check that the clients
are still up. If a client has not been confirmed as being up
for some time, the client is assumed to be compromised and is no
- longer eligible to receive the encrypted password. (Manual
- intervention is required to re-enable a client.) The timeout,
- extended timeout, checker program, and interval between checks
- can be configured both globally and per client; see
- mandos-clients.conf
+ longer eligible to receive the encrypted password. The timeout,
+ checker program, and interval between checks can be configured
+ both globally and per client; see
+ mandos-clients.conf
5.
-
-
- APPROVAL
-
- The server can be configured to require manual approval for a
- client before it is sent its secret. The delay to wait for such
- approval and the default action (approve or deny) can be
- configured both globally and per client; see
- mandos-clients.conf
- 5. By default all clients
- will be approved immediately without delay.
-
-
- This can be used to deny a client its secret if not manually
- approved within a specified time. It can also be used to make
- the server delay before giving a client its secret, allowing
- optional manual denying of this specific client.
-
-
-
-
+
LOGGING
The server will send log message with various severity levels to
- /dev/log. With the
+ /dev/log. With the
option, it will log even more messages,
and also show them on the console.
-
-
- PERSISTENT STATE
-
- Client settings, initially read from
- clients.conf, are persistent across
- restarts, and run-time changes will override settings in
- clients.conf. However, if a setting is
- changed (or a client added, or removed) in
- clients.conf, this will take precedence.
-
-
-
-
- D-BUS INTERFACE
-
- The server will by default provide a D-Bus system bus interface.
- This interface will only be accessible by the root user or a
- Mandos-specific user, if such a user exists. For documentation
- of the D-Bus API, see the file DBUS-API.
-
-
-
+
EXIT STATUS
@@ -481,12 +332,12 @@
critical error is encountered.
-
+
ENVIRONMENT
- PATH
+ PATH
To start the configured checker (see
-
-
+
+
FILES
Use the option to change where
@@ -531,31 +382,16 @@
- /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.
-
-
-
-
- /var/lib/mandos
-
-
- Directory where persistent state will be saved. Change
- this with the option. See
- also the option.
-
-
-
-
- /dev/log
+ /var/run/mandos/mandos.pid
+
+
+ The file containing the process id of
+ &COMMANDNAME;.
+
+
+
+
+ /dev/log
The Unix domain socket to where local syslog messages are
@@ -584,9 +420,25 @@
backtrace. This could be considered a feature.
+ Currently, if a client is declared invalid
due to
+ having timed out, the server does not record this fact onto
+ permanent storage. This has some security implications, see
+ .
+
+
+ There is currently no way of querying the server of the current
+ status of clients, other than analyzing its syslog output.
+
+
There is no fine-grained control over logging and debug output.
-
+
+ Debug mode is conflated with running in the foreground.
+
+
+ The console log messages does not show a timestamp.
+
@@ -596,20 +448,20 @@
Normal invocation needs no options:
- &COMMANDNAME;
+ mandos
Run the server in debug mode, read configuration files from
- the ~/mandos directory,
- and use the Zeroconf service name Test
to not
- collide with any other official Mandos server on this host:
+ the ~/mandos directory, and use the
+ Zeroconf service name Test
to not collide with
+ any other official Mandos server on this host:
-&COMMANDNAME; --debug --configdir ~/mandos --servicename Test
+mandos --debug --configdir ~/mandos --servicename Test
@@ -621,37 +473,37 @@
-&COMMANDNAME; --interface eth7 --address fe80::aede:48ff:fe71:f6f2
+mandos --interface eth7 --address fe80::aede:48ff:fe71:f6f2
-
+
SECURITY
-
+
SERVER
Running this &COMMANDNAME; server program
should not in itself present any security risk to the host
- computer running it. The program switches to a non-root user
- soon after startup.
+ computer running it. The program does not need any special
+ privileges to run, and is designed to run as a non-root user.
-
+
CLIENTS
The server only gives out its stored data to clients which
- does have the correct key ID of the stored key ID. This is
- guaranteed by the fact that the client sends its public key in
- the TLS handshake; this ensures it to be genuine. The server
- computes the key ID of the key itself and looks up the key ID
- in its list of clients. The clients.conf
- file (see
+ does have the OpenPGP key of the stored fingerprint. This is
+ guaranteed by the fact that the client sends its OpenPGP
+ public key in the TLS handshake; this ensures it to be
+ genuine. The server computes the fingerprint of the key
+ itself and looks up the fingerprint in its list of
+ clients. The clients.conf file (see
mandos-clients.conf
5)
must be made non-readable by anyone
- except the user starting the server (usually root).
+ except the user running the server.
As detailed in , the status of all
@@ -659,26 +511,41 @@
compromised if they are gone for too long.
+ If a client is compromised, its downtime should be duly noted
+ by the server which would therefore declare the client
+ invalid. But if the server was ever restarted, it would
+ re-read its client list from its configuration file and again
+ regard all clients therein as valid, and hence eligible to
+ receive their passwords. Therefore, be careful when
+ restarting servers if it is suspected that a client has, in
+ fact, been compromised by parties who may now be running a
+ fake Mandos client with the keys from the non-encrypted
+ initial RAM image of the client host. What should be done in
+ that case (if restarting the server program really is
+ necessary) is to stop the server program, edit the
+ configuration file to omit any suspect clients, and restart
+ the server program.
+
+
For more details on client-side security, see
- mandos-client
+ password-request
8mandos.
-
+
SEE ALSO
- intro
- 8mandos,
- mandos-clients.conf
- 5,
- mandos.conf
- 5,
- mandos-client
- 8mandos,
- sh
- 1
+
+ mandos.conf
+ 5,
+ mandos-clients.conf
+ 5,
+ password-request
+ 8mandos,
+ sh1
+
@@ -705,13 +572,14 @@
- GnuTLS
+ GnuTLS
GnuTLS is the library this server uses to implement TLS for
communicating securely with the client, and at the same time
- confidently get the client’s public key.
+ confidently get the client’s public OpenPGP key.
@@ -749,12 +617,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.
@@ -770,36 +638,16 @@
- RFC 7250: Using Raw Public Keys in Transport
- Layer Security (TLS) and Datagram Transport Layer Security
- (DTLS)
-
-
-
- This is implemented by GnuTLS version 3.6.6 and is, if
- present, used by this server so that raw public keys can be
- used.
-
-
-
-
-
- RFC 6091: Using OpenPGP Keys for Transport Layer
- Security (TLS) Authentication
-
-
-
- This is implemented by GnuTLS before version 3.6.0 and is,
- if present, used by this server so that OpenPGP keys can be
- used.
+ RFC 5081: Using OpenPGP Keys for Transport Layer
+ Security
+
+
+
+ This is implemented by GnuTLS and used by this server so
+ that OpenPGP keys can be used.
-
-
-
-
-
=== removed directory 'network-hooks.d'
=== removed file 'network-hooks.d/bridge'
--- network-hooks.d/bridge 2018-02-08 10:23:55 +0000
+++ network-hooks.d/bridge 1970-01-01 00:00:00 +0000
@@ -1,93 +0,0 @@
-#!/bin/sh
-#
-# This is an example of a Mandos client network hook. This hook
-# brings up a bridge interface as specified in a separate
-# configuration file. To be used, this file and any needed
-# configuration file(s) should be copied into the
-# /etc/mandos/network-hooks.d directory.
-#
-# Copyright © 2012-2018 Teddy Hogeborn
-# Copyright © 2012-2018 Björn Påhlsson
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved. This file is offered as-is,
-# without any warranty.
-
-set -e
-
-CONFIG="$MANDOSNETHOOKDIR/bridge.conf"
-
-addrtoif(){
- grep -liFe "$1" /sys/class/net/*/address \
- | sed -e 's,.*/\([^/]*\)/[^/]*,\1,' -e "/^${BRIDGE}\$/d"
-}
-
-# Read config file, which must set "BRIDGE", "PORT_ADDRESSES", and
-# optionally "IPADDRS" and "ROUTES".
-if [ -e "$CONFIG" ]; then
- . "$CONFIG"
-fi
-
-if [ -z "$BRIDGE" ] || [ -z "$PORT_ADDRESSES" ]; then
- exit
-fi
-
-if [ -n "$DEVICE" ]; then
- case "$DEVICE" in
- *,"$BRIDGE"|*,"$BRIDGE",*|"$BRIDGE",*|"$BRIDGE") :;;
- *) exit;;
- esac
-fi
-
-brctl="/sbin/brctl"
-for b in "$brctl" /usr/sbin/brctl; do
- if [ -e "$b" ]; then
- brctl="$b"
- break
- fi
-done
-
-do_start(){
- "$brctl" addbr "$BRIDGE"
- for address in $PORT_ADDRESSES; do
- interface=`addrtoif "$address"`
- "$brctl" addif "$BRIDGE" "$interface"
- ip link set dev "$interface" up
- done
- ip link set dev "$BRIDGE" up
- sleep "${DELAY%%.*}"
- if [ -n "$IPADDRS" ]; then
- for ipaddr in $IPADDRS; do
- ip addr add "$ipaddr" dev "$BRIDGE"
- done
- fi
- if [ -n "$ROUTES" ]; then
- for route in $ROUTES; do
- ip route add "$route" dev "$BRIDGE"
- done
- fi
-}
-
-do_stop(){
- ip link set dev "$BRIDGE" down
- for address in $PORT_ADDRESSES; do
- interface=`addrtoif "$address"`
- ip link set dev "$interface" down
- "$brctl" delif "$BRIDGE" "$interface"
- done
- "$brctl" delbr "$BRIDGE"
-}
-
-case "${MODE:-$1}" in
- start|stop)
- do_"${MODE:-$1}"
- ;;
- files)
- echo /bin/ip
- echo "$brctl"
- ;;
- modules)
- echo bridge
- ;;
-esac
=== removed file 'network-hooks.d/bridge.conf'
--- network-hooks.d/bridge.conf 2011-12-31 13:25:58 +0000
+++ network-hooks.d/bridge.conf 1970-01-01 00:00:00 +0000
@@ -1,11 +0,0 @@
-## Required
-
-#BRIDGE=br0
-
-#PORT_ADDRESSES="00:11:22:33:44:55 11:22:33:44:55:66"
-
-## Optional
-
-#IPADDRS="192.0.2.3/24 2001:DB8::aede:48ff:fe71:f6f2/32"
-
-#ROUTES="192.0.2.0/24 2001:DB8::/32"
=== removed file 'network-hooks.d/openvpn'
--- network-hooks.d/openvpn 2018-02-08 10:23:55 +0000
+++ network-hooks.d/openvpn 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
-#!/bin/sh
-#
-# This is an example of a Mandos client network hook. This hook
-# brings up an OpenVPN interface as specified in a separate
-# configuration file. To be used, this file and any needed
-# configuration file(s) should be copied into the
-# /etc/mandos/network-hooks.d directory.
-#
-# Copyright © 2012-2018 Teddy Hogeborn
-# Copyright © 2012-2018 Björn Påhlsson
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved. This file is offered as-is,
-# without any warranty.
-
-set -e
-
-CONFIG="openvpn.conf"
-
-# Extract the "dev" setting from the config file
-VPNDEVICE=`sed -n -e 's/[[:space:]]#.*//' \
- -e 's/^[[:space:]]*dev[[:space:]]\+//p' \
- "$MANDOSNETHOOKDIR/$CONFIG"`
-
-PIDFILE=/run/openvpn-mandos.pid
-
-# Exit if no device set in config
-if [ -z "$VPNDEVICE" ]; then
- exit
-fi
-
-# Exit if DEVICE is set and it doesn't match the VPN interface
-if [ -n "$DEVICE" ]; then
- case "$DEVICE" in
- *,"$VPNDEVICE"*|"$VPNDEVICE"*) :;;
- *) exit;;
- esac
-fi
-
-openvpn=/usr/sbin/openvpn
-
-do_start(){
- "$openvpn" --cd "$MANDOSNETHOOKDIR" --daemon 'openvpn(Mandos)' \
- --writepid "$PIDFILE" --config "$CONFIG"
- sleep "$DELAY"
-}
-
-do_stop(){
- PID="`cat \"$PIDFILE\"`"
- if [ "$PID" -gt 0 ]; then
- kill "$PID"
- fi
-}
-
-case "${MODE:-$1}" in
- start|stop)
- do_"${MODE:-$1}"
- ;;
- files)
- echo "$openvpn"
- ;;
- modules)
- echo tun
- ;;
-esac
=== removed file 'network-hooks.d/openvpn.conf'
--- network-hooks.d/openvpn.conf 2011-12-02 16:52:50 +0000
+++ network-hooks.d/openvpn.conf 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
-# Sample OpenVPN configuration file
-# Uncomment and change - see openvpn(8)
-
-# Network device.
-#dev tun
-
-# Our remote peer
-#remote 192.0.2.3
-#float 192.0.2.3
-#port 1194
-
-# VPN endpoints
-#ifconfig 10.1.0.1 10.1.0.2
-
-# A pre-shared static key
-#secret openvpn.key
-
-# Cipher
-#cipher AES-128-CBC
=== removed file 'network-hooks.d/wireless'
--- network-hooks.d/wireless 2018-02-08 10:23:55 +0000
+++ network-hooks.d/wireless 1970-01-01 00:00:00 +0000
@@ -1,165 +0,0 @@
-#!/bin/sh
-#
-# This is an example of a Mandos client network hook. This hook
-# brings up a wireless interface as specified in a separate
-# configuration file. To be used, this file and any needed
-# configuration file(s) should be copied into the
-# /etc/mandos/network-hooks.d directory.
-#
-# Copyright © 2012-2018 Teddy Hogeborn
-# Copyright © 2012-2018 Björn Påhlsson
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved. This file is offered as-is,
-# without any warranty.
-
-set -e
-
-RUNDIR="/run"
-CTRL="$RUNDIR/wpa_supplicant-global"
-CTRLDIR="$RUNDIR/wpa_supplicant"
-PIDFILE="$RUNDIR/wpa_supplicant-mandos.pid"
-
-CONFIG="$MANDOSNETHOOKDIR/wireless.conf"
-
-addrtoif(){
- grep -liFe "$1" /sys/class/net/*/address \
- | sed -e 's,.*/\([^/]*\)/[^/]*,\1,'
-}
-
-# Read config file
-if [ -e "$CONFIG" ]; then
- . "$CONFIG"
-else
- exit
-fi
-
-ifkeys=`sed -n -e 's/^ADDRESS_\([^=]*\)=.*/\1/p' "$CONFIG" | sort -u`
-
-# Exit if DEVICE is set and is not any of the wireless interfaces
-if [ -n "$DEVICE" ]; then
- while :; do
- for KEY in $ifkeys; do
- ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
- INTERFACE=`addrtoif "$ADDRESS"`
-
- case "$DEVICE" in
- *,"$INTERFACE"|*,"$INTERFACE",*|"$INTERFACE",*|"$INTERFACE")
- break 2;;
- esac
- done
- exit
- done
-fi
-
-wpa_supplicant=/sbin/wpa_supplicant
-wpa_cli=/sbin/wpa_cli
-ip=/bin/ip
-
-# Used by the wpa_interface_* functions in the wireless.conf file
-wpa_cli_set(){
- case "$1" in
- ssid|psk) arg="\"$2\"" ;;
- *) arg="$2" ;;
- esac
- "$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" set_network "$NETWORK" \
- "$1" "$arg" 2>&1 | sed -e '/^OK$/d'
-}
-
-if [ $VERBOSITY -gt 0 ]; then
- WPAS_OPTIONS="-d $WPAS_OPTIONS"
-fi
-if [ -n "$PIDFILE" ]; then
- WPAS_OPTIONS="-P$PIDFILE $WPAS_OPTIONS"
-fi
-
-do_start(){
- mkdir -m u=rwx,go= -p "$CTRLDIR"
- "$wpa_supplicant" -B -g "$CTRL" -p "$CTRLDIR" $WPAS_OPTIONS
- for KEY in $ifkeys; do
- ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
- INTERFACE=`addrtoif "$ADDRESS"`
- DRIVER=`eval 'echo "$WPA_DRIVER_'"$KEY"\"`
- IFDELAY=`eval 'echo "$DELAY_'"$KEY"\"`
- "$wpa_cli" -g "$CTRL" interface_add "$INTERFACE" "" \
- "${DRIVER:-wext}" "$CTRLDIR" > /dev/null \
- | sed -e '/^OK$/d'
- NETWORK=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" add_network`
- eval wpa_interface_"$KEY"
- "$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" enable_network \
- "$NETWORK" | sed -e '/^OK$/d'
- sleep "${IFDELAY:-$DELAY}" &
- sleep=$!
- while :; do
- kill -0 $sleep 2>/dev/null || break
- STATE=`"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" status \
- | sed -n -e 's/^wpa_state=//p'`
- if [ "$STATE" = COMPLETED ]; then
- while :; do
- kill -0 $sleep 2>/dev/null || break 2
- UP=`cat /sys/class/net/"$INTERFACE"/operstate`
- if [ "$UP" = up ]; then
- kill $sleep 2>/dev/null
- break 2
- fi
- sleep 1
- done
- fi
- sleep 1
- done &
- wait $sleep || :
- IPADDRS=`eval 'echo "$IPADDRS_'"$KEY"\"`
- if [ -n "$IPADDRS" ]; then
- if [ "$IPADDRS" = dhcp ]; then
- ipconfig -c dhcp -d "$INTERFACE" || :
- #dhclient "$INTERFACE"
- else
- for ipaddr in $IPADDRS; do
- "$ip" addr add "$ipaddr" dev "$INTERFACE"
- done
- fi
- fi
- ROUTES=`eval 'echo "$ROUTES_'"$KEY"\"`
- if [ -n "$ROUTES" ]; then
- for route in $ROUTES; do
- "$ip" route add "$route" dev "$INTERFACE"
- done
- fi
- done
-}
-
-do_stop(){
- "$wpa_cli" -g "$CTRL" terminate 2>&1 | sed -e '/^OK$/d'
- for KEY in $ifkeys; do
- ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"`
- INTERFACE=`addrtoif "$ADDRESS"`
- "$ip" addr show scope global permanent dev "$INTERFACE" \
- | while read type addr rest; do
- case "$type" in
- inet|inet6)
- "$ip" addr del "$addr" dev "$INTERFACE"
- ;;
- esac
- done
- "$ip" link set dev "$INTERFACE" down
- done
-}
-
-case "${MODE:-$1}" in
- start|stop)
- do_"${MODE:-$1}"
- ;;
- files)
- echo "$wpa_supplicant"
- echo "$wpa_cli"
- echo "$ip"
- ;;
- modules)
- if [ "$IPADDRS" = dhcp ]; then
- echo af_packet
- fi
- sed -n -e 's/#.*$//' -e 's/[ ]*$//' \
- -e 's/^MODULE_[^=]\+=//p' "$CONFIG"
- ;;
-esac
=== removed file 'network-hooks.d/wireless.conf'
--- network-hooks.d/wireless.conf 2011-12-31 13:25:58 +0000
+++ network-hooks.d/wireless.conf 1970-01-01 00:00:00 +0000
@@ -1,23 +0,0 @@
-# Extra options for wpa_supplicant, if any
-#WPAS_OPTIONS=""
-
-# wlan0
-ADDRESS_0=00:11:22:33:44:55
-MODULE_0=ath9k
-#WPA_DRIVER_0=wext
-wpa_interface_0(){
- # Use this format to set simple things:
- wpa_cli_set ssid home
- wpa_cli_set psk "secret passphrase"
- # Use this format to do more complex things with wpa_cli:
- #"$wpa_cli" -p "$CTRLDIR" -i "$INTERFACE" bssid "$NETWORK" 00:11:22:33:44:55
- #"$wpa_cli" -g "$CTRL" ping
-}
-#DELAY_0=10
-IPADDRS_0=dhcp
-#IPADDRS_0="192.0.2.3/24 2001:DB8::aede:48ff:fe71:f6f2/32"
-#ROUTES_0="192.0.2.0/24 2001:DB8::/32"
-
-#ADDRESS_1=11:22:33:44:55:66
-#MODULE_1=...
-#...
=== modified file 'overview.xml'
--- overview.xml 2019-02-09 23:23:26 +0000
+++ overview.xml 2008-08-23 07:17:28 +0000
@@ -1,17 +1,15 @@
-
This is part of the Mandos system for allowing computers to have
- encrypted root file systems and at the same time be capable of
- remote and/or unattended reboots. The computers run a small client
- program in the initial RAM disk environment which
- will communicate with a server over a network. All network
- communication is encrypted using TLS. The
- clients are identified by the server using a TLS key; each client
- has one unique to it. The server sends the clients an encrypted
- password. The encrypted password is decrypted by the clients using
- a separate OpenPGP key, and the password is then used to unlock the
- root file system, whereupon the computers can continue booting
- normally.
+ encrypted root file systems and also be capable of remote and
+ unattended reboots. The computers run a small client program in the
+ initial RAM disk environment which will communicate with a server
+ over a network. The clients are identified by the server using a
+ OpenPGP key; each client has one unique to it. The server sends the
+ clients an encrypted password. The encrypted password is decrypted
+ by the clients using the same OpenPGP key, and the password is then
+ used to unlock the root file system, whereupon the computers can
+ continue booting normally.
=== removed directory 'plugin-helpers'
=== removed file 'plugin-helpers/mandos-client-iprouteadddel.c'
--- plugin-helpers/mandos-client-iprouteadddel.c 2018-02-18 01:29:21 +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-2018 Teddy Hogeborn
- * Copyright © 2015-2018 Björn Påhlsson
- *
- * This file is part of Mandos.
- *
- * Mandos 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.
- *
- * Mandos 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 Mandos. If not, see .
- *
- * Contact the authors at .
- */
-
-#define _GNU_SOURCE /* 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 to 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 2018-08-19 01:03:28 +0000
+++ plugin-runner.c 2008-08-25 07:53:43 +0000
@@ -1,59 +1,57 @@
-/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/* -*- coding: utf-8 -*- */
/*
* Mandos plugin runner - Run Mandos plugins
*
- * Copyright © 2008-2018 Teddy Hogeborn
- * Copyright © 2008-2018 Björn Påhlsson
- *
- * This file is part of Mandos.
- *
- * Mandos 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.
- *
- * Mandos is distributed in the hope that it will be useful, but
+ * Copyright © 2007-2008 Teddy Hogeborn & 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 Mandos. If not, see .
+ * along with this program. If not, see
+ * .
*
- * Contact the authors at .
+ * Contact the authors at .
*/
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(),
- O_CLOEXEC, pipe2() */
+ asprintf() */
#include /* size_t, NULL */
-#include /* malloc(), exit(), EXIT_SUCCESS,
- realloc() */
+#include /* malloc(), exit(), EXIT_FAILURE,
+ 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() */
+#include /* perror, popen(), fileno(),
+ fprintf(), stderr, STDOUT_FILENO */
+#include /* DIR, opendir(), 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() */
+#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, opendir(),
+ 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() */
#include /* errno */
#include /* struct argp_option, struct
argp_state, struct argp,
@@ -63,24 +61,30 @@
#include /* struct sigaction, sigemptyset(),
sigaddset(), sigaction(),
sigprocmask(), SIG_BLOCK, SIGCHLD,
- SIG_UNBLOCK, kill(), sig_atomic_t
- */
+ SIG_UNBLOCK, kill() */
#include /* errno, EBADF */
-#include /* intmax_t, PRIdMAX, strtoimax() */
-#include /* EX_OSERR, EX_USAGE, EX_IOERR,
- 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;
-const char *argp_program_bug_address = "";
+const char *argp_program_version = "plugin-runner 1.0";
+const char *argp_program_bug_address = "";
+
+struct process;
+
+typedef struct process{
+ pid_t pid;
+ int fd;
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_length;
+ bool eof;
+ volatile bool completed;
+ volatile int status;
+ struct process *next;
+} process;
typedef struct plugin{
char *name; /* can be NULL or any plugin name */
@@ -89,109 +93,69 @@
char **environ;
int envc;
bool disabled;
-
- /* Variables used for running processes*/
- pid_t pid;
- int fd;
- char *buffer;
- size_t buffer_size;
- size_t buffer_length;
- bool eof;
- volatile sig_atomic_t completed;
- int status;
struct plugin *next;
} plugin;
-static plugin *plugin_list = NULL;
-
-/* 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){
- if((p->name == name)
- or (p->name and name and (strcmp(p->name, name) == 0))){
+static plugin *getplugin(char *name, plugin **plugin_list){
+ for (plugin *p = *plugin_list; p != NULL; p = p->next){
+ if ((p->name == name)
+ or (p->name and name and (strcmp(p->name, name) == 0))){
return p;
}
}
/* Create a new plugin */
- plugin *new_plugin = NULL;
- do {
- new_plugin = malloc(sizeof(plugin));
- } while(new_plugin == NULL and errno == EINTR);
- if(new_plugin == NULL){
+ plugin *new_plugin = malloc(sizeof(plugin));
+ if (new_plugin == NULL){
return NULL;
}
char *copy_name = NULL;
if(name != NULL){
- do {
- copy_name = strdup(name);
- } while(copy_name == NULL and errno == EINTR);
+ copy_name = strdup(name);
if(copy_name == NULL){
- int e = errno;
- free(new_plugin);
- errno = e;
return NULL;
}
}
- *new_plugin = (plugin){ .name = copy_name,
- .argc = 1,
- .disabled = false,
- .next = plugin_list };
+ *new_plugin = (plugin) { .name = copy_name,
+ .argc = 1,
+ .envc = 0,
+ .disabled = false,
+ .next = *plugin_list };
- do {
- new_plugin->argv = malloc(sizeof(char *) * 2);
- } while(new_plugin->argv == NULL and errno == EINTR);
- if(new_plugin->argv == NULL){
- int e = errno;
+ new_plugin->argv = malloc(sizeof(char *) * 2);
+ if (new_plugin->argv == NULL){
free(copy_name);
free(new_plugin);
- errno = e;
return NULL;
}
new_plugin->argv[0] = copy_name;
new_plugin->argv[1] = NULL;
-
- do {
- new_plugin->environ = malloc(sizeof(char *));
- } while(new_plugin->environ == NULL and errno == EINTR);
+
+ new_plugin->environ = malloc(sizeof(char *));
if(new_plugin->environ == NULL){
- int e = errno;
free(copy_name);
free(new_plugin->argv);
free(new_plugin);
- errno = e;
return NULL;
}
new_plugin->environ[0] = NULL;
-
/* Append the new plugin to the list */
- plugin_list = new_plugin;
+ *plugin_list = new_plugin;
return new_plugin;
}
/* Helper function for add_argument and add_environment */
-__attribute__((nonnull, warn_unused_result))
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));
/* Malloc check */
- if(new_array == NULL){
+ if(*array == NULL){
return false;
}
- *array = new_array;
/* Make a copy of the new string */
- char *copy;
- do {
- copy = strdup(new);
- } while(copy == NULL and errno == EINTR);
+ char *copy = strdup(new);
if(copy == NULL){
return false;
}
@@ -204,7 +168,6 @@
}
/* Add to a plugin's argument vector */
-__attribute__((nonnull(2), warn_unused_result))
static bool add_argument(plugin *p, const char *arg){
if(p == NULL){
return false;
@@ -213,60 +176,37 @@
}
/* Add to a plugin's environment */
-__attribute__((nonnull(2), warn_unused_result))
-static bool add_environment(plugin *p, const char *def, bool replace){
+static bool add_environment(plugin *p, const char *def){
if(p == NULL){
return false;
}
- /* 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){
- /* It already exists */
- if(replace){
- char *new_envdef;
- do {
- new_envdef = realloc(*envdef, strlen(def) + 1);
- } while(new_envdef == NULL and errno == EINTR);
- if(new_envdef == NULL){
- return false;
- }
- *envdef = new_envdef;
- strcpy(*envdef, def);
- }
- return true;
- }
- }
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]] |
+ * *Note File Descriptor Flags:(libc)Descriptor Flags.
*/
-__attribute__((warn_unused_result))
-static int set_cloexec_flag(int fd){
- int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
+static int set_cloexec_flag(int fd)
+{
+ int ret = fcntl(fd, F_GETFD, 0);
/* If reading the flags failed, return error indication now. */
if(ret < 0){
return ret;
}
/* Store modified flag word in the descriptor. */
- return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
- ret | FD_CLOEXEC));
+ return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
}
-#endif /* not O_CLOEXEC */
+process *process_list = NULL;
/* Mark processes as completed when they exit, and save their exit
status. */
-static void handle_sigchld(__attribute__((unused)) int sig){
- int old_errno = errno;
+void handle_sigchld(__attribute__((unused)) int sig){
while(true){
- plugin *proc = plugin_list;
+ process *proc = process_list;
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if(pid == 0){
@@ -274,13 +214,13 @@
break;
}
if(pid == -1){
- if(errno == ECHILD){
- /* No child processes */
- break;
+ if (errno != ECHILD){
+ perror("waitpid");
}
- error(0, errno, "waitpid");
+ /* No child processes */
+ break;
}
-
+
/* A child exited, find it in process_list */
while(proc != NULL and proc->pid != pid){
proc = proc->next;
@@ -290,15 +230,15 @@
continue;
}
proc->status = status;
- proc->completed = 1;
+ proc->completed = true;
}
- errno = old_errno;
}
-/* Prints out a password to stdout */
-__attribute__((nonnull, warn_unused_result))
-static bool print_out_password(const char *buffer, size_t length){
+bool print_out_password(const char *buffer, size_t length){
ssize_t ret;
+ if(length>0 and buffer[length-1] == '\n'){
+ length--;
+ }
for(size_t written = 0; written < length; written += (size_t)ret){
ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
length - written));
@@ -309,53 +249,31 @@
return true;
}
-/* Removes and free a plugin from the plugin list */
-__attribute__((nonnull))
-static void free_plugin(plugin *plugin_node){
-
- for(char **arg = plugin_node->argv; *arg != NULL; arg++){
- free(*arg);
- }
- free(plugin_node->argv);
- for(char **env = plugin_node->environ; *env != NULL; env++){
- free(*env);
- }
- free(plugin_node->environ);
- free(plugin_node->buffer);
-
- /* Removes the plugin from the singly-linked list */
- if(plugin_node == plugin_list){
- /* First one - simple */
- plugin_list = plugin_list->next;
- } else {
- /* Second one or later */
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->next == plugin_node){
- p->next = plugin_node->next;
- break;
- }
- }
- }
-
- free(plugin_node);
-}
-
-static void free_plugin_list(void){
- while(plugin_list != NULL){
- free_plugin(plugin_list);
+static void free_plugin_list(plugin *plugin_list){
+ for(plugin *next; plugin_list != NULL; plugin_list = next){
+ next = plugin_list->next;
+ for(char **arg = plugin_list->argv; *arg != NULL; arg++){
+ free(*arg);
+ }
+ free(plugin_list->argv);
+ for(char **env = plugin_list->environ; *env != NULL; env++){
+ free(*env);
+ }
+ free(plugin_list->environ);
+ free(plugin_list);
}
}
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;
- ssize_t sret;
uid_t uid = 65534;
gid_t gid = 65534;
bool debug = false;
@@ -365,20 +283,19 @@
.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);
ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
if(ret == -1){
- error(0, errno, "sigaddset");
- exitstatus = EX_OSERR;
+ perror("sigaddset");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
if(ret == -1){
- error(0, errno, "sigaction");
- exitstatus = EX_OSERR;
+ perror("sigaction");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
@@ -387,21 +304,18 @@
{ .name = "global-options", .key = 'g',
.arg = "OPTION[,OPTION[,...]]",
.doc = "Options passed to all plugins" },
- { .name = "global-env", .key = 'G',
+ { .name = "global-envs", .key = 'e',
.arg = "VAR=value",
.doc = "Environment variable passed to all plugins" },
{ .name = "options-for", .key = 'o',
.arg = "PLUGIN:OPTION[,OPTION[,...]]",
.doc = "Options passed only to specified plugin" },
- { .name = "env-for", .key = 'E',
+ { .name = "envs-for", .key = 'f',
.arg = "PLUGIN:ENV=value",
.doc = "Environment variable passed to specified plugin" },
{ .name = "disable", .key = 'd',
.arg = "PLUGIN",
.doc = "Disable a specific plugin", .group = 1 },
- { .name = "enable", .key = 'e',
- .arg = "PLUGIN",
- .doc = "Enable a specific plugin", .group = 1 },
{ .name = "plugin-dir", .key = 128,
.arg = "DIRECTORY",
.doc = "Specify a different plugin directory", .group = 2 },
@@ -416,275 +330,169 @@
.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
- */
- { .name = "help", .key = '?',
- .doc = "Give this help list", .group = -1 },
- { .name = "usage", .key = -3,
- .doc = "Give a short usage message", .group = -1 },
- { .name = "version", .key = 'V',
- .doc = "Print program version", .group = -1 },
{ .name = NULL }
};
- __attribute__((nonnull(3)))
- error_t parse_opt(int key, char *arg, struct argp_state *state){
- errno = 0;
- switch(key){
- char *tmp;
- intmax_t tmp_id;
- case 'g': /* --global-options */
- {
- char *plugin_option;
- while((plugin_option = strsep(&arg, ",")) != NULL){
- if(not add_argument(getplugin(NULL), plugin_option)){
- break;
- }
- }
- errno = 0;
- }
- break;
- case 'G': /* --global-env */
- if(add_environment(getplugin(NULL), arg, true)){
- errno = 0;
- }
- break;
- case 'o': /* --options-for */
- {
- char *option_list = strchr(arg, ':');
- if(option_list == NULL){
- argp_error(state, "No colon in \"%s\"", arg);
- errno = EINVAL;
- break;
- }
- *option_list = '\0';
- option_list++;
- if(arg[0] == '\0'){
- argp_error(state, "Empty plugin name");
- errno = EINVAL;
- break;
- }
- char *option;
- while((option = strsep(&option_list, ",")) != NULL){
- if(not add_argument(getplugin(arg), option)){
- break;
- }
- }
- errno = 0;
- }
- break;
- case 'E': /* --env-for */
+ error_t parse_opt (int key, char *arg, struct argp_state *state) {
+ /* Get the INPUT argument from `argp_parse', which we know is a
+ pointer to our plugin list pointer. */
+ plugin **plugins = state->input;
+ switch (key) {
+ case 'g':
+ if (arg != NULL){
+ char *p;
+ while((p = strsep(&arg, ",")) != NULL){
+ if(p[0] == '\0'){
+ continue;
+ }
+ if(not add_argument(getplugin(NULL, plugins), p)){
+ perror("add_argument");
+ return ARGP_ERR_UNKNOWN;
+ }
+ }
+ }
+ break;
+ case 'e':
+ if(arg == NULL){
+ break;
+ }
+ {
+ char *envdef = strdup(arg);
+ if(envdef == NULL){
+ break;
+ }
+ if(not add_environment(getplugin(NULL, plugins), envdef)){
+ perror("add_environment");
+ }
+ }
+ break;
+ case 'o':
+ if (arg != NULL){
+ char *p_name = strsep(&arg, ":");
+ if(p_name[0] == '\0'){
+ break;
+ }
+ char *opt = strsep(&arg, ":");
+ if(opt[0] == '\0'){
+ break;
+ }
+ if(opt != NULL){
+ char *p;
+ while((p = strsep(&opt, ",")) != NULL){
+ if(p[0] == '\0'){
+ continue;
+ }
+ if(not add_argument(getplugin(p_name, plugins), p)){
+ perror("add_argument");
+ return ARGP_ERR_UNKNOWN;
+ }
+ }
+ }
+ }
+ break;
+ case 'f':
+ if(arg == NULL){
+ break;
+ }
{
char *envdef = strchr(arg, ':');
if(envdef == NULL){
- argp_error(state, "No colon in \"%s\"", arg);
- errno = EINVAL;
- break;
- }
- *envdef = '\0';
+ break;
+ }
+ char *p_name = strndup(arg, (size_t) (envdef-arg));
+ if(p_name == NULL){
+ break;
+ }
envdef++;
- if(arg[0] == '\0'){
- argp_error(state, "Empty plugin name");
- errno = EINVAL;
- break;
- }
- if(add_environment(getplugin(arg), envdef, true)){
- errno = 0;
- }
- }
- break;
- case 'd': /* --disable */
- {
- plugin *p = getplugin(arg);
- if(p != NULL){
- p->disabled = true;
- errno = 0;
- }
- }
- break;
- case 'e': /* --enable */
- {
- plugin *p = getplugin(arg);
- if(p != NULL){
- p->disabled = false;
- errno = 0;
- }
- }
- break;
- case 128: /* --plugin-dir */
- free(plugindir);
+ if(not add_environment(getplugin(p_name, plugins), envdef)){
+ perror("add_environment");
+ }
+ }
+ break;
+ case 'd':
+ if (arg != NULL){
+ plugin *p = getplugin(arg, plugins);
+ if(p == NULL){
+ return ARGP_ERR_UNKNOWN;
+ }
+ p->disabled = true;
+ }
+ break;
+ case 128:
plugindir = strdup(arg);
- if(plugindir != NULL){
- errno = 0;
- }
- break;
- case 129: /* --config-file */
- /* This is already done by parse_opt_config_file() */
- break;
- case 130: /* --userid */
- tmp_id = strtoimax(arg, &tmp, 10);
- if(errno != 0 or tmp == arg or *tmp != '\0'
- or tmp_id != (uid_t)tmp_id){
- argp_error(state, "Bad user ID number: \"%s\", using %"
- PRIdMAX, arg, (intmax_t)uid);
- break;
- }
- uid = (uid_t)tmp_id;
- errno = 0;
- break;
- case 131: /* --groupid */
- tmp_id = strtoimax(arg, &tmp, 10);
- if(errno != 0 or tmp == arg or *tmp != '\0'
- or tmp_id != (gid_t)tmp_id){
- argp_error(state, "Bad group ID number: \"%s\", using %"
- PRIdMAX, arg, (intmax_t)gid);
- break;
- }
- gid = (gid_t)tmp_id;
- errno = 0;
- break;
- case 132: /* --debug */
+ if(plugindir == NULL){
+ perror("strdup");
+ }
+ break;
+ case 129:
+ argfile = strdup(arg);
+ if(argfile == NULL){
+ perror("strdup");
+ }
+ break;
+ case 130:
+ uid = (uid_t)strtol(arg, NULL, 10);
+ break;
+ case 131:
+ gid = (gid_t)strtol(arg, NULL, 10);
+ break;
+ case 132:
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
- */
- case '?': /* --help */
- state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
- argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
- case -3: /* --usage */
- state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
- argp_state_help(state, state->out_stream,
- ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
- case 'V': /* --version */
- fprintf(state->out_stream, "%s\n", argp_program_version);
- exit(EXIT_SUCCESS);
- break;
-/*
- * When adding more options before this line, remember to also add a
- * "case" to the "parse_opt_config_file" function below.
- */
- case ARGP_KEY_ARG:
- /* Cryptsetup always passes an argument, which is an empty
- string if "none" was specified in /etc/crypttab. So if
- argument was empty, we ignore it silently. */
- if(arg[0] == '\0'){
- break;
- }
- /* FALLTHROUGH */
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return errno; /* Set to 0 at start */
- }
-
- /* This option parser is the same as parse_opt() above, except it
- ignores everything but the --config-file option. */
- error_t parse_opt_config_file(int key, char *arg,
- __attribute__((unused))
- struct argp_state *state){
- errno = 0;
- switch(key){
- case 'g': /* --global-options */
- case 'G': /* --global-env */
- case 'o': /* --options-for */
- case 'E': /* --env-for */
- case 'd': /* --disable */
- case 'e': /* --enable */
- case 128: /* --plugin-dir */
- break;
- 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 */
- case ARGP_KEY_ARG:
- break;
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return errno;
- }
-
- struct argp argp = { .options = options,
- .parser = parse_opt_config_file,
- .args_doc = "",
+ case ARGP_KEY_ARG:
+ fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ plugin *plugin_list = NULL;
+
+ struct argp argp = { .options = options, .parser = parse_opt,
+ .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
.doc = "Mandos plugin runner -- Run plugins" };
- /* Parse using parse_opt_config_file() in order to get the custom
- config file location, if any. */
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_USAGE;
+ ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
+ if (ret == ARGP_ERR_UNKNOWN){
+ fprintf(stderr, "Unknown error while parsing arguments\n");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
-
- /* Reset to the normal argument parser */
- argp.parser = parse_opt;
-
- /* Open the configfile if available */
- if(argfile == NULL){
+
+ if (argfile == NULL){
conffp = fopen(AFILE, "r");
} else {
conffp = fopen(argfile, "r");
}
+
if(conffp != NULL){
char *org_line = NULL;
char *p, *arg, *new_arg, *line;
size_t size = 0;
+ ssize_t sret;
const char whitespace_delims[] = " \r\t\f\v\n";
const char comment_delim[] = "#";
-
+
custom_argc = 1;
custom_argv = malloc(sizeof(char*) * 2);
if(custom_argv == NULL){
- error(0, errno, "malloc");
- exitstatus = EX_OSERR;
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
custom_argv[0] = argv[0];
custom_argv[1] = NULL;
- /* for each line in the config file, strip whitespace and ignore
- commented text */
while(true){
sret = getline(&org_line, &size, conffp);
if(sret == -1){
break;
}
-
+
line = org_line;
arg = strsep(&line, comment_delim);
while((p = strsep(&arg, whitespace_delims)) != NULL){
@@ -693,631 +501,507 @@
}
new_arg = strdup(p);
if(new_arg == NULL){
- error(0, errno, "strdup");
- exitstatus = EX_OSERR;
+ perror("strdup");
+ exitstatus = EXIT_FAILURE;
free(org_line);
goto fallback;
}
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){
+ perror("realloc");
+ exitstatus = EXIT_FAILURE;
+ free(org_line);
+ goto fallback;
}
custom_argv[custom_argc-1] = new_arg;
- custom_argv[custom_argc] = NULL;
+ custom_argv[custom_argc] = NULL;
}
}
- do {
- ret = fclose(conffp);
- } while(ret == EOF and errno == EINTR);
- if(ret == EOF){
- error(0, errno, "fclose");
- exitstatus = EX_IOERR;
- goto fallback;
- }
free(org_line);
- } else {
+ } else{
/* Check for harmful errors and go to fallback. Other errors might
not affect opening plugins */
- if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
- error(0, errno, "fopen");
- exitstatus = EX_OSERR;
+ if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
+ perror("fopen");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
}
- /* If there were any arguments from the configuration file, pass
- them to parser as command line arguments */
+
if(custom_argv != NULL){
- ret = argp_parse(&argp, custom_argc, custom_argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_CONFIG;
- goto fallback;
- }
- }
-
- /* Parse actual command line arguments, to let them override the
- config file */
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_USAGE;
- 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);
+ ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
+ if (ret == ARGP_ERR_UNKNOWN){
+ fprintf(stderr, "Unknown error while parsing arguments\n");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
}
}
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++){
fprintf(stderr, "\tArg: %s\n", *a);
}
- fprintf(stderr, "...and %d environment variables\n", p->envc);
+ fprintf(stderr, "...and %u environment variables\n", p->envc);
for(char **a = p->environ; *a != NULL; a++){
fprintf(stderr, "\t%s\n", *a);
}
}
}
- 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 "\")");
- }
- } else {
- ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
- if(ret == -1){
- error(0, errno, "fstat");
- } else {
- if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
- ret = fchown(plugindir_fd, uid, gid);
- if(ret == -1){
- error(0, errno, "fchown");
- }
- }
- }
- close(plugindir_fd);
- }
- }
-
- /* Lower permissions */
- ret = setgid(gid);
- if(ret == -1){
- error(0, errno, "setgid");
- }
ret = setuid(uid);
- if(ret == -1){
- error(0, errno, "setuid");
- }
-
- /* Open plugin directory with close_on_exec flag */
+ if (ret == -1){
+ perror("setuid");
+ }
+
+ setgid(gid);
+ if (ret == -1){
+ perror("setgid");
+ }
+
+ if (plugindir == NULL){
+ dir = opendir(PDIR);
+ } else {
+ dir = opendir(plugindir);
+ }
+
+ if(dir == NULL){
+ perror("Could not open plugin dir");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
+
+ /* Set the FD_CLOEXEC flag on the directory, if possible */
{
- dir_fd = open(plugindir != NULL ? plugindir : PDIR, 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;
- goto fallback;
- }
-
-#ifndef O_CLOEXEC
- /* Set the FD_CLOEXEC flag on the directory */
- ret = set_cloexec_flag(dir_fd);
- if(ret < 0){
- error(0, errno, "set_cloexec_flag");
- 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;
+ int dir_fd = dirfd(dir);
+ if(dir_fd >= 0){
+ ret = set_cloexec_flag(dir_fd);
+ if(ret < 0){
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
+ 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]);
- continue;
- }
- ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
- if(ret == -1){
- error(0, errno, "stat");
- close(plugin_fd);
- free(direntries[i]);
- 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)){
+ while(true){
+ dirst = readdir(dir);
+
+ // All directory entries have been processed
+ if(dirst == NULL){
+ if (errno == EBADF){
+ perror("readdir");
+ exitstatus = EXIT_FAILURE;
+ 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-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;
+ ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
+ if(ret < 0){
+ perror("asprintf");
+ continue;
+ }
+
+ ret = stat(filename, &st);
+ if (ret == -1){
+ perror("stat");
+ free(filename);
+ continue;
+ }
+
+ if (not S_ISREG(st.st_mode) or (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, &plugin_list);
if(p == NULL){
- error(0, errno, "getplugin");
- close(plugin_fd);
- free(direntries[i]);
+ perror("getplugin");
+ 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;
}
{
/* Add global arguments to argument list for this plugin */
- plugin *g = getplugin(NULL);
+ plugin *g = getplugin(NULL, &plugin_list);
if(g != NULL){
for(char **a = g->argv + 1; *a != NULL; a++){
if(not add_argument(p, *a)){
- error(0, errno, "add_argument");
+ perror("add_argument");
}
}
/* Add global environment variables */
for(char **e = g->environ; *e != NULL; e++){
- if(not add_environment(p, *e, false)){
- error(0, errno, "add_environment");
+ if(not add_environment(p, *e)){
+ perror("add_environment");
}
}
}
}
- /* 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)){
- error(0, errno, "add_environment");
+ char *copy = strdup(*e);
+ if(copy == NULL){
+ perror("strdup");
+ continue;
+ }
+ if(not add_environment(p, copy)){
+ perror("add_environment");
}
}
}
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
- /* Ask OS to automatic close the pipe on exec */
+ ret = pipe(pipefd);
+ if (ret == -1){
+ perror("pipe");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
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]);
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
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]);
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
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,
- NULL));
+ ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- free(direntries[i]);
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
- /* Starting a new process to be watched */
- pid_t pid;
- do {
- pid = fork();
- } while(pid == -1 and errno == EINTR);
+ // Starting a new process to be watched
+ pid_t pid = fork();
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]);
+ perror("fork");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
if(pid == 0){
/* this is the child process */
ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
if(ret < 0){
- error(0, errno, "sigaction");
- _exit(EX_OSERR);
+ perror("sigaction");
+ _exit(EXIT_FAILURE);
}
- ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- _exit(EX_OSERR);
+ perror("sigprocmask");
+ _exit(EXIT_FAILURE);
}
-
+
ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
if(ret == -1){
- error(0, errno, "dup2");
- _exit(EX_OSERR);
+ perror("dup2");
+ _exit(EXIT_FAILURE);
}
- 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){
+ perror("execv");
+ _exit(EXIT_FAILURE);
+ }
+ } else {
+ if(execve(filename, p->argv, p->environ) < 0){
+ perror("execve");
+ _exit(EXIT_FAILURE);
+ }
}
/* no return */
}
- /* Parent process */
- close(pipefd[1]); /* Close unused write end of pipe */
- close(plugin_fd);
- plugin *new_plugin = getplugin(direntries[i]->d_name);
- if(new_plugin == NULL){
- error(0, errno, "getplugin");
- ret = (int)(TEMP_FAILURE_RETRY
- (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
- NULL)));
+ /* parent process */
+ free(filename);
+ close(pipefd[1]); /* close unused write end of pipe */
+ process *new_process = malloc(sizeof(process));
+ if (new_process == NULL){
+ perror("malloc");
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
+ perror("sigprocmask");
}
- exitstatus = EX_OSERR;
- free(direntries[i]);
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
- free(direntries[i]);
- new_plugin->pid = pid;
- new_plugin->fd = pipefd[0];
-
- if(debug){
- fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
- new_plugin->name, (intmax_t) (new_plugin->pid));
- }
-
+ *new_process = (struct process){ .pid = pid,
+ .fd = pipefd[0],
+ .next = process_list };
+ // List handling
+ process_list = new_process;
/* Unblock SIGCHLD so signal handler can be run if this process
has already completed */
- ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
- &sigchld_action.sa_mask,
- NULL));
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
- FD_SET(new_plugin->fd, &rfds_all);
-
- if(maxfd < new_plugin->fd){
- maxfd = new_plugin->fd;
- }
- }
-
- free(direntries);
- direntries = NULL;
- close(dir_fd);
- dir_fd = -1;
- free_plugin(getplugin(NULL));
-
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->pid != 0){
- break;
- }
- if(p->next == NULL){
- fprintf(stderr, "No plugin processes started. Incorrect plugin"
- " directory?\n");
- free_plugin_list();
- }
- }
-
- /* Main loop while running plugins exist */
- while(plugin_list){
+ FD_SET(new_process->fd, &rfds_all);
+
+ if (maxfd < new_process->fd){
+ maxfd = new_process->fd;
+ }
+
+ }
+
+ free_plugin_list(plugin_list);
+ plugin_list = NULL;
+
+ closedir(dir);
+ dir = NULL;
+
+ if (process_list == NULL){
+ fprintf(stderr, "No plugin processes started. Incorrect plugin"
+ " directory?\n");
+ process_list = NULL;
+ }
+ while(process_list){
fd_set rfds = rfds_all;
int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
- if(select_ret == -1 and errno != EINTR){
- error(0, errno, "select");
- exitstatus = EX_OSERR;
+ if (select_ret == -1){
+ perror("select");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
/* OK, now either a process completed, or something can be read
from one of them */
- for(plugin *proc = plugin_list; proc != NULL;){
+ for(process *proc = process_list; proc ; proc = proc->next){
/* Is this process completely done? */
- if(proc->completed and proc->eof){
+ if(proc->eof and proc->completed){
/* Only accept the plugin output if it exited cleanly */
if(not WIFEXITED(proc->status)
or WEXITSTATUS(proc->status) != 0){
/* Bad exit by plugin */
-
if(debug){
if(WIFEXITED(proc->status)){
- fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
- " status %d\n", proc->name,
- (intmax_t) (proc->pid),
+ fprintf(stderr, "Plugin %u exited with status %d\n",
+ (unsigned int) (proc->pid),
WEXITSTATUS(proc->status));
- } else if(WIFSIGNALED(proc->status)){
- fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
- " signal %d: %s\n", proc->name,
- (intmax_t) (proc->pid),
- WTERMSIG(proc->status),
- strsignal(WTERMSIG(proc->status)));
+ } else if(WIFSIGNALED(proc->status)) {
+ fprintf(stderr, "Plugin %u killed by signal %d\n",
+ (unsigned int) (proc->pid),
+ WTERMSIG(proc->status));
+ } else if(WCOREDUMP(proc->status)){
+ fprintf(stderr, "Plugin %d dumped core\n",
+ (unsigned int) (proc->pid));
}
}
-
/* Remove the plugin */
FD_CLR(proc->fd, &rfds_all);
-
/* Block signal while modifying process_list */
- ret = (int)TEMP_FAILURE_RETRY(sigprocmask
- (SIG_BLOCK,
- &sigchld_action.sa_mask,
- NULL));
+ ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
-
- plugin *next_plugin = proc->next;
- free_plugin(proc);
- proc = next_plugin;
-
+ /* Delete this process entry from the list */
+ if(process_list == proc){
+ /* First one - simple */
+ process_list = proc->next;
+ } else {
+ /* Second one or later */
+ for(process *p = process_list; p != NULL; p = p->next){
+ if(p->next == proc){
+ p->next = proc->next;
+ break;
+ }
+ }
+ }
/* We are done modifying process list, so unblock signal */
- ret = (int)(TEMP_FAILURE_RETRY
- (sigprocmask(SIG_UNBLOCK,
- &sigchld_action.sa_mask, NULL)));
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
+ NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- goto fallback;
- }
-
- if(plugin_list == NULL){
- break;
- }
-
- continue;
+ perror("sigprocmask");
+ }
+ free(proc->buffer);
+ free(proc);
+ /* We deleted this process from the list, so we can't go
+ proc->next. Therefore, start over from the beginning of
+ the process list */
+ break;
}
-
/* This process exited nicely, so print its buffer */
-
+
bool bret = print_out_password(proc->buffer,
proc->buffer_length);
if(not bret){
- error(0, errno, "print_out_password");
- exitstatus = EX_IOERR;
+ perror("print_out_password");
+ exitstatus = EXIT_FAILURE;
}
goto fallback;
}
-
/* This process has not completed. Does it have any output? */
if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
/* 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){
- error(0, errno, "malloc");
- exitstatus = EX_OSERR;
+ proc->buffer = realloc(proc->buffer, proc->buffer_size
+ + (size_t) BUFFER_SIZE);
+ if (proc->buffer == NULL){
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
goto fallback;
}
- proc->buffer = new_buffer;
proc->buffer_size += BUFFER_SIZE;
}
/* Read from the process */
- sret = TEMP_FAILURE_RETRY(read(proc->fd,
- proc->buffer
- + proc->buffer_length,
- BUFFER_SIZE));
- if(sret < 0){
+ ret = read(proc->fd, proc->buffer + proc->buffer_length,
+ BUFFER_SIZE);
+ if(ret < 0){
/* Read error from this process; ignore the error */
- proc = proc->next;
continue;
}
- if(sret == 0){
+ if(ret == 0){
/* got EOF */
proc->eof = true;
} else {
- proc->buffer_length += (size_t) sret;
+ proc->buffer_length += (size_t) ret;
}
}
}
-
-
+
+
fallback:
- if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
- and exitstatus != EX_OK)){
+ if(process_list == NULL or exitstatus != EXIT_SUCCESS){
/* Fallback if all plugins failed, none are found or an error
occured */
bool bret;
fprintf(stderr, "Going to fallback mode using getpass(3)\n");
char *passwordbuffer = getpass("Password: ");
- size_t len = strlen(passwordbuffer);
- /* Strip trailing newline */
- if(len > 0 and passwordbuffer[len-1] == '\n'){
- passwordbuffer[len-1] = '\0'; /* not strictly necessary */
- len--;
- }
- bret = print_out_password(passwordbuffer, len);
+ bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
if(not bret){
- error(0, errno, "print_out_password");
- exitstatus = EX_IOERR;
+ perror("print_out_password");
+ exitstatus = EXIT_FAILURE;
}
}
/* Restore old signal handler */
ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
if(ret == -1){
- error(0, errno, "sigaction");
- exitstatus = EX_OSERR;
+ perror("sigaction");
+ exitstatus = EXIT_FAILURE;
}
-
+
if(custom_argv != NULL){
for(char **arg = custom_argv+1; *arg != NULL; arg++){
free(*arg);
}
free(custom_argv);
}
-
- free(direntries);
-
- if(dir_fd != -1){
- close(dir_fd);
+ free_plugin_list(plugin_list);
+
+ if(dir != NULL){
+ closedir(dir);
}
- /* Kill the processes */
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->pid != 0){
- close(p->fd);
- ret = kill(p->pid, SIGTERM);
- if(ret == -1 and errno != ESRCH){
- /* Set-uid proccesses might not get closed */
- error(0, errno, "kill");
- }
+ /* Free the process list and kill the processes */
+ for(process *next; process_list != NULL; process_list = next){
+ next = process_list->next;
+ close(process_list->fd);
+ ret = kill(process_list->pid, SIGTERM);
+ if(ret == -1 and errno != ESRCH){
+ /* set-uid proccesses migth not get closed */
+ perror("kill");
}
+ free(process_list->buffer);
+ free(process_list);
}
/* Wait for any remaining child processes to terminate */
- do {
+ do{
ret = wait(NULL);
} while(ret >= 0);
if(errno != ECHILD){
- error(0, errno, "wait");
+ perror("wait");
}
-
- free_plugin_list();
-
+
free(plugindir);
- free(pluginhelperdir);
free(argfile);
return exitstatus;
=== removed file 'plugin-runner.conf'
--- plugin-runner.conf 2009-04-17 08:26:17 +0000
+++ plugin-runner.conf 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-## This is the configuration file for plugin-runner(8mandos). This
-## file should be installed as "/etc/mandos/plugin-runner.conf", and
-## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the
-## initrd.img file.
-##
-## After editing this file, the initrd image file must be updated for
-## the changes to take effect!
-
-## Example:
-#--options-for=mandos-client:--debug
=== modified file 'plugin-runner.xml'
--- plugin-runner.xml 2019-02-09 23:23:26 +0000
+++ plugin-runner.xml 2008-08-16 20:31:21 +0000
@@ -1,53 +1,63 @@
-
+
+
-
-
-%common;
]>
-
+
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
+ &COMMANDNAME;
+
+ &COMMANDNAME;
+ &VERSION;
Björn
Påhlsson
- belorn@recompile.se
+ belorn@fukt.bsnet.se
Teddy
Hogeborn
- teddy@recompile.se
+ teddy@fukt.bsnet.se
2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
- Teddy Hogeborn
- Björn Påhlsson
+ Teddy Hogeborn & Björn Påhlsson
-
+
+
+ This manual page 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 manual page 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
+ .
+
+
-
+
&COMMANDNAME;
8mandos
@@ -56,642 +66,209 @@
&COMMANDNAME;
- Run Mandos plugins, pass data from first to succeed.
+ get password for encrypted rootdisk
-
+
&COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
- &COMMANDNAME;
-
-
-
-
-
+ --global-optionsOPTIONS
+ --options-forPLUGIN:OPTIONS
+ --disablePLUGIN
+ --groupidID
+ --useridID
+ --plugin-dirDIRECTORY
+ --debug
+
+
+ &COMMANDNAME;
+ --help
+
+
+ &COMMANDNAME;
+ --usage
+
+
+ &COMMANDNAME;
+ --version
+
-
+
DESCRIPTION
- &COMMANDNAME; is a program which is meant to
- be specified as a keyscript
for the root disk in
+ &COMMANDNAME; is a plugin runner that waits
+ for any of its plugins to return sucessfull with a password, and
+ passes it to cryptsetup as stdout message. This command is not
+ meant to be invoked directly, but is instead meant to be run by
+ cryptsetup by being specified in /etc/crypttab as a keyscript
+ and subsequlently started in the initrd environment. See
crypttab
- 5. The aim of this
- program is therefore to output a password, which then
- cryptsetup
- 8 will use to unlock the
- root disk.
-
-
- This program is not meant to be invoked directly, but can be in
- order to test it. Note that any password obtained will simply
- be output on standard output.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
+ 5 for more information on
+ keyscripts.
+
+
+
+ plugins is looked for in the plugins directory which by default will be
+ /conf/conf.d/mandos/plugins.d if not changed by option --plugin-dir.
+
+
OPTIONS
-
-
-
-
- This option will add an environment variable setting to
- all plugins. This will override any inherited environment
- variable.
-
-
-
-
-
-
-
-
-
- This option will add an environment variable setting to
- the PLUGIN plugin. This will
- override any inherited environment variables or
- environment variables specified using
- .
-
-
-
-
-
-
-
-
-
- Pass some options to all plugins.
- OPTIONS is a comma separated
- list of options. This is not a very useful option, except
- for specifying the
- option to all plugins.
-
-
-
-
-
-
-
-
-
- Pass some options to a specific plugin. PLUGIN is the name (file basename) of a
- plugin, and OPTIONS is a comma
- separated list of options.
-
-
- Note that since options are not split on whitespace, the
- way to pass, to the plugin
- foo
, the option
- with the option argument
- baz
is either
- --options-for=foo:--bar=baz or
- --options-for=foo:--bar,baz. Using
- --options-for="foo:--bar baz". will
- not work.
-
-
-
-
-
-
-
-
-
- Disable the plugin named
- PLUGIN. The plugin will not be
- started.
-
-
-
-
-
-
-
-
-
- Re-enable the plugin named
- PLUGIN. This is only useful to
- undo a previous option, maybe
- from the configuration file.
-
-
-
-
-
-
-
-
- Change to group ID ID on
- startup. The default is 65534. All plugins will be
- started using this group ID. Note:
- This must be a number, not a name.
-
-
-
-
-
-
-
-
- Change to user ID ID on
- startup. The default is 65534. All plugins will be
- started using this user ID. Note:
- This must be a number, not a name.
-
-
-
-
-
-
-
-
- Specify a different plugin directory. The default is
- /lib/mandos/plugins.d, which will
- exist in the initial RAM disk
- environment.
-
-
-
-
-
-
-
-
- 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 )
-
-
-
-
-
-
-
-
- Specify a different file to read additional options from.
- See . Other command line options
- will override options specified in the file.
-
-
-
-
-
-
-
-
- Enable debug mode. This will enable a lot of output to
- standard error about what the program is doing. The
- program will still perform all other functions normally.
- The default is to not run in debug
- mode.
-
-
- The plugins will not be affected by
- this option. Use
-
- if complete debugging eruption is desired.
-
-
-
-
-
-
-
-
-
- Gives a help message about options and their meanings.
-
-
-
-
-
-
-
-
- Gives a short usage message.
-
-
-
-
-
-
-
-
-
- Prints the program version.
-
-
-
+ -g,--global-options
+ OPTIONS
+
+
+ Global options given to all plugins as additional start
+ arguments. Options are specified with a -o flag followed
+ by a comma separated string of options.
+
+
+
+
+
+ -o, --options-for
+ PLUGIN:OPTION
+
+
+
+ Plugin specific options given to the plugin as additional
+ start arguments. Options are specified with a -o flag
+ followed by a comma separated string of options.
+
+
+
+
+
+ -d, --disable
+ PLUGIN
+
+
+
+ Disable a specific plugin
+
+
+
+
+
+ --groupid ID
+
+
+
+ Group ID the plugins will run as
+
+
+
+
+
+ --userid ID
+
+
+
+ User ID the plugins will run as
+
+
+
+
+
+ --plugin-dir DIRECTORY
+
+
+
+ Specify a different plugin directory
+
+
+
+
+
+ --debug
+
+
+ Debug mode
+
+
+
+
+
+ -?, --help
+
+
+ Gives a help message
+
+
+
+
+
+ --usage
+
+
+ Gives a short usage message
+
+
+
+
+
+ -V, --version
+
+
+ Prints the program version
+
+
+
-
-
- OVERVIEW
-
-
- This program will run on the client side in the initial
- RAM disk environment, and is responsible for
- getting a password. It does this by running plugins, one of
- which will normally be the actual client program communicating
- with the server.
-
-
-
- PLUGINS
-
- This program will get a password by running a number of
- plugins, which are simply executable
- programs in a directory in the initial RAM
- disk environment. The default directory is
- /lib/mandos/plugins.d, but this can be
- changed with the option. The
- plugins are started in parallel, and the first plugin to output
- a password and exit with a successful exit
- code will make this plugin-runner output the password from that
- plugin, stop any other plugins, and exit.
-
-
-
- WRITING PLUGINS
-
- A plugin is simply a program which prints a password to its
- standard output and then exits with a successful (zero) exit
- status. If the exit status is not zero, any output on
- standard output will be ignored by the plugin runner. Any
- output on its standard error channel will simply be passed to
- the standard error of the plugin runner, usually the system
- console.
-
-
- If the password is a single-line, manually entered passprase,
- a final trailing newline character should
- not be printed.
-
-
- 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.
-
-
- The plugin must exit cleanly and free all allocated resources
- upon getting the TERM signal, since this is what the plugin
- runner uses to stop all other plugins when one plugin has
- output a password and exited cleanly.
-
-
- The plugin must not use resources, like for instance reading
- from the standard input, without knowing that no other plugin
- is also using it.
-
-
- It is useful, but not required, for the plugin to take the
- option.
-
-
-
-
-
- FALLBACK
-
- If no plugins succeed, this program will, as a fallback, ask for
- a password on the console using getpass3,
- and output it. This is not meant to be the normal mode of
- operation, as there is a separate plugin for getting a password
- from the console.
-
-
-
+
EXIT STATUS
- Exit status of this program is zero if no errors were
- encountered, and otherwise not. The fallback (see ) may or may not have succeeded in either
- case.
-
-
-
-
- ENVIRONMENT
-
- This program does not use any environment variables itself, it
- 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.
-
-
-
-
+
+
+
+
FILES
-
-
- /conf/conf.d/mandos/plugin-runner.conf
-
-
- Since this program will be run as a keyscript, there is
- little to no opportunity to pass command line arguments
- to it. Therefore, it will also
- read this file and use its contents as
- whitespace-separated command line options. Also,
- everything from a #
character to the end
- of a line is ignored.
-
-
- This program is meant to run in the initial RAM disk
- environment, so that is where this file is assumed to
- exist. The file does not need to exist in the normal
- file system.
-
-
- This file will be processed before
- the normal command line options, so the latter can
- override the former, if need be.
-
-
- This file name is the default; the file to read for
- arguments can be changed using the
- option.
-
-
-
-
- /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.
-
-
-
-
+
+
+
+
+ NOTES
+
BUGS
- The option is ignored when
- specified from within a configuration file.
-
-
-
+
+
- EXAMPLE
-
-
- Normal invocation needs no options:
-
-
- &COMMANDNAME;
-
-
-
-
- Run the program, but not the plugins, in debug mode:
-
-
-
-
- &COMMANDNAME; --debug
-
-
-
-
-
- Run all plugins, but run the foo
plugin in
- debug mode:
-
-
-
-
- &COMMANDNAME; --options-for=foo:--debug
-
-
-
-
-
- Run all plugins, but not the program, in debug mode:
-
-
-
-
- &COMMANDNAME; --global-options=--debug
-
-
-
-
-
- Read a different configuration file, run plugins from a
- different directory, specify an alternate plugin helper
- directory 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,--tls-pubkey=tls-pubkey.pem,--tls-privkey=tls-privkey.pem
-
-
-
+ EXAMPLES
+
+
+
SECURITY
- This program will, when starting, try to switch to another user.
- If it is started as root, it will succeed, and will by default
- switch to user and group 65534, which are assumed to be
- non-privileged. This user and group is then what all plugins
- will be started as. Therefore, the only way to run a plugin as
- a privileged user is to have the set-user-ID or set-group-ID bit
- set on the plugin executable file (see
- execve2
- ).
-
-
- If this program is used as a keyscript in crypttab5
- , there is a slight risk that if this program
- fails to work, there might be no way to boot the system except
- for booting from another media and editing the initial RAM disk
- image to not run this program. This is, however, unlikely,
- since the password-prompt8mandos
- plugin will read a password from the console in
- case of failure of the other plugins, and this plugin runner
- will also, in case of catastrophic failure, itself fall back to
- asking and outputting a password on the console (see ).
-
+
SEE ALSO
- intro
- 8mandos,
- cryptsetup
- 8,
- crypttab
- 5,
- execve
- 2,
mandos
- 8,
- password-prompt
- 8mandos,
- mandos-client
- 8mandos
+ 8,
+ password-request
+ 8mandos,
+ password-prompt
+ 8mandos, and
+ cryptsetup
+ 8
-
+
-
-
-
-
-
=== removed file 'plugins.d/askpass-fifo.c'
--- plugins.d/askpass-fifo.c 2018-02-08 10:23:55 +0000
+++ plugins.d/askpass-fifo.c 1970-01-01 00:00:00 +0000
@@ -1,218 +0,0 @@
-/* -*- coding: utf-8 -*- */
-/*
- * Askpass-FIFO - Read a password from a FIFO and output it
- *
- * Copyright © 2008-2018 Teddy Hogeborn
- * Copyright © 2008-2018 Björn Påhlsson
- *
- * This file is part of Mandos.
- *
- * Mandos 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.
- *
- * Mandos 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 Mandos. If not, see .
- *
- * Contact the authors at .
- */
-
-#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
-#include /* uid_t, gid_t, ssize_t */
-#include /* mkfifo(), S_IRUSR, S_IWUSR */
-#include /* and */
-#include /* errno, EACCES, ENOTDIR, ELOOP,
- ENAMETOOLONG, ENOSPC, EROFS,
- ENOENT, EEXIST, EFAULT, EMFILE,
- ENFILE, ENOMEM, EBADF, EINVAL, EIO,
- EISDIR, EFBIG */
-#include /* error() */
-#include /* fprintf(), vfprintf(),
- vasprintf() */
-#include /* EXIT_FAILURE, NULL, size_t, free(),
- realloc(), EXIT_SUCCESS */
-#include /* open(), O_RDONLY */
-#include /* read(), close(), write(),
- STDOUT_FILENO */
-#include /* EX_OSERR, EX_OSFILE,
- EX_UNAVAILABLE, EX_IOERR */
-#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)))
-void error_plus(int status, int errnum, const char *formatstring,
- ...){
- va_list ap;
- char *text;
- int ret;
-
- va_start(ap, formatstring);
- ret = vasprintf(&text, formatstring, ap);
- if(ret == -1){
- fprintf(stderr, "Mandos plugin %s: ",
- program_invocation_short_name);
- vfprintf(stderr, formatstring, ap);
- fprintf(stderr, ": ");
- fprintf(stderr, "%s\n", strerror(errnum));
- error(status, errno, "vasprintf while printing error");
- return;
- }
- fprintf(stderr, "Mandos plugin ");
- error(status, errnum, "%s", text);
- free(text);
-}
-
-int main(__attribute__((unused))int argc,
- __attribute__((unused))char **argv){
- 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);
- if(ret == -1){
- int e = errno;
- switch(e){
- case EACCES:
- case ENOTDIR:
- case ELOOP:
- error_plus(EX_OSFILE, errno, "mkfifo");
- case ENAMETOOLONG:
- case ENOSPC:
- case EROFS:
- default:
- error_plus(EX_OSERR, errno, "mkfifo");
- case ENOENT:
- /* no "/lib/cryptsetup"? */
- error_plus(EX_UNAVAILABLE, errno, "mkfifo");
- case EEXIST:
- break; /* not an error */
- }
- }
-
- /* Open FIFO */
- int fifo_fd = open(passfifo, O_RDONLY);
- if(fifo_fd == -1){
- int e = errno;
- error_plus(0, errno, "open");
- switch(e){
- case EACCES:
- case ENOENT:
- case EFAULT:
- return EX_UNAVAILABLE;
- case ENAMETOOLONG:
- case EMFILE:
- case ENFILE:
- case ENOMEM:
- default:
- return EX_OSERR;
- case ENOTDIR:
- case ELOOP:
- return EX_OSFILE;
- }
- }
-
- /* 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;
- {
- size_t buf_allocated = 0;
- const size_t blocksize = 1024;
- do {
- if(buf_len + blocksize > buf_allocated){
- char *tmp = realloc(buf, buf_allocated + blocksize);
- if(tmp == NULL){
- error_plus(0, errno, "realloc");
- free(buf);
- return EX_OSERR;
- }
- buf = tmp;
- buf_allocated += blocksize;
- }
- sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
- if(sret == -1){
- int e = errno;
- free(buf);
- errno = e;
- error_plus(0, errno, "read");
- switch(e){
- case EBADF:
- case EFAULT:
- case EINVAL:
- default:
- return EX_OSERR;
- case EIO:
- return EX_IOERR;
- case EISDIR:
- return EX_UNAVAILABLE;
- }
- }
- buf_len += (size_t)sret;
- } while(sret != 0);
- }
-
- /* Close FIFO */
- close(fifo_fd);
-
- /* Print password to stdout */
- size_t written = 0;
- while(written < buf_len){
- sret = write(STDOUT_FILENO, buf + written, buf_len - written);
- if(sret == -1){
- int e = errno;
- free(buf);
- errno = e;
- error_plus(0, errno, "write");
- switch(e){
- case EBADF:
- case EFAULT:
- case EINVAL:
- return EX_OSFILE;
- case EFBIG:
- case EIO:
- case ENOSPC:
- default:
- return EX_IOERR;
- }
- }
- written += (size_t)sret;
- }
- free(buf);
-
- ret = close(STDOUT_FILENO);
- if(ret == -1){
- int e = errno;
- error_plus(0, errno, "close");
- switch(e){
- case EBADF:
- return EX_OSFILE;
- case EIO:
- default:
- return EX_IOERR;
- }
- }
- return EXIT_SUCCESS;
-}
=== removed file 'plugins.d/askpass-fifo.xml'
--- plugins.d/askpass-fifo.xml 2018-02-08 10:23:55 +0000
+++ plugins.d/askpass-fifo.xml 1970-01-01 00:00:00 +0000
@@ -1,177 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@recompile.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@recompile.se
-
-
-
-
- 2008
- 2009
- 2010
- 2011
- 2012
- 2013
- 2014
- 2015
- 2016
- 2017
- 2018
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
- Mandos plugin to get a password from a
- FIFO.
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- DESCRIPTION
-
- This program reads a password from a FIFO and
- outputs it to standard output.
-
-
- This program is not very useful on its own. This program is
- really meant to run as a plugin in the Mandos client-side system, where it is used as a
- fallback and alternative to retrieving passwords from a
- Mandos server.
-
-
- This program is meant to be imitate a feature of the
- askpass program, so that programs written to
- interface with it can keep working under the
- Mandos system.
-
-
-
-
- OPTIONS
-
- This program takes no options.
-
-
-
-
- EXIT STATUS
-
- If exit status is 0, the output from the program is the password
- as it was read. Otherwise, if exit status is other than 0, the
- program was interrupted or encountered an error, and any output
- so far could be corrupt and/or truncated, and should therefore
- be ignored.
-
-
-
-
- FILES
-
-
- /lib/cryptsetup/passfifo
-
-
- This is the FIFO where this program
- will read the password. If it does not exist, it will be
- created.
-
-
-
-
-
-
-
- BUGS
-
-
-
-
- EXAMPLE
-
- Note that normally, this program will not be invoked directly,
- but instead started by the Mandos plugin-runner8mandos
- .
-
-
-
- This program takes no options.
-
-
- &COMMANDNAME;
-
-
-
-
-
- SECURITY
-
- The only thing that could be considered worthy of note is
- this: This program is meant to be run by
- plugin-runner8mandos, and will, when run
- standalone, outside, in a normal environment, immediately output
- on its standard output any presumably secret password it just
- received. Therefore, when running this program standalone
- (which should never normally be done), take care not to type in
- any real secret password by force of habit, since it would then
- immediately be shown as output.
-
-
-
-
- SEE ALSO
-
- intro
- 8mandos,
- fifo
- 7,
- plugin-runner
- 8mandos
-
-
-
-
-
-
-
-
=== modified file 'plugins.d/password-prompt.c'
--- plugins.d/password-prompt.c 2018-02-08 10:23:55 +0000
+++ plugins.d/password-prompt.c 2008-08-16 16:58:31 +0000
@@ -1,235 +1,66 @@
-/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/* -*- coding: utf-8 -*- */
/*
- * Password-prompt - Read a password from the terminal and print it
- *
- * Copyright © 2008-2018 Teddy Hogeborn
- * Copyright © 2008-2018 Björn Påhlsson
- *
- * This file is part of Mandos.
- *
- * Mandos 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.
- *
- * Mandos is distributed in the hope that it will be useful, but
+ * Passprompt - Read a password from the terminal and print it
+ *
+ * Copyright © 2007-2008 Teddy Hogeborn & 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 Mandos. If not, see .
+ * along with this program. If not, see
+ * .
*
- * Contact the authors at .
+ * Contact the authors at and
+ * .
*/
-#define _GNU_SOURCE /* getline(), asprintf() */
+#define _GNU_SOURCE /* getline() */
-#include /* struct termios, tcsetattr(),
+#include /* struct termios, tcsetattr(),
TCSAFLUSH, tcgetattr(), ECHO */
#include /* struct termios, tcsetattr(),
STDIN_FILENO, TCSAFLUSH,
- tcgetattr(), ECHO, readlink() */
+ tcgetattr(), ECHO */
#include /* sig_atomic_t, raise(), struct
sigaction, sigemptyset(),
sigaction(), sigaddset(), SIGINT,
- SIGQUIT, SIGHUP, SIGTERM,
- raise() */
+ SIGQUIT, SIGHUP, SIGTERM */
#include /* NULL, size_t, ssize_t */
-#include /* ssize_t, struct dirent, pid_t,
- ssize_t, open() */
+#include /* ssize_t */
#include /* EXIT_SUCCESS, EXIT_FAILURE,
- getenv(), free() */
-#include /* scandir(), alphasort() */
+ getopt_long, getenv() */
#include /* fprintf(), stderr, getline(),
- stdin, feof(), fputc(), vfprintf(),
- vasprintf() */
-#include /* errno, EBADF, ENOTTY, EINVAL,
- EFAULT, EFBIG, EIO, ENOSPC, EINTR
- */
-#include /* error() */
+ stdin, feof(), perror(), fputc(),
+ stdout, getopt_long */
+#include /* errno, EINVAL */
#include /* or, not */
#include /* bool, false, true */
-#include /* strtoumax() */
-#include /* struct stat, lstat(), open() */
-#include /* strlen, rindex, memcmp, strerror()
- */
+#include /* strlen, rindex, strncmp, strcmp */
#include /* struct argp_option, struct
argp_state, struct argp,
argp_parse(), error_t,
ARGP_KEY_ARG, ARGP_KEY_END,
ARGP_ERR_UNKNOWN */
-#include /* EX_SOFTWARE, EX_OSERR,
- EX_UNAVAILABLE, EX_IOERR, EX_OK */
-#include /* open() */
-#include /* va_list, va_start(), ... */
-volatile sig_atomic_t quit_now = 0;
-int signal_received;
+volatile bool quit_now = false;
bool debug = false;
-const char *argp_program_version = "password-prompt " VERSION;
-const char *argp_program_bug_address = "";
-
-/* Needed for conflict resolution */
-const char plymouth_name[] = "plymouthd";
-
-/* Function to use when printing errors */
-__attribute__((format (gnu_printf, 3, 4)))
-void error_plus(int status, int errnum, const char *formatstring,
- ...){
- va_list ap;
- char *text;
- int ret;
-
- va_start(ap, formatstring);
- ret = vasprintf(&text, formatstring, ap);
- if(ret == -1){
- fprintf(stderr, "Mandos plugin %s: ",
- program_invocation_short_name);
- vfprintf(stderr, formatstring, ap);
- fprintf(stderr, ": %s\n", strerror(errnum));
- error(status, errno, "vasprintf while printing error");
- return;
- }
- fprintf(stderr, "Mandos plugin ");
- error(status, errnum, "%s", text);
- free(text);
-}
-
-static void termination_handler(int signum){
- if(quit_now){
- return;
- }
- quit_now = 1;
- signal_received = signum;
-}
-
-bool conflict_detection(void){
-
- /* plymouth conflicts with password-prompt since both want to read
- from the terminal. Password-prompt will exit if it detects
- plymouth since plymouth performs the same functionality.
- */
- __attribute__((nonnull))
- int is_plymouth(const struct dirent *proc_entry){
- int ret;
- int cl_fd;
- {
- uintmax_t proc_id;
- char *tmp;
- errno = 0;
- proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
-
- if(errno != 0 or *tmp != '\0'
- or proc_id != (uintmax_t)((pid_t)proc_id)){
- return 0;
- }
- }
-
- char *cmdline_filename;
- ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
- proc_entry->d_name);
- if(ret == -1){
- error_plus(0, errno, "asprintf");
- return 0;
- }
-
- /* Open /proc//cmdline */
- cl_fd = open(cmdline_filename, O_RDONLY);
- free(cmdline_filename);
- if(cl_fd == -1){
- if(errno != ENOENT){
- error_plus(0, errno, "open");
- }
- return 0;
- }
-
- char *cmdline = NULL;
- {
- size_t cmdline_len = 0;
- size_t cmdline_allocated = 0;
- char *tmp;
- const size_t blocksize = 1024;
- ssize_t sret;
- do {
- /* Allocate more space? */
- if(cmdline_len + blocksize + 1 > cmdline_allocated){
- tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
- if(tmp == NULL){
- error_plus(0, errno, "realloc");
- free(cmdline);
- close(cl_fd);
- return 0;
- }
- cmdline = tmp;
- cmdline_allocated += blocksize;
- }
-
- /* Read data */
- sret = read(cl_fd, cmdline + cmdline_len,
- cmdline_allocated - cmdline_len);
- if(sret == -1){
- error_plus(0, errno, "read");
- free(cmdline);
- close(cl_fd);
- return 0;
- }
- cmdline_len += (size_t)sret;
- } while(sret != 0);
- ret = close(cl_fd);
- if(ret == -1){
- error_plus(0, errno, "close");
- free(cmdline);
- return 0;
- }
- cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
- }
- /* we now have cmdline */
-
- /* get basename */
- char *cmdline_base = strrchr(cmdline, '/');
- if(cmdline_base != NULL){
- cmdline_base += 1; /* skip the slash */
- } else {
- cmdline_base = cmdline;
- }
-
- if(strcmp(cmdline_base, plymouth_name) != 0){
- if(debug){
- fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
- plymouth_name);
- }
- free(cmdline);
- return 0;
- }
- if(debug){
- fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
- plymouth_name);
- }
- free(cmdline);
- return 1;
- }
-
- struct dirent **direntries = NULL;
- int ret;
- ret = scandir("/proc", &direntries, is_plymouth, alphasort);
- if(ret == -1){
- error_plus(1, errno, "scandir");
- }
- {
- int i = ret;
- while(i--){
- free(direntries[i]);
- }
- }
- free(direntries);
- return ret > 0;
-}
-
+const char *argp_program_version = "password-prompt 1.0";
+const char *argp_program_bug_address = "