=== modified file '.bzrignore' --- .bzrignore 2010-09-30 06:24:20 +0000 +++ .bzrignore 2008-10-03 09:32:30 +0000 @@ -2,6 +2,8 @@ *.8 *.8mandos confdir +debian/po/messages.mo +debian/po/templates.pot keydir man plugin-runner @@ -10,4 +12,3 @@ plugins.d/password-prompt plugins.d/splashy plugins.d/usplash -plugins.d/plymouth === removed file 'DBUS-API' --- DBUS-API 2010-09-26 18:32:58 +0000 +++ DBUS-API 1970-01-01 00:00:00 +0000 @@ -1,172 +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.bsnet.fukt.Mandos" - - -* Object Paths: - - | Path | Object | - |-----------------------+-------------------| - | "/" | The Mandos Server | - | "/clients/CLIENTNAME" | Mandos Client | - - -* Mandos Server Interface: - Interface name: "se.bsnet.fukt.Mandos" - -** Methods: -*** GetAllClients() → (ao: Clients) - Returns an array of all client D-Bus object paths - -*** GetAllClientsWithProperties() → (a{oa{sv}}: ClientProperties) - Returns an array of all clients and all their properties - -*** RemoveClient(o: ObjectPath) → nothing - Removes a client - -** Signals: -*** ClientAdded(o: ObjectPath) - A new client was added. - -*** ClientNotFound(s: Fingerprint, s: Address) - A client connected from Address using Fingerprint, but was - rejected because it was not found in the server. The fingerprint - is represented as a string of hexadecimal digits. The address is - an IPv4 or IPv6 address in its normal string format. - -*** ClientRemoved(o: ObjectPath, s: Name) - A client named Name on ObjectPath was removed. - - -* Mandos Client Interface: - Interface name: "se.bsnet.fukt.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. - -*** Disable() → nothing - Disable this client. See also the "Enabled" property. - -*** Enable() → nothing - Enable this client. See also the "Enabled" property. - -*** StartChecker() → nothing - Start a new checker for this client, if none is currently - running. See also the "CheckerRunning" property. - -*** StopChecker() → nothing - Abort a running checker process for this client, if any. See also - the "CheckerRunning" property. - -** Properties - - 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 | - | Fingerprint | s | Read | fingerprint | - | Host | s | Read/Write | host | - | Interval (a) | t | Read/Write | interval | - | LastApprovalRequest (f) | s | Read | N/A | - | LastCheckedOK (g) | s | Read/Write | N/A | - | LastEnabled (h) | s | Read | N/A | - | Name | s | Read | (Section name) | - | ObjectPath | o | Read | N/A | - | Secret (i) | ay | Write | secret (or secfile) | - | Timeout (a) | t | Read/Write | timeout | - - a) Represented as milliseconds. - - b) An approval is currently pending. - - c) Setting this property is equivalent to calling StartChecker() or - StopChecker(). - - d) The creation time of this client object, as a RFC 3339 string. - - e) Setting this property is equivalent to calling Enable() or - Disable(). - - f) The time of the last approval request, as a RFC 3339 string, or - an empty string if this has not happened. - - g) The last time a checker was successful, as a 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. - - h) The last time this client was enabled, as a RFC 3339 string, or - an empty string if this has not happened. - - i) 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. - -*** PropertyChanged(s: Property, v: Value) - The Property on this client has changed to Value. - -*** Rejected(s: Reason) - This client was not given its secret for a specified Reason. - -* Copyright - - Copyright © 2010 Teddy Hogeborn - Copyright © 2010 Björn Påhlsson - -** License: - - This program is free software: you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - - -#+STARTUP: showall === modified file 'INSTALL' --- INSTALL 2009-02-15 09:28:06 +0000 +++ INSTALL 2008-10-28 18:00:20 +0000 @@ -39,18 +39,17 @@ *** Mandos Server + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ + Avahi 0.6.16 http://www.avahi.org/ - + Python 2.5 http://www.python.org/ + + Python 2.4 http://www.python.org/ + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/ + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + python-ctypes 1.0.0 http://pypi.python.org/pypi/ctypes - + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/ Strongly recommended: + fping 2.4b2-to-ipv6 http://www.fping.com/ Package names: python-gnutls avahi-daemon python python-avahi python-dbus - python-ctypes python-gobject + python-ctypes *** Mandos Client + initramfs-tools 0.85i === modified file 'Makefile' --- Makefile 2010-09-28 18:57:31 +0000 +++ Makefile 2008-10-28 18:00:20 +0000 @@ -1,6 +1,6 @@ WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs \ -Wswitch-default -Wswitch-enum -Wunused-parameter \ - -Wstrict-aliasing=1 -Wextra -Wfloat-equal -Wundef -Wshadow \ + -Wstrict-aliasing=2 -Wextra -Wfloat-equal -Wundef -Wshadow \ -Wunsafe-loop-optimizations -Wpointer-arith \ -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \ -Wconversion -Wstrict-prototypes -Wold-style-definition \ @@ -8,22 +8,14 @@ # -Wunreachable-code #DEBUG=-ggdb3 # For info about _FORTIFY_SOURCE, see -# -# and . -FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC -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 +# +FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIE -pie +LINK_FORTIFY=-z relro -pie #COVERAGE=--coverage OPTIMIZE=-Os LANGUAGE=-std=gnu99 htmldir=man -version=1.2 +version=1.0.2 SED=sed ## Use these settings for a traditional /usr/local install @@ -42,22 +34,21 @@ INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools ## -GNUTLS_CFLAGS=$(shell pkg-config --cflags-only-I gnutls) -GNUTLS_LIBS=$(shell pkg-config --libs gnutls) +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; getconf LFS_CFLAGS) -GPGME_LIBS=$(shell gpgme-config --libs; getconf LFS_LIBS; \ - getconf LFS_LDFLAGS) +GPGME_CFLAGS=$(shell gpgme-config --cflags) +GPGME_LIBS=$(shell gpgme-config --libs) # Do not change these two CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \ $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \ -DVERSION='"$(version)"' -LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag)) +LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) # Commands to format a DocBook document into a manual page -DOCBOOKTOMAN=$(strip cd $(dir $<); xsltproc --nonet --xinclude \ +DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \ --param man.charmap.use.subset 0 \ --param make.year.ranges 1 \ --param make.single.year.ranges 1 \ @@ -65,11 +56,11 @@ --param man.authors.section.enabled 0 \ /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \ $(notdir $<); \ - $(MANPOST) $(notdir $@)) + $(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' -DOCBOOKTOHTML=$(strip xsltproc --nonet --xinclude \ +DOCBOOKTOHTML=xsltproc --nonet --xinclude \ --param make.year.ranges 1 \ --param make.single.year.ranges 1 \ --param man.output.quietly 1 \ @@ -77,22 +68,20 @@ --param citerefentry.link 1 \ --output $@ \ /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \ - $<; $(HTMLPOST) $@) + $<; $(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 + plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo CPROGS=plugin-runner $(PLUGINS) -PROGS=mandos mandos-keygen mandos-ctl mandos-monitor $(CPROGS) -DOCS=mandos.8 mandos-keygen.8 mandos-monitor.8 mandos-ctl.8 \ - mandos.conf.5 mandos-clients.conf.5 plugin-runner.8mandos \ +PROGS=mandos mandos-keygen $(CPROGS) +DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \ 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 + plugins.d/password-prompt.8mandos mandos.conf.5 \ + plugins.d/usplash.8mandos plugins.d/splashy.8mandos \ + plugins.d/askpass-fifo.8mandos mandos-clients.conf.5 htmldocs=$(addsuffix .xhtml,$(DOCS)) @@ -133,20 +122,6 @@ 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) @@ -174,44 +149,31 @@ # Update all these files with version number $(version) common.ent: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\($$/\1$(version)">/' \ - $@) + $(SED) --in-place \ + --expression='s/^\($$/\1$(version)"/' \ + $@ mandos: Makefile - $(strip $(SED) --in-place \ + $(SED) --in-place \ --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@) + $@ mandos-keygen: Makefile - $(strip $(SED) --in-place \ + $(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 \ + $(SED) --in-place \ --expression='s/^\(Version:\).*/\1\t$(version)/' \ - $@) - $(strip $(SED) --in-place \ + $@ + $(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/' \ - $@) + $@ -plugins.d/mandos-client: plugins.d/mandos-client.c - $(LINK.c) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) $(strip\ - ) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ +plugins.d/mandos-client: plugins.d/mandos-client.o + $(LINK.o) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) \ + $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ .PHONY : all doc html clean distclean run-client run-server install \ install-server install-client uninstall uninstall-server \ @@ -230,21 +192,9 @@ # Run the client with a local config and key run-client: all keydir/seckey.txt keydir/pubkey.txt - @echo "###################################################################" - @echo "# The following error messages are harmless and can be safely #" - @echo "# ignored. The messages are caused by not running as root, but #" - @echo "# you should NOT run \"make run-client\" as root unless you also #" - @echo "# unpacked and compiled Mandos as root, which is NOT recommended. #" - @echo "# From plugin-runner: setuid: Operation not permitted #" - @echo "# From askpass-fifo: mkfifo: Permission denied #" - @echo "# From mandos-client: setuid: Operation not permitted #" - @echo "# seteuid: Operation not permitted #" - @echo "# klogctl: Operation not permitted #" - @echo "###################################################################" ./plugin-runner --plugin-dir=plugins.d \ --config-file=plugin-runner.conf \ - --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt \ - $(CLIENTARGS) + --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt # Used by run-client keydir/seckey.txt keydir/pubkey.txt: mandos-keygen @@ -253,13 +203,7 @@ # Run the server with a local config run-server: confdir/mandos.conf confdir/clients.conf - @echo "#################################################################" - @echo "# NOTE: Please IGNORE the error about \"Could not open file #" - @echo "# u'/var/run/mandos.pid'\" - it is harmless and is caused by #" - @echo "# the server not running as root. Do NOT run \"make run-server\" #" - @echo "# server as root if you didn't also unpack and compile it thus. #" - @echo "#################################################################" - ./mandos --debug --no-dbus --configdir=confdir $(SERVERARGS) + ./mandos --debug --configdir=confdir # Used by run-server confdir/mandos.conf: mandos.conf @@ -281,16 +225,10 @@ install-server: doc install --directory $(CONFDIR) 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) \ 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 install --mode=u=rw,go=r default-mandos \ @@ -300,10 +238,6 @@ 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 \ @@ -336,32 +270,27 @@ install --mode=u=rwxs,go=rx \ --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/askpass-fifo - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/plymouth install initramfs-tools-hook \ $(INITRAMFSTOOLS)/hooks/mandos install --mode=u=rw,go=r initramfs-tools-hook-conf \ $(INITRAMFSTOOLS)/conf-hooks.d/mandos install initramfs-tools-script \ - $(INITRAMFSTOOLS)/scripts/init-premount/mandos + $(INITRAMFSTOOLS)/scripts/local-top/mandos install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR) 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/password-prompt.8mandos \ + > $(MANDIR)/man8/password-prompt.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 @@ -373,11 +302,7 @@ uninstall-server: -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 @@ -395,18 +320,16 @@ $(PREFIX)/lib/mandos/plugins.d/usplash \ $(PREFIX)/lib/mandos/plugins.d/splashy \ $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \ - $(PREFIX)/lib/mandos/plugins.d/plymouth \ $(INITRAMFSTOOLS)/hooks/mandos \ $(INITRAMFSTOOLS)/conf-hooks.d/mandos \ - $(INITRAMFSTOOLS)/scripts/init-premount/mandos \ + $(INITRAMFSTOOLS)/scripts/local-top/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 \ + $(MANDIR)/man8/mandos-client.8mandos.gz -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \ $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR) update-initramfs -k all -u @@ -415,7 +338,6 @@ 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 \ $(DESTDIR)/var/run/mandos.pid === modified file 'NEWS' --- NEWS 2010-09-28 18:57:31 +0000 +++ NEWS 2008-10-17 18:56:25 +0000 @@ -1,137 +1,6 @@ This NEWS file records noteworthy changes, very tersely. See the manual for detailed information. -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 === modified file 'README' --- README 2010-09-26 18:32:58 +0000 +++ README 2008-10-28 18:00:20 +0000 @@ -99,7 +99,7 @@ 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 + bring it back up, but if both reboots at the same time they will stay down until someone types in the password on one of them. ** Faking ping replies? @@ -134,19 +134,15 @@ In the early designs, the mandos-client(8mandos) 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-client(8mandos) and - password-prompt(8mandos), and a plugin-runner(8mandos) 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. + found. This duality of purpose was seen to be too complex to be a + viable way to continue. Instead, the programs are now separated + into mandos-client(8mandos) and password-prompt(8mandos), and a + plugin-runner(8mandos) exist to run them both in parallel, allowing + the first plugin to succeed 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 plymouth(8). + Three additional plugins are provided: * usplash(8mandos) This prompts for a password when using usplash(8). * splashy(8mandos) @@ -156,14 +152,16 @@ cryptsetup, this plugin listens to the same FIFO as askpass would do. - More plugins can easily be written and added by the system + (None of these take any options or reads any files.) + + More plugins could easily be written and added by the system administrator; see the section called "WRITING PLUGINS" in plugin-runner(8mandos) to learn the plugin requirements. * Copyright - Copyright © 2008-2010 Teddy Hogeborn - Copyright © 2008-2010 Björn Påhlsson + Copyright © 2008 Teddy Hogeborn + Copyright © 2008 Björn Påhlsson ** License: === modified file 'TODO' --- TODO 2010-09-26 18:32:58 +0000 +++ TODO 2008-12-10 01:26:02 +0000 @@ -1,134 +1,55 @@ -*- org -*- -* Use _attribute_((nonnull)) wherever possible. - * mandos-client -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] Retry a server which has a non-definite reply: -*** A closed connection during the TLS handshake -*** A TCP timeout -** TODO [#B] Use capabilities instead of seteuid(). -** TODO [#A] Retry --connect forever - -* splashy -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name - -* usplash -** TODO [#A] Make it work again -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO Use [[info:libc:Argz%20Functions][argz_extract]] - -* askpass-fifo -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] Drop privileges after opening FIFO. - -* password-prompt -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] lock stdin (with flock()?) - -* TODO [#B] passdev - -* plugin-runner -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#C] use same file name rules as run-parts(8) -** kernel command line option for debug info -** TODO [#B] Use openat() +** TODO [#B] Temporarily lower kernel log level + for less printouts during sucessfull boot. + klogctl(6, NULL, 0); klogctl(7, NULL, 0); +** TODO [#C] IPv4 support * mandos (server) -** TODO [#B] Log level :BUGS: -** TODO Persistent state :BUGS: - /var/lib/mandos/* -*** TODO /etc/mandos/clients.d/*.conf - Watch this directory and add/remove/update clients? -** TODO [#C] config for TXT record -** TODO Log level option - syslogger.setLevel(logging.WARNING) - + SetLogLevel D-Bus call -** TODO Implement --foreground :BUGS: +** TODO [#B] Log level :bugs: +** TODO /etc/mandos/clients.d/*.conf + Watch this directory and add/remove/update clients? +** TODO config for TXT record +** TODO [#B] Run-time communication with server :bugs: + Probably using D-Bus + See also [[*Mandos-tools]] +*** Client class +*** Main server + + SetLogLevel + syslogger.setLevel(logging.WARNING) + + Quit + + [[http://log.ometer.com/2007-05.html][Best D-Bus practices]] +** TODO Implement --foreground :bugs: [[info:standards:Option%20Table][Table of Long Options]] ** TODO Implement --socket [[info:standards:Option%20Table][Table of Long Options]] -** TODO Date+time on console log messages :BUGS: +** TODO Date+time on console log messages :bugs: Is this the default? -** TODO [#C] DBusServiceObjectUsingSuper -** TODO [#B] Global enable/disable flag -** TODO [#B] By-client countdown on secrets given -** TODO [#B] Fix problem with fsck taking a really long time - Whenever a client successfully gets a secret it could get a - one-time timeout boost to allow for an fsck-incurred delay -** TODO [#A] Delay before client receives key - This would give an operator opportunity to cancel the request if - desired. -** TODO [#A] Client manual approval mode - A client needs manual approval on the server before it gets the - secret -** TODO [#B] Support RFC 3339 time duration syntax -** More D-Bus methods -*** NeedsApproval(50, True) -> timeout, default approve - Default approval is configurable, but True by default - + Approve(True) -> approve sending saved - + Approve(False) -> Close client connection immediately -*** NeedsPassword(50) - Timeout, default disapprove - + SetPass(u"gazonk", True) -> Approval, persistent - + Approve(False) -> Close client connection immediately -** TODO [#C] python-parsedatetime -** TODO [#C] systemd/launchd - http://0pointer.de/blog/projects/systemd.html -** TODO Separate logging logic to own object -** TODO make clients to a dict! -** TODO [#A] Limit approval_delay to max gnutls/tls timeout value -** TODO [#B] break the wait on approval_delay if connection dies -** TODO Generate Client.runtime_expansions from client options + extra -** TODO Allow %%(checker)s as a runtime expansion - -* mandos.xml -** Add mandos contact info in manual pages - -* mandos-ctl -*** Handle "no D-Bus server" and/or "no Mandos server found" better -*** [#B] --dump option -** TODO Support RFC 3339 time duration syntax - -* TODO mandos-dispatch - Listens for specified D-Bus signals and spawns shell commands with - arguments. - -* mandos-monitor -** TODO help should be toggable -** Urwid client data displayer - Better view of client data in the listing -*** Properties popup -** Nicer crashes. Stack traces Messes up shell. -*** Print a nice "We are sorry" message, save stack trace to log. -** Show timeout countdown for approval +** TODO delete hook when clients fall out by timeout + This will not be strictly necessary when the D-Bus interface is + implemented. + +* Mandos-tools/utilities + All of this probably using D-Bus +** TODO List clients +** TODO Disable client +** TODO Enable client +** TODO Reset timer * mandos-keygen -** TODO Loop until passwords match when run interactively ** TODO "--secfile" option Using the "secfile" option instead of "secret" ** TODO [#B] "--test" option For testing decryption before rebooting. -* Makefile -** TODO Add "--Xlinker --as-needed" - http://udrepper.livejournal.com/19395.html -** TODO [#C] Implement DEB_BUILD_OPTIONS - http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options - -* Package +* [#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] /etc/bash_completion.d/mandos +*** TODO Do not install in initrd.img if configured not to. + Use "/etc/initramfs-tools/conf.d/mandos"? Definitely a debconf + question. +** TODO /etc/bash_completion.d/mandos From XML sources directly? -* Side Stuff -** TODO Locate which packet move the other bin/sh when busy box is deactivated -** TODO contact owner of packet, and ask them to have that shell static in position regardless of busybox - #+STARTUP: showall === modified file 'clients.conf' --- clients.conf 2010-09-12 03:00:40 +0000 +++ clients.conf 2008-09-21 13:42:34 +0000 @@ -2,28 +2,19 @@ # 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. +# 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. +# 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 = 0s - -# How long one approval will last. -;approval_duration = 1s +;checker = fping -q -- %(host)s ;#### @@ -71,9 +62,4 @@ ; ;# Parameters from the [DEFAULT] section can be overridden per client. ;interval = 5m -; -;# This client requires manual approval before it receives its secret. -;approved_by_default = False -;# Require approval within 30 seconds. -;approval_delay = 30s ;#### === modified file 'common.ent' --- common.ent 2010-09-28 18:57:31 +0000 +++ common.ent 2008-09-30 07:23:39 +0000 @@ -1,3 +1,3 @@ - + === removed file 'dbus-mandos.conf' --- dbus-mandos.conf 2009-11-09 07:35:16 +0000 +++ dbus-mandos.conf 1970-01-01 00:00:00 +0000 @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - === modified file 'debian/changelog' --- debian/changelog 2010-09-28 18:57:31 +0000 +++ debian/changelog 2008-10-17 18:56:25 +0000 @@ -1,148 +1,3 @@ -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) 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. === modified file 'debian/control' --- debian/control 2010-10-01 18:40:55 +0000 +++ debian/control 2008-09-30 03:19:39 +0000 @@ -4,10 +4,10 @@ Maintainer: Mandos Maintainers Uploaders: Teddy Hogeborn , Björn Påhlsson -Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl, - libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc, +Build-Depends: debhelper (>= 7), docbook-xsl, libavahi-core-dev, + libgpgme11-dev, libgnutls-dev, xsltproc, po-debconf, pkg-config -Standards-Version: 3.9.1 +Standards-Version: 3.8.0 Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files Homepage: http://www.fukt.bsnet.se/mandos @@ -15,8 +15,7 @@ Package: mandos Architecture: all Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus, - python-avahi, python-gobject, avahi-daemon, adduser, - python-urwid, python (>=2.6) | python-multiprocessing + python-avahi, avahi-daemon, gnupg (< 2), adduser Recommends: fping Description: a server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows @@ -35,8 +34,7 @@ Package: mandos-client Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup, - gnupg (<< 2) +Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup Enhances: cryptsetup Description: do unattended reboots with an encrypted root file system This is the client part of the Mandos system, which allows === modified file 'debian/copyright' --- debian/copyright 2010-09-26 18:32:58 +0000 +++ debian/copyright 2008-10-15 19:54:11 +0000 @@ -5,8 +5,8 @@ Upstream-Source: Files: * -Copyright: Copyright © 2008-2010 Teddy Hogeborn -Copyright: Copyright © 2008-2010 Björn Påhlsson +Copyright: Copyright © 2008 Teddy Hogeborn +Copyright: Copyright © 2008 Björn Påhlsson License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as === modified file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2010-09-27 17:53:53 +0000 +++ debian/mandos-client.README.Debian 2008-10-05 17:38:31 +0000 @@ -1,86 +1,45 @@ -* Choose the Client Network Interface - - Please make sure that the correct network interface is specified in - the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf" - file. If the setting is empty, the interface will be autodetected - at boot time, which may not be correct. *If* the DEVICE setting is - changed, it will be necessary to update the initrd image by running - the command - - update-initramfs -k all -u - - The device can 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/nfsroot.txt", - available in the "linux-doc-*" package. - - Note that since this network interface is used in the initial RAM - disk environment, the network interface *must* exist at that stage. - Thus, the interface can *not* be a pseudo-interface such as "br0" or - "tun0"; instead, a real interface (such as "eth0") must be used. - -* 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 +* Configure The Server + + A client key has been automatically created in /etc/keys/mandos. + The next step is to run "mandos-keygen --password" to get a config + file section. This should be appended to /etc/mandos/clients.conf + on the Mandos server. + +* Use the Correct Network Interface + + If some other network interface than "eth0" is used, it will be + necessary to edit /etc/mandos/plugin-runner.conf to uncomment and + change the line there. If this is done, it will be necessary to + update the initrd image by doing "update-initramfs -k all -u". + +* Test the Server + + After the server has been started and 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: - /usr/lib/mandos/plugins.d/mandos-client \ + # /usr/lib/mandos/plugins.d/mandos-client \ --pubkey=/etc/keys/mandos/pubkey.txt \ --seckey=/etc/keys/mandos/seckey.txt; echo 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. + it, and output it to standard output. It is now possible to verify + the correctness of the password before rebooting. * 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 + + 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. -* 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. - -* 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::". - - For very advanced users, it it possible to specify simply - "mandos=connect" on the kernel command line to make the system only - set up the network (using the data in the "ip=" option) and not pass - any extra "--connect" options to mandos-client at boot. For this to - work, "--options-for=mandos-client:--connect=
:" needs - to be manually added to the file "/etc/mandos/plugin-runner.conf". - - -- Teddy Hogeborn , Mon, 27 Sep 2010 19:53:21 +0200 + -- Teddy Hogeborn , Sun, 5 Oct 2008 19:04:26 +0200 === added file 'debian/mandos-client.config' --- debian/mandos-client.config 1970-01-01 00:00:00 +0000 +++ debian/mandos-client.config 2008-09-21 04:22:50 +0000 @@ -0,0 +1,27 @@ +#! /bin/sh +# +# config Mandos Debconf configuration. +# + +# Source debconf library. +. /usr/share/debconf/confmodule +if ! db_version 2.0; then + echo "mandos.config: need DebConf 2.0 or later" + exit 1 +fi + +set -e +umask 022 + +# Now, interaction. Batch it in case any front ends can use this. +db_beginblock + +# If this is a first time install then prompt +if [ "$1" = "configure" -a "$2" != "" ]; then + db_input high mandos-client/not-yet-configured || true +fi + +db_endblock +db_go || true + +exit 0 === modified file 'debian/mandos-client.dirs' --- debian/mandos-client.dirs 2009-02-07 04:50:39 +0000 +++ debian/mandos-client.dirs 2008-09-17 00:34:09 +0000 @@ -2,4 +2,4 @@ usr/sbin usr/share/initramfs-tools/hooks usr/share/initramfs-tools/conf-hooks.d -usr/share/initramfs-tools/scripts/init-premount +usr/share/initramfs-tools/scripts/local-top === modified file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2009-01-18 06:41:57 +0000 +++ debian/mandos-client.lintian-overrides 2008-10-01 15:29:01 +0000 @@ -1,3 +1,8 @@ +# This example command line is long without spaces, but it must be +# that way; it's part of the point of showing it. +# +mandos-client binary: manpage-has-errors-from-man usr/share/man/man8/plugin-runner.8mandos.gz 297: warning [p 4, 5.8i]: can't break line + # This directory contains secret client key files. # mandos-client binary: non-standard-dir-perm etc/keys/mandos/ 0700 != 0755 @@ -11,7 +16,7 @@ # 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 +# 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. # === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2009-05-24 23:36:15 +0000 +++ debian/mandos-client.postinst 2008-12-10 01:26:02 +0000 @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # This script can be called in the following ways: # # After the package was installed: @@ -15,33 +15,26 @@ # If prerm fails during replacement due to conflict: # abort-remove in-favour +. /usr/share/debconf/confmodule + # Update the initial RAM file system image update_initramfs() { if [ -x /usr/sbin/update-initramfs ]; then update-initramfs -u -k all fi - - if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then - # Make old initrd.img files unreadable too, in case they were - # 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 + case "$(getent passwd mandos)" in + *:Mandos\ password\ system,,,:/nonexistent:/bin/false) + usermod --login _mandos mandos + groupmod --new-name _mandos mandos + return + ;; + esac # Create new user and group if ! getent passwd _mandos >/dev/null; then adduser --system --force-badname --quiet --home /nonexistent \ @@ -63,15 +56,15 @@ case "$1" in configure) - add_mandos_user "$@" - create_key "$@" - update_initramfs "$@" + add_mandos_user + create_key + update_initramfs ;; abort-upgrade|abort-deconfigure|abort-remove) ;; *) - echo "$0 called with unknown argument '$1'" 1>&2 + echo "$0 called with unknown argument \`$1'" 1>&2 exit 1 ;; esac === modified file 'debian/mandos-client.postrm' --- debian/mandos-client.postrm 2009-01-18 00:16:57 +0000 +++ debian/mandos-client.postrm 2008-09-19 20:54:58 +0000 @@ -50,7 +50,7 @@ ;; *) - echo "$0 called with unknown argument '$1'" 1>&2 + echo "$0 called with unknown argument \`$1'" 1>&2 exit 1 ;; esac === added file 'debian/mandos-client.templates' --- debian/mandos-client.templates 1970-01-01 00:00:00 +0000 +++ debian/mandos-client.templates 2008-09-21 04:22:50 +0000 @@ -0,0 +1,8 @@ +Template: mandos-client/not-yet-configured +Type: note +_Description: Your system needs more configuration[ mandos-client] + Your system can not function as a Mandos client until a + password for this client has been added to the + configuration on the Mandos server. Please read + /usr/share/doc/mandos-client/README.Debian.gz to find out + how. === modified file 'debian/mandos.README.Debian' --- debian/mandos.README.Debian 2009-09-08 06:28:20 +0000 +++ debian/mandos.README.Debian 2008-09-21 04:22:50 +0000 @@ -1,10 +1,7 @@ -The Mandos server is useless without at least one configured client in +The Mandos server cannot run without at least one configured client in /etc/mandos/clients.conf. To create one, install the "mandos-client" -package on a client computer, and run the command - - # mandos-keygen --password - -there to get a config file stanza. Append the output of that command -to the file "/etc/mandos/clients.conf" on the Mandos server. - - -- Teddy Hogeborn , Tue, 8 Sep 2009 06:57:45 +0200 +package on a client computer, and run "mandos-keygen --password" there +to get a config file stanza. Append that to /etc/mandos/clients.conf +on the Mandos server. + + -- Teddy Hogeborn , Sat, 20 Sep 2008 21:21:19 +0200 === added file 'debian/mandos.config' --- debian/mandos.config 1970-01-01 00:00:00 +0000 +++ debian/mandos.config 2008-09-21 04:22:50 +0000 @@ -0,0 +1,27 @@ +#! /bin/sh +# +# config Mandos Debconf configuration. +# + +# Source debconf library. +. /usr/share/debconf/confmodule +if ! db_version 2.0; then + echo "mandos.config: need DebConf 2.0 or later" + exit 1 +fi + +set -e +umask 022 + +# Now, interaction. Batch it in case any front ends can use this. +db_beginblock + +# If this is a first time install then prompt +if [ "$1" = "configure" -a "$2" != "" ]; then + db_input high mandos/not-yet-configured || true +fi + +db_endblock +db_go || true + +exit 0 === modified file 'debian/mandos.dirs' --- debian/mandos.dirs 2010-09-15 17:33:14 +0000 +++ debian/mandos.dirs 2008-09-17 00:34:09 +0000 @@ -2,5 +2,4 @@ usr/share/man/man8 etc/init.d etc/default -etc/dbus-1/system.d usr/sbin === modified file 'debian/mandos.docs' --- debian/mandos.docs 2010-09-12 03:00:40 +0000 +++ debian/mandos.docs 2008-10-18 11:17:22 +0000 @@ -1,4 +1,3 @@ NEWS README TODO -DBUS-API === modified file 'debian/mandos.postinst' --- debian/mandos.postinst 2009-05-24 23:28:04 +0000 +++ debian/mandos.postinst 2008-12-10 01:26:02 +0000 @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -e # This script can be called in the following ways: # # After the package was installed: @@ -15,17 +15,17 @@ # If prerm fails during replacement due to conflict: # abort-remove in-favour +. /usr/share/debconf/confmodule + 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 + case "$(getent passwd mandos)" in + *:Mandos\ password\ system,,,:/nonexistent:/bin/false) + usermod --login _mandos mandos + groupmod --new-name _mandos mandos + ;; + esac # Create new user and group if ! getent passwd _mandos >/dev/null; then adduser --system --force-badname --quiet \ @@ -39,7 +39,7 @@ ;; *) - echo "$0 called with unknown argument '$1'" 1>&2 + echo "$0 called with unknown argument \`$1'" 1>&2 exit 1 ;; esac === modified file 'debian/mandos.prerm' --- debian/mandos.prerm 2009-01-18 00:16:57 +0000 +++ debian/mandos.prerm 2008-09-21 13:42:34 +0000 @@ -6,12 +6,12 @@ set -e # summary of how this script can be called: -# * 'remove' -# * 'upgrade' -# * 'failed-upgrade' -# * 'remove' 'in-favour' -# * 'deconfigure' 'in-favour' -# 'removing' +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' # # for details, see /usr/share/doc/packaging-manual/ @@ -28,7 +28,7 @@ upgrade|failed-upgrade) ;; *) - echo "prerm called with unknown argument '$1'" >&2 + echo "prerm called with unknown argument \`$1'" >&2 exit 0 ;; esac === added file 'debian/mandos.templates' --- debian/mandos.templates 1970-01-01 00:00:00 +0000 +++ debian/mandos.templates 2008-09-21 04:22:50 +0000 @@ -0,0 +1,9 @@ +Template: mandos/not-yet-configured +Type: note +_Description: Your system needs more configuration[ mandos] + Your system has not yet been completely configured as a + Mandos server - clients need to be added to to + /etc/mandos/clients.conf. Please read + /usr/share/doc/mandos/README.Debian.gz to find out how. + . + (The server has not been started.) === added file 'debian/po/POTFILES.in' --- debian/po/POTFILES.in 1970-01-01 00:00:00 +0000 +++ debian/po/POTFILES.in 2008-09-21 04:22:50 +0000 @@ -0,0 +1,2 @@ +[type: gettext/rfc822deb] mandos.templates +[type: gettext/rfc822deb] mandos-client.templates === added file 'debian/po/sv.po' --- debian/po/sv.po 1970-01-01 00:00:00 +0000 +++ debian/po/sv.po 2008-09-21 04:22:50 +0000 @@ -0,0 +1,66 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the mandos package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 1.0\n" +"Report-Msgid-Bugs-To: mandos@packages.debian.org\n" +"POT-Creation-Date: 2008-09-20 23:01+0200\n" +"PO-Revision-Date: 2008-09-21 06:01+0200\n" +"Last-Translator: Teddy Hogeborn \n" +"Language-Team: Swedish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "Your system needs more configuration[ mandos]" +msgstr "Ditt system behöver ytterligare konfigurering" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +#| msgid "" +#| "Your system has not yet been completely configured as a Mandos server - " +#| "you need to setup /etc/mandos/clients.conf. Please read /usr/share/doc/" +#| "mandos/README.Debian.gz to find out how." +msgid "" +"Your system has not yet been completely configured as a Mandos server - " +"clients need to be added to to /etc/mandos/clients.conf. Please read /usr/" +"share/doc/mandos/README.Debian.gz to find out how." +msgstr "" +"Ditt system är inte helt inställd som en Mandos-server än -\n" +"det behövs läggas till klienter i Mandos-serverns\n" +"inställingar. Var vänlig läs\n" +"/usr/share/doc/mandos-client/README.Debian.gz för att få\n" +"veta hur." + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "(The server has not been started.)" +msgstr "(Servern har inte startats.)" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "Your system needs more configuration[ mandos-client]" +msgstr "Ditt system behöver ytterligare konfigurering" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"Your system can not function as a Mandos client until a password for this " +"client has been added to the configuration on the Mandos server. Please " +"read /usr/share/doc/mandos-client/README.Debian.gz to find out how." +msgstr "" +"Ditt system kan inte fungera som en Mandos-klient förrän\n" +"ett krypterat lösenord har lagts till i Mandos-serverns\n" +"inställingar. Var vänlig läs\n" +"/usr/share/doc/mandos-client/README.Debian.gz för att få\n" +"veta hur." === modified file 'debian/rules' --- debian/rules 2010-09-09 18:16:14 +0000 +++ debian/rules 2008-09-30 18:59:44 +0000 @@ -15,20 +15,6 @@ # This has to be exported to make some magic below work. export DH_OPTIONS -# -pie was broken briefly on the mips and mipsel architectures, see -# -BINUTILS_V := $(shell dpkg-query --showformat='$${Version}' \ - --show binutils) -ifeq (yes,$(shell dpkg --compare-versions $(BINUTILS_V) lt 2.20-3 \ - && dpkg --compare-versions $(BINUTILS_V) ge 2.19.1-1 \ - && echo yes)) - ifneq (,$(strip $(findstring :$(DEB_HOST_ARCH):,:mips:mipsel:) \ - $(findstring :$(DEB_BUILD_ARCH):,:mips:mipsel:))) - BROKEN_PIE := yes - export BROKEN_PIE - endif -endif - configure: configure-stamp configure-stamp: dh_testdir @@ -52,6 +38,7 @@ rm -f build-arch-stamp build-indep-stamp configure-stamp dh_auto_clean dh_clean + debconf-updatepo install: install-indep install-arch install-indep: @@ -61,7 +48,7 @@ dh_installdirs --indep $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server dh_lintian - dh_installinit --onlyscripts \ + dh_installinit --onlyscripts --no-start \ --update-rcd-params="defaults 25 15" dh_install --indep @@ -79,6 +66,7 @@ dh_testroot dh_installchangelogs dh_installdocs + dh_installdebconf dh_link dh_strip dh_compress === removed file 'debian/watch' --- debian/watch 2010-09-15 17:17:46 +0000 +++ debian/watch 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -version=3 -ftp://ftp.fukt.bsnet.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z|xz) === modified file 'init.d-mandos' --- init.d-mandos 2009-09-16 23:28:39 +0000 +++ init.d-mandos 2008-09-21 12:04:02 +0000 @@ -1,8 +1,8 @@ #! /bin/sh ### BEGIN INIT INFO # Provides: mandos -# Required-Start: $remote_fs $syslog avahi -# Required-Stop: $remote_fs $syslog avahi +# Required-Start: $remote_fs avahi-daemon +# Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Mandos server === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2010-09-09 18:16:14 +0000 +++ initramfs-tools-hook 2008-12-10 01:26:02 +0000 @@ -51,13 +51,16 @@ 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" +mandos_user="`{ getent passwd _mandos \ + || getent passwd mandos \ + || getent passwd nobody \ + || echo ::65534::::; } \ + | awk --field-separator=: '{ print $3 }'`" +mandos_group="`{ getent group _mandos \ + || getent group mandos \ + || getent group nogroup \ + || echo ::65534:; } \ + | awk --field-separator=: '{ print $3 }'`" # The Mandos network client uses the network auto_add_modules net @@ -88,10 +91,9 @@ 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}" ;; + *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;; + "*") :;; + *) copy_exec "$file" "${PLUGINDIR}";; esac done @@ -99,10 +101,9 @@ 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}" ;; + *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;; + "*") :;; + *) copy_exec "$file" "${PLUGINDIR}";; esac done @@ -114,7 +115,7 @@ fi # Config files -for file in /etc/mandos/plugin-runner.conf; do +for file in /etc/mandos/*; do if [ -d "$file" ]; then continue fi @@ -122,17 +123,17 @@ done if [ ${mandos_user} != 65534 ]; then - sed --in-place --expression="1i--userid=${mandos_user}" \ - "${DESTDIR}${CONFDIR}/plugin-runner.conf" + PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf" + echo "--userid=${mandos_user}" >> "$PLUGINRUNNERCONF" fi if [ ${mandos_group} != 65534 ]; then - sed --in-place --expression="1i--groupid=${mandos_group}" \ - "${DESTDIR}${CONFDIR}/plugin-runner.conf" + PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf" + echo "--groupid=${mandos_group}" >> "$PLUGINRUNNERCONF" fi # Key files -for file in "$keydir"/*; do +for file in "$keydir"/*; do if [ -d "$file" ]; then continue fi @@ -167,9 +168,7 @@ chmod a+rX "${DESTDIR}$dir" fi done -for dir in "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do - if [ -d "$dir" ]; then - find "$dir" \! -perm -u+rw,g+r -prune -or -print0 \ - | xargs --null --no-run-if-empty chmod a+rX - fi +for dir in /lib /usr/lib; do + find "${DESTDIR}$dir" \! -perm -u+rw,g+r -prune -or -print0 \ + | xargs --null --no-run-if-empty chmod a+rX done === modified file 'initramfs-tools-hook-conf' --- initramfs-tools-hook-conf 2009-05-17 00:50:09 +0000 +++ initramfs-tools-hook-conf 2008-08-12 19:22:34 +0000 @@ -1,13 +1,1 @@ -# -*- shell-script -*- - -# if mkinitramfs is started by mkinitramfs-kpkg, mkinitramfs-kpkg has -# already touched the initrd file with umask 022 before we had a -# chance to affect it. We cannot allow a readable initrd file, -# therefore we must fix this now. -if [ -e "${outfile}" ] \ - && [ `stat --format=%s "${outfile}"` -eq 0 ]; then - rm "${outfile}" - (umask 027; touch "${outfile}") -fi - UMASK=027 === modified file 'initramfs-tools-script' --- initramfs-tools-script 2009-09-16 23:28:39 +0000 +++ initramfs-tools-script 2008-09-07 15:42:11 +0000 @@ -6,102 +6,27 @@ # # 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 - ;; + 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 -test -r /conf/conf.d/cryptroot -test -w /conf/conf.d - -# 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 - configure_networking - if [ -n "$connect" ]; then - cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf - - --options-for=mandos-client:--connect=${connect} -EOF - fi -fi +test -w /conf/conf.d/cryptroot # Do not replace cryptroot file unless we need to. replace_cryptroot=no === modified file 'mandos' --- mandos 2010-09-28 18:57:31 +0000 +++ mandos 2008-12-10 01:26:02 +0000 @@ -6,13 +6,13 @@ # 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-2010 Teddy Hogeborn -# Copyright © 2008-2010 Björn Påhlsson +# Copyright © 2008 Teddy Hogeborn +# Copyright © 2008 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 @@ -33,9 +33,9 @@ from __future__ import division, with_statement, absolute_import -import SocketServer as socketserver +import SocketServer import socket -import optparse +from optparse import OptionParser import datetime import errno import gnutls.crypto @@ -44,23 +44,19 @@ import gnutls.library.functions import gnutls.library.constants import gnutls.library.types -import ConfigParser as configparser +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 -import cPickle as pickle -import multiprocessing +from contextlib import closing import dbus import dbus.service @@ -69,42 +65,28 @@ from dbus.mainloop.glib import DBusGMainLoop import ctypes import ctypes.util -import xml.dom.minidom -import inspect - -try: - SO_BINDTODEVICE = socket.SO_BINDTODEVICE -except AttributeError: - try: - from IN import SO_BINDTODEVICE - except ImportError: - SO_BINDTODEVICE = None - - -version = "1.2" - -#logger = logging.getLogger(u'mandos') -logger = logging.Logger(u'mandos') + +version = "1.0.2" + +logger = logging.Logger('mandos') syslogger = (logging.handlers.SysLogHandler (facility = logging.handlers.SysLogHandler.LOG_DAEMON, address = "/dev/log")) syslogger.setFormatter(logging.Formatter - (u'Mandos [%(process)d]: %(levelname)s:' - u' %(message)s')) + ('Mandos: %(levelname)s: %(message)s')) logger.addHandler(syslogger) console = logging.StreamHandler() -console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:' - u' %(levelname)s:' - u' %(message)s')) +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 - super(AvahiError, self).__init__(value, *args, **kwargs) - def __unicode__(self): - return unicode(repr(self.value)) + super(AvahiError, self).__init__() + def __str__(self): + return repr(self.value) class AvahiServiceError(AvahiError): pass @@ -115,12 +97,11 @@ 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: u'Mandos' - type: string; Example: u'_mandos._tcp'. + name: string; Example: 'Mandos' + type: string; Example: '_mandos._tcp'. See port: integer; what port to announce TXT: list of strings; TXT record for the service @@ -129,14 +110,10 @@ 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 = u"", host = u"", max_renames = 32768, - protocol = avahi.PROTO_UNSPEC, bus = None): + domain = "", host = "", max_renames = 32768): self.interface = interface self.name = name self.type = servicetype @@ -146,290 +123,270 @@ 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 def rename(self): """Derived from the Avahi example code""" if self.rename_count >= self.max_renames: logger.critical(u"No suitable Zeroconf service name found" u" after %i retries, exiting.", self.rename_count) - raise AvahiServiceError(u"Too many renames") - self.name = unicode(self.server.GetAlternativeServiceName(self.name)) + raise AvahiServiceError("Too many renames") + self.name = server.GetAlternativeServiceName(self.name) logger.info(u"Changing Zeroconf service name to %r ...", - self.name) + str(self.name)) syslogger.setFormatter(logging.Formatter - (u'Mandos (%s) [%%(process)d]:' - u' %%(levelname)s: %%(message)s' - % self.name)) + ('Mandos (%s): %%(levelname)s:' + ' %%(message)s' % self.name)) self.remove() - try: - self.add() - except dbus.exceptions.DBusException, error: - logger.critical(u"DBusException: %s", error) - self.cleanup() - os._exit(1) + self.add() self.rename_count += 1 def remove(self): """Derived from the Avahi example code""" - 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""" - 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.group.connect_to_signal('StateChanged', - self - .entry_group_state_changed) + 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' ...", - 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(u"Avahi entry group 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.") - self.rename() - elif state == avahi.ENTRY_GROUP_FAILURE: - logger.critical(u"Avahi: Error in group state changed %s", - unicode(error)) - raise AvahiGroupError(u"State changed: %s" - % unicode(error)) - def cleanup(self): - """Derived from the Avahi example code""" - if self.group is not None: - self.group.Free() - self.group = None - def server_state_changed(self, state): - """Derived from the Avahi example code""" - logger.debug(u"Avahi server state change: %i", state) - if state == avahi.SERVER_COLLISION: - logger.error(u"Zeroconf server name collision") - self.remove() - elif state == avahi.SERVER_RUNNING: - self.add() - 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), - avahi.DBUS_INTERFACE_SERVER) - self.server.connect_to_signal(u"StateChanged", - self.server_state_changed) - self.server_state_changed(self.server.GetState()) - - -class Client(object): + 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 + + +def _datetime_to_dbus_struct(dt, variant_level=0): + """Convert a UTC datetime.datetime() to a D-Bus struct. + The format is special to this application, since we could not find + any other standard way.""" + return dbus.Struct((dbus.Int16(dt.year), + dbus.Byte(dt.month), + dbus.Byte(dt.day), + dbus.Byte(dt.hour), + dbus.Byte(dt.minute), + dbus.Byte(dt.second), + dbus.UInt32(dt.microsecond)), + signature="nyyyyyu", + variant_level=variant_level) + + +class Client(dbus.service.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 + 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(); (UTC) object creation + last_started: datetime.datetime(); (UTC) + started: bool() + last_checked_ok: datetime.datetime(); (UTC) or None + 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_callback_tag: a gobject event source tag, or None - checker_command: string; External command which is run to check - if client lives. %() expansions are done at + 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 gobject event source tag, or None - created: datetime.datetime(); (UTC) object creation - current_checker_command: string; current running checker_command - disable_hook: If set, called by disable() as disable_hook(self) - disable_initiator_tag: a gobject event source tag, or None - enabled: bool() - fingerprint: string (40 or 32 hexadecimal digits); used to - uniquely identify the client - 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_enabled: datetime.datetime(); (UTC) - 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 - runtime_expansions: Allowed attributes for runtime expansion. + dbus_object_path: dbus.ObjectPath + Private attibutes: + _timeout: Real variable for 'timeout' + _interval: Real variable for 'interval' + _timeout_milliseconds: Used when calling gobject.timeout_add() + _interval_milliseconds: - '' - """ - - runtime_expansions = (u"approval_delay", u"approval_duration", - u"created", u"enabled", u"fingerprint", - u"host", u"interval", u"last_checked_ok", - u"last_enabled", u"name", u"timeout") - - @staticmethod - def _timedelta_to_milliseconds(td): - "Convert a datetime.timedelta() to milliseconds" - return ((td.days * 24 * 60 * 60 * 1000) - + (td.seconds * 1000) - + (td.microseconds // 1000)) - - def timeout_milliseconds(self): - "Return the 'timeout' attribute in milliseconds" - return self._timedelta_to_milliseconds(self.timeout) - - def interval_milliseconds(self): - "Return the 'interval' attribute in milliseconds" - return self._timedelta_to_milliseconds(self.interval) - - def approval_delay_milliseconds(self): - return self._timedelta_to_milliseconds(self.approval_delay) - - def __init__(self, name = None, disable_hook=None, config=None): + def _set_timeout(self, timeout): + "Setter function for the 'timeout' attribute" + self._timeout = timeout + self._timeout_milliseconds = ((self.timeout.days + * 24 * 60 * 60 * 1000) + + (self.timeout.seconds * 1000) + + (self.timeout.microseconds + // 1000)) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"timeout"), + (dbus.UInt64(self._timeout_milliseconds, + variant_level=1))) + timeout = property(lambda self: self._timeout, _set_timeout) + del _set_timeout + + def _set_interval(self, interval): + "Setter function for the 'interval' attribute" + self._interval = interval + self._interval_milliseconds = ((self.interval.days + * 24 * 60 * 60 * 1000) + + (self.interval.seconds + * 1000) + + (self.interval.microseconds + // 1000)) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"interval"), + (dbus.UInt64(self._interval_milliseconds, + variant_level=1))) + interval = property(lambda self: self._interval, _set_interval) + del _set_interval + + def __init__(self, name = None, stop_hook=None, config=None): """Note: the 'checker' key in 'config' sets the 'checker_command' attribute and *not* the 'checker' attribute.""" - self.name = name + self.dbus_object_path = (dbus.ObjectPath + ("/Mandos/clients/" + + name.replace(".", "_"))) + dbus.service.Object.__init__(self, bus, + self.dbus_object_path) if config is None: config = {} + self.name = name 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[u"fingerprint"].upper() + self.fingerprint = (config["fingerprint"].upper() .replace(u" ", u"")) logger.debug(u" Fingerprint: %s", self.fingerprint) - if u"secret" in config: - self.secret = config[u"secret"].decode(u"base64") - elif u"secfile" in config: - with open(os.path.expanduser(os.path.expandvars - (config[u"secfile"])), - "rb") as secfile: + if "secret" in config: + self.secret = config["secret"].decode(u"base64") + elif "secfile" in config: + with closing(open(os.path.expanduser + (os.path.expandvars + (config["secfile"])))) as secfile: self.secret = secfile.read() else: raise TypeError(u"No secret or secfile for client %s" % self.name) - self.host = config.get(u"host", u"") + self.host = config.get("host", "") self.created = datetime.datetime.utcnow() - self.enabled = False - self.last_approval_request = None - self.last_enabled = None + self.started = False + self.last_started = None self.last_checked_ok = None - self.timeout = string_to_delta(config[u"timeout"]) - self.interval = string_to_delta(config[u"interval"]) - self.disable_hook = disable_hook + 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.checker_command = config[u"checker"] - self.current_checker_command = None - self.last_connect = None - self._approved = None - self.approved_by_default = config.get(u"approved_by_default", - True) - self.approvals_pending = 0 - self.approval_delay = string_to_delta( - config[u"approval_delay"]) - self.approval_duration = string_to_delta( - config[u"approval_duration"]) - self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock()) + self.checker_command = config["checker"] - def send_changedstate(self): - self.changedstate.acquire() - self.changedstate.notify_all() - self.changedstate.release() - - def enable(self): + def start(self): """Start this client's checker and timeout hooks""" - if getattr(self, u"enabled", False): - # Already enabled - return - self.send_changedstate() - self.last_enabled = datetime.datetime.utcnow() + self.last_started = datetime.datetime.utcnow() # Schedule a new checker to be started an 'interval' from now, # and every interval from then on. self.checker_initiator_tag = (gobject.timeout_add - (self.interval_milliseconds(), + (self._interval_milliseconds, self.start_checker)) - # Schedule a disable() when 'timeout' has passed - self.disable_initiator_tag = (gobject.timeout_add - (self.timeout_milliseconds(), - self.disable)) - self.enabled = True # Also start a new checker *right now*. self.start_checker() + # Schedule a stop() when 'timeout' has passed + self.stop_initiator_tag = (gobject.timeout_add + (self._timeout_milliseconds, + self.stop)) + self.started = True + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"started"), + dbus.Boolean(True, variant_level=1)) + self.PropertyChanged(dbus.String(u"last_started"), + (_datetime_to_dbus_struct + (self.last_started, variant_level=1))) - def disable(self, quiet=True): - """Disable this client.""" - if not getattr(self, "enabled", False): + def stop(self): + """Stop this client.""" + if not getattr(self, "started", False): return False - if not quiet: - self.send_changedstate() - if not quiet: - logger.info(u"Disabling client %s", self.name) - if getattr(self, u"disable_initiator_tag", False): - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = None - if getattr(self, u"checker_initiator_tag", False): + logger.info(u"Stopping client %s", self.name) + 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.disable_hook: - self.disable_hook(self) - self.enabled = False + if self.stop_hook: + self.stop_hook(self) + self.started = False + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"started"), + dbus.Boolean(False, variant_level=1)) # Do not run this again if called by a gobject.timeout_add return False def __del__(self): - self.disable_hook = None - self.disable() + self.stop_hook = None + self.stop() def checker_callback(self, pid, condition, command): """The checker has completed, so take appropriate actions.""" self.checker_callback_tag = None self.checker = None - if os.WIFEXITED(condition): - exitstatus = os.WEXITSTATUS(condition) - if exitstatus == 0: - logger.info(u"Checker for %(name)s succeeded", - vars(self)) - self.checked_ok() - else: - logger.info(u"Checker for %(name)s failed", - vars(self)) - else: + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"checker_running"), + dbus.Boolean(False, variant_level=1)) + if (os.WIFEXITED(condition) + and (os.WEXITSTATUS(condition) == 0)): + logger.info(u"Checker for %(name)s succeeded", + vars(self)) + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(True), + dbus.UInt16(condition), + dbus.String(command)) + self.bump_timeout() + elif not os.WIFEXITED(condition): logger.warning(u"Checker for %(name)s crashed?", vars(self)) + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) + else: + logger.info(u"Checker for %(name)s failed", + vars(self)) + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) - def checked_ok(self): + def bump_timeout(self): """Bump up the timeout for this client. - This should only be called when the client has been seen, alive and well. """ self.last_checked_ok = datetime.datetime.utcnow() - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = (gobject.timeout_add - (self.timeout_milliseconds(), - self.disable)) - - def need_approval(self): - self.last_approval_request = datetime.datetime.utcnow() + gobject.source_remove(self.stop_initiator_tag) + self.stop_initiator_tag = (gobject.timeout_add + (self._timeout_milliseconds, + self.stop)) + self.PropertyChanged(dbus.String(u"last_checked_ok"), + (_datetime_to_dbus_struct + (self.last_checked_ok, + variant_level=1))) 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 @@ -438,44 +395,23 @@ # 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 a checker exists, make sure it is not a zombie - try: - pid, status = os.waitpid(self.checker.pid, os.WNOHANG) - except (AttributeError, OSError), error: - if (isinstance(error, OSError) - and error.errno != errno.ECHILD): - raise error - else: - if pid: - logger.warning(u"Checker was a zombie") - gobject.source_remove(self.checker_callback_tag) - self.checker_callback(pid, status, - self.current_checker_command) - # Start a new checker if needed + # than 'timeout' for the client to be declared invalid, which + # is as it should be. if self.checker is None: try: # In case checker_command has exactly one % operator command = self.checker_command % self.host except TypeError: # Escape attributes for the shell - escaped_attrs = dict( - (attr, - re.escape(unicode(str(getattr(self, attr, u"")), - errors= - u'replace'))) - for attr in - self.runtime_expansions) - + escaped_attrs = dict((key, re.escape(str(val))) + for key, val in + vars(self).iteritems()) try: command = self.checker_command % escaped_attrs except TypeError, error: logger.error(u'Could not format string "%s":' u' %s', self.checker_command, error) return True # Try again later - self.current_checker_command = command try: logger.info(u"Starting checker %r for %s", command, self.name) @@ -485,17 +421,15 @@ # always replaced by /dev/null.) self.checker = subprocess.Popen(command, close_fds=True, - shell=True, cwd=u"/") + shell=True, cwd="/") + # Emit D-Bus signal + self.CheckerStarted(command) + self.PropertyChanged(dbus.String("checker_running"), + dbus.Boolean(True, variant_level=1)) self.checker_callback_tag = (gobject.child_watch_add (self.checker.pid, self.checker_callback, data=command)) - # The checker may have completed before the gobject - # watch was added. Check for this. - pid, status = os.waitpid(self.checker.pid, os.WNOHANG) - if pid: - gobject.source_remove(self.checker_callback_tag) - self.checker_callback(pid, status, command) except OSError, error: logger.error(u"Failed to start subprocess: %s", error) @@ -507,427 +441,133 @@ if self.checker_callback_tag: gobject.source_remove(self.checker_callback_tag) self.checker_callback_tag = None - if getattr(self, u"checker", None) is None: + if getattr(self, "checker", None) is None: return logger.debug(u"Stopping checker for %(name)s", vars(self)) try: os.kill(self.checker.pid, signal.SIGTERM) - #time.sleep(0.5) + #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=u"v", - access=u"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 != u"ay": - raise ValueError(u"Byte arrays not supported for non-'ay'" - u" signature %r" % 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(u"_dbus_property"): - func._dbus_name = func._dbus_name[:-14] - func._dbus_get_args_options = {u'byte_arrays': byte_arrays } - return func - return decorator - - -class DBusPropertyException(dbus.exceptions.DBusException): - """A base class for D-Bus property-related exceptions - """ - def __unicode__(self): - return unicode(str(self)) - - -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 DBusObjectWithProperties(dbus.service.Object): - """A D-Bus object with properties. - - Classes inheriting from this can use the dbus_service_property - decorator to expose methods as D-Bus properties. It exposes the - standard Get(), Set(), and GetAll() methods on the D-Bus. - """ - - @staticmethod - def _is_dbus_property(obj): - return getattr(obj, u"_dbus_is_property", False) - - def _get_all_dbus_properties(self): - """Returns a generator of (name, attribute) pairs - """ - return ((prop._dbus_name, prop) - for name, prop in - inspect.getmembers(self, self._is_dbus_property)) - - 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 name in (property_name, - property_name + u"_dbus_property"): - prop = getattr(self, name, None) - if (prop is None - or not self._is_dbus_property(prop) - or prop._dbus_name != property_name - or (interface_name and prop._dbus_interface - and interface_name != prop._dbus_interface)): - continue - return prop - # No such property - raise DBusPropertyNotFound(self.dbus_object_path + u":" - + interface_name + u"." - + property_name) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss", - out_signature=u"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 == u"write": - raise DBusPropertyAccessException(property_name) - value = prop() - if not hasattr(value, u"variant_level"): - return value - return type(value)(value, variant_level=value.variant_level+1) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"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 == u"read": - raise DBusPropertyAccessException(property_name) - if prop._dbus_get_args_options[u"byte_arrays"]: - # The byte_arrays option is not supported yet on - # signatures other than "ay". - if prop._dbus_signature != u"ay": - raise ValueError - value = dbus.ByteArray(''.join(unichr(byte) - for byte in value)) - prop(value) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s", - out_signature=u"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". - """ - all = {} - for name, prop in self._get_all_dbus_properties(): - 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 == u"write": - continue - value = prop() - if not hasattr(value, u"variant_level"): - all[name] = value - continue - all[name] = type(value)(value, variant_level= - value.variant_level+1) - return dbus.Dictionary(all, signature=u"sv") - - @dbus.service.method(dbus.INTROSPECTABLE_IFACE, - out_signature=u"s", - path_keyword='object_path', - connection_keyword='connection') - def Introspect(self, object_path, connection): - """Standard D-Bus method, overloaded to insert property tags. - """ - xmlstring = dbus.service.Object.Introspect(self, object_path, - connection) - try: - document = xml.dom.minidom.parseString(xmlstring) - def make_tag(document, name, prop): - e = document.createElement(u"property") - e.setAttribute(u"name", name) - e.setAttribute(u"type", prop._dbus_signature) - e.setAttribute(u"access", prop._dbus_access) - return e - for if_tag in document.getElementsByTagName(u"interface"): - for tag in (make_tag(document, name, prop) - for name, prop - in self._get_all_dbus_properties() - if prop._dbus_interface - == if_tag.getAttribute(u"name")): - if_tag.appendChild(tag) - # Add the names to the return values for the - # "org.freedesktop.DBus.Properties" methods - if (if_tag.getAttribute(u"name") - == u"org.freedesktop.DBus.Properties"): - for cn in if_tag.getElementsByTagName(u"method"): - if cn.getAttribute(u"name") == u"Get": - for arg in cn.getElementsByTagName(u"arg"): - if (arg.getAttribute(u"direction") - == u"out"): - arg.setAttribute(u"name", u"value") - elif cn.getAttribute(u"name") == u"GetAll": - for arg in cn.getElementsByTagName(u"arg"): - if (arg.getAttribute(u"direction") - == u"out"): - arg.setAttribute(u"name", u"props") - xmlstring = document.toxml(u"utf-8") - document.unlink() - except (AttributeError, xml.dom.DOMException, - xml.parsers.expat.ExpatError), error: - logger.error(u"Failed to override Introspection method", - error) - return xmlstring - - -class ClientDBus(Client, DBusObjectWithProperties): - """A Client class using D-Bus - - Attributes: - dbus_object_path: dbus.ObjectPath - bus: dbus.SystemBus() - """ - - runtime_expansions = (Client.runtime_expansions - + (u"dbus_object_path",)) - - # dbus.service.Object doesn't use super(), so we can't either. - - def __init__(self, bus = None, *args, **kwargs): - self._approvals_pending = 0 - 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 = unicode(self.name).translate( - {ord(u"."): ord(u"_"), - ord(u"-"): ord(u"_")}) - self.dbus_object_path = (dbus.ObjectPath - (u"/clients/" + client_object_name)) - DBusObjectWithProperties.__init__(self, self.bus, - self.dbus_object_path) - - def _get_approvals_pending(self): - return self._approvals_pending - def _set_approvals_pending(self, value): - old_value = self._approvals_pending - self._approvals_pending = value - bval = bool(value) - if (hasattr(self, "dbus_object_path") - and bval is not bool(old_value)): - dbus_bool = dbus.Boolean(bval, variant_level=1) - self.PropertyChanged(dbus.String(u"ApprovalPending"), - dbus_bool) - - approvals_pending = property(_get_approvals_pending, - _set_approvals_pending) - del _get_approvals_pending, _set_approvals_pending - - @staticmethod - def _datetime_to_dbus(dt, variant_level=0): - """Convert a UTC datetime.datetime() to a D-Bus type.""" - return dbus.String(dt.isoformat(), - variant_level=variant_level) - - def enable(self): - oldstate = getattr(self, u"enabled", False) - r = Client.enable(self) - if oldstate != self.enabled: - # Emit D-Bus signals - self.PropertyChanged(dbus.String(u"Enabled"), - dbus.Boolean(True, variant_level=1)) - self.PropertyChanged( - dbus.String(u"LastEnabled"), - self._datetime_to_dbus(self.last_enabled, - variant_level=1)) - return r - - def disable(self, quiet = False): - oldstate = getattr(self, u"enabled", False) - r = Client.disable(self, quiet=quiet) - if not quiet and oldstate != self.enabled: - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Enabled"), - dbus.Boolean(False, variant_level=1)) - return r - - def __del__(self, *args, **kwargs): - try: - self.remove_from_connection() - except LookupError: - pass - if hasattr(DBusObjectWithProperties, u"__del__"): - DBusObjectWithProperties.__del__(self, *args, **kwargs) - Client.__del__(self, *args, **kwargs) - - def checker_callback(self, pid, condition, command, - *args, **kwargs): - self.checker_callback_tag = None - self.checker = None - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"CheckerRunning"), + self.PropertyChanged(dbus.String(u"checker_running"), dbus.Boolean(False, variant_level=1)) - if os.WIFEXITED(condition): - exitstatus = os.WEXITSTATUS(condition) - # Emit D-Bus signal - self.CheckerCompleted(dbus.Int16(exitstatus), - dbus.Int64(condition), - dbus.String(command)) - else: - # Emit D-Bus signal - self.CheckerCompleted(dbus.Int16(-1), - dbus.Int64(condition), - dbus.String(command)) - - return Client.checker_callback(self, pid, condition, command, - *args, **kwargs) - - def checked_ok(self, *args, **kwargs): - r = Client.checked_ok(self, *args, **kwargs) - # Emit D-Bus signal - self.PropertyChanged( - dbus.String(u"LastCheckedOK"), - (self._datetime_to_dbus(self.last_checked_ok, - variant_level=1))) - return r - - def need_approval(self, *args, **kwargs): - r = Client.need_approval(self, *args, **kwargs) - # Emit D-Bus signal - self.PropertyChanged( - dbus.String(u"LastApprovalRequest"), - (self._datetime_to_dbus(self.last_approval_request, - variant_level=1))) - return r - - def start_checker(self, *args, **kwargs): - old_checker = self.checker - if self.checker is not None: - old_checker_pid = self.checker.pid - else: - old_checker_pid = None - r = Client.start_checker(self, *args, **kwargs) - # Only if new checker process was started - if (self.checker is not None - and old_checker_pid != self.checker.pid): - # Emit D-Bus signal - self.CheckerStarted(self.current_checker_command) - self.PropertyChanged( - dbus.String(u"CheckerRunning"), - dbus.Boolean(True, variant_level=1)) - return r - - def stop_checker(self, *args, **kwargs): - old_checker = getattr(self, u"checker", None) - r = Client.stop_checker(self, *args, **kwargs) - if (old_checker is not None - and getattr(self, u"checker", None) is None): - self.PropertyChanged(dbus.String(u"CheckerRunning"), - dbus.Boolean(False, variant_level=1)) - return r - - def _reset_approved(self): - self._approved = None - return False - - def approve(self, value=True): - self.send_changedstate() - self._approved = value - gobject.timeout_add(self._timedelta_to_milliseconds - (self.approval_duration), - self._reset_approved) - - - ## D-Bus methods, signals & properties - _interface = u"se.bsnet.fukt.Mandos.Client" - - ## Signals + + def still_valid(self): + """Has the timeout not yet passed for this client?""" + if not getattr(self, "started", False): + return False + now = datetime.datetime.utcnow() + if self.last_checked_ok is None: + return now < (self.created + self.timeout) + else: + return now < (self.last_checked_ok + self.timeout) + + ## D-Bus methods & signals + _interface = u"org.mandos_system.Mandos.Client" + + # BumpTimeout - method + BumpTimeout = dbus.service.method(_interface)(bump_timeout) + BumpTimeout.__name__ = "BumpTimeout" # CheckerCompleted - signal - @dbus.service.signal(_interface, signature=u"nxs") - def CheckerCompleted(self, exitcode, waitstatus, command): + @dbus.service.signal(_interface, signature="bqs") + def CheckerCompleted(self, success, condition, command): "D-Bus signal" pass # CheckerStarted - signal - @dbus.service.signal(_interface, signature=u"s") + @dbus.service.signal(_interface, signature="s") def CheckerStarted(self, command): "D-Bus signal" pass + # GetAllProperties - method + @dbus.service.method(_interface, out_signature="a{sv}") + def GetAllProperties(self): + "D-Bus method" + return dbus.Dictionary({ + dbus.String("name"): + dbus.String(self.name, variant_level=1), + dbus.String("fingerprint"): + dbus.String(self.fingerprint, variant_level=1), + dbus.String("host"): + dbus.String(self.host, variant_level=1), + dbus.String("created"): + _datetime_to_dbus_struct(self.created, + variant_level=1), + dbus.String("last_started"): + (_datetime_to_dbus_struct(self.last_started, + variant_level=1) + if self.last_started is not None + else dbus.Boolean(False, variant_level=1)), + dbus.String("started"): + dbus.Boolean(self.started, variant_level=1), + dbus.String("last_checked_ok"): + (_datetime_to_dbus_struct(self.last_checked_ok, + variant_level=1) + if self.last_checked_ok is not None + else dbus.Boolean (False, variant_level=1)), + dbus.String("timeout"): + dbus.UInt64(self._timeout_milliseconds, + variant_level=1), + dbus.String("interval"): + dbus.UInt64(self._interval_milliseconds, + variant_level=1), + dbus.String("checker"): + dbus.String(self.checker_command, + variant_level=1), + dbus.String("checker_running"): + dbus.Boolean(self.checker is not None, + variant_level=1), + }, signature="sv") + + # IsStillValid - method + IsStillValid = (dbus.service.method(_interface, out_signature="b") + (still_valid)) + IsStillValid.__name__ = "IsStillValid" + # PropertyChanged - signal - @dbus.service.signal(_interface, signature=u"sv") + @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=u"s") - def Rejected(self, reason): - "D-Bus signal" - pass - - # NeedApproval - signal - @dbus.service.signal(_interface, signature=u"tb") - def NeedApproval(self, timeout, default): - "D-Bus signal" - return self.need_approval() - - ## Methods - - # Approve - method - @dbus.service.method(_interface, in_signature=u"b") - def Approve(self, value): - self.approve(value) - - # CheckedOK - method - @dbus.service.method(_interface) - def CheckedOK(self): - return self.checked_ok() - - # Enable - method - @dbus.service.method(_interface) - def Enable(self): - "D-Bus method" - self.enable() + # SetChecker - method + @dbus.service.method(_interface, in_signature="s") + def SetChecker(self, checker): + "D-Bus setter method" + self.checker_command = checker + + # SetHost - method + @dbus.service.method(_interface, in_signature="s") + def SetHost(self, host): + "D-Bus setter method" + self.host = host + + # SetInterval - method + @dbus.service.method(_interface, in_signature="t") + def SetInterval(self, milliseconds): + self.interval = datetime.timdeelta(0, 0, 0, milliseconds) + + # SetSecret - method + @dbus.service.method(_interface, in_signature="ay", + byte_arrays=True) + def SetSecret(self, secret): + "D-Bus setter method" + self.secret = str(secret) + + # SetTimeout - method + @dbus.service.method(_interface, in_signature="t") + def SetTimeout(self, milliseconds): + self.timeout = datetime.timedelta(0, 0, 0, milliseconds) + + # Start - method + Start = dbus.service.method(_interface)(start) + Start.__name__ = "Start" # StartChecker - method @dbus.service.method(_interface) @@ -935,664 +575,230 @@ "D-Bus method" self.start_checker() - # Disable - method + # Stop - method @dbus.service.method(_interface) - def Disable(self): + def Stop(self): "D-Bus method" - self.disable() + self.stop() # StopChecker - method - @dbus.service.method(_interface) - def StopChecker(self): - self.stop_checker() - - ## Properties - - # ApprovalPending - property - @dbus_service_property(_interface, signature=u"b", access=u"read") - def ApprovalPending_dbus_property(self): - return dbus.Boolean(bool(self.approvals_pending)) - - # ApprovedByDefault - property - @dbus_service_property(_interface, signature=u"b", - access=u"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) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovedByDefault"), - dbus.Boolean(value, variant_level=1)) - - # ApprovalDelay - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def ApprovalDelay_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.approval_delay_milliseconds()) - self.approval_delay = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovalDelay"), - dbus.UInt64(value, variant_level=1)) - - # ApprovalDuration - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def ApprovalDuration_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self._timedelta_to_milliseconds( - self.approval_duration)) - self.approval_duration = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovalDuration"), - dbus.UInt64(value, variant_level=1)) - - # Name - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Name_dbus_property(self): - return dbus.String(self.name) - - # Fingerprint - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Fingerprint_dbus_property(self): - return dbus.String(self.fingerprint) - - # Host - property - @dbus_service_property(_interface, signature=u"s", - access=u"readwrite") - def Host_dbus_property(self, value=None): - if value is None: # get - return dbus.String(self.host) - self.host = value - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Host"), - dbus.String(value, variant_level=1)) - - # Created - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Created_dbus_property(self): - return dbus.String(self._datetime_to_dbus(self.created)) - - # LastEnabled - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def LastEnabled_dbus_property(self): - if self.last_enabled is None: - return dbus.String(u"") - return dbus.String(self._datetime_to_dbus(self.last_enabled)) - - # Enabled - property - @dbus_service_property(_interface, signature=u"b", - access=u"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=u"s", - access=u"readwrite") - def LastCheckedOK_dbus_property(self, value=None): - if value is not None: - self.checked_ok() - return - if self.last_checked_ok is None: - return dbus.String(u"") - return dbus.String(self._datetime_to_dbus(self - .last_checked_ok)) - - # LastApprovalRequest - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def LastApprovalRequest_dbus_property(self): - if self.last_approval_request is None: - return dbus.String(u"") - return dbus.String(self. - _datetime_to_dbus(self - .last_approval_request)) - - # Timeout - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def Timeout_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.timeout_milliseconds()) - self.timeout = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Timeout"), - dbus.UInt64(value, variant_level=1)) - if getattr(self, u"disable_initiator_tag", None) is None: - return - # Reschedule timeout - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = None - time_to_die = (self. - _timedelta_to_milliseconds((self - .last_checked_ok - + self.timeout) - - datetime.datetime - .utcnow())) - if time_to_die <= 0: - # The timeout has passed - self.disable() - else: - self.disable_initiator_tag = (gobject.timeout_add - (time_to_die, self.disable)) - - # Interval - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def Interval_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.interval_milliseconds()) - self.interval = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Interval"), - dbus.UInt64(value, variant_level=1)) - if getattr(self, u"checker_initiator_tag", None) is None: - return - # Reschedule checker run - gobject.source_remove(self.checker_initiator_tag) - self.checker_initiator_tag = (gobject.timeout_add - (value, self.start_checker)) - self.start_checker() # Start one now, too - - # Checker - property - @dbus_service_property(_interface, signature=u"s", - access=u"readwrite") - def Checker_dbus_property(self, value=None): - if value is None: # get - return dbus.String(self.checker_command) - self.checker_command = value - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Checker"), - dbus.String(self.checker_command, - variant_level=1)) - - # CheckerRunning - property - @dbus_service_property(_interface, signature=u"b", - access=u"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_service_property(_interface, signature=u"o", access=u"read") - def ObjectPath_dbus_property(self): - return self.dbus_object_path # is already a dbus.ObjectPath - - # Secret = property - @dbus_service_property(_interface, signature=u"ay", - access=u"write", byte_arrays=True) - def Secret_dbus_property(self, value): - self.secret = str(value) + StopChecker = dbus.service.method(_interface)(stop_checker) + StopChecker.__name__ = "StopChecker" del _interface -class ProxyClient(object): - def __init__(self, child_pipe, fpr, address): - self._pipe = child_pipe - self._pipe.send(('init', fpr, address)) - if not self._pipe.recv(): - raise KeyError() - - 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 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 + buf = ctypes.create_string_buffer(20) + buf_len = ctypes.c_size_t() + # Get the fingerprint from the certificate into the buffer + (gnutls.library.functions + .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), + ctypes.byref(buf_len))) + # Deinit the certificate + gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) + # Convert the buffer to a Python bytestring + fpr = ctypes.string_at(buf, buf_len.value) + # Convert the bytestring to hexadecimal notation + 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(u"TCP connection from: %s", - unicode(self.client_address)) - logger.debug(u"Pipe FD: %d", - self.server.child_pipe.fileno()) - - session = (gnutls.connection - .ClientSession(self.request, - gnutls.connection - .X509Credentials())) - - # Note: gnutls.connection.X509Credentials is really a - # generic GnuTLS certificate credentials object so long as - # no X.509 keys are added to it. Therefore, we can use it - # here despite using OpenPGP certificates. - - #priority = u':'.join((u"NONE", u"+VERS-TLS1.1", - # u"+AES-256-CBC", u"+SHA1", - # u"+COMP-NULL", u"+CTYPE-OPENPGP", - # u"+DHE-DSS")) - # Use a fallback default, since this MUST be set. - priority = self.server.gnutls_priority - if priority is None: - priority = u"NORMAL" - (gnutls.library.functions - .gnutls_priority_set_direct(session._c_object, - priority, None)) - - # Start communication using the Mandos protocol - # Get protocol number - line = self.request.makefile().readline() - logger.debug(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 - - # Start GnuTLS connection - 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 - logger.debug(u"Handshake succeeded") - - approval_required = False - try: - try: - fpr = self.fingerprint(self.peer_certificate - (session)) - except (TypeError, gnutls.errors.GNUTLSError), error: - logger.warning(u"Bad certificate: %s", error) - return - logger.debug(u"Fingerprint: %s", fpr) - - try: - client = ProxyClient(child_pipe, fpr, - self.client_address) - except KeyError: - return - - if client.approval_delay: - delay = client.approval_delay - client.approvals_pending += 1 - approval_required = True - - while True: - if not client.enabled: - logger.warning(u"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(u"Client %s needs approval", - client.name) - if self.server.use_dbus: - # Emit D-Bus signal - client.NeedApproval( - client.approval_delay_milliseconds(), - client.approved_by_default) - else: - logger.warning(u"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 - #x = float(client._timedelta_to_milliseconds(delay)) - time = datetime.datetime.now() - client.changedstate.acquire() - client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000)) - 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 - - sent_size = 0 - while sent_size < len(client.secret): - try: - sent = session.send(client.secret[sent_size:]) - except (gnutls.errors.GNUTLSError), error: - logger.warning("gnutls send failed") - return - logger.debug(u"Sent: %d, remaining: %d", - sent, len(client.secret) - - (sent_size + sent)) - sent_size += sent - - logger.info(u"Sending secret to %s", client.name) - # bump the timeout as if seen - client.checked_ok() - if self.server.use_dbus: - # Emit D-Bus signal - client.GotSecret() - - finally: - if approval_required: - client.approvals_pending -= 1 - try: - session.bye() - except (gnutls.errors.GNUTLSError), error: - logger.warning("GnuTLS bye failed") - - @staticmethod - 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(1) - cert_list = (gnutls.library.functions - .gnutls_certificate_get_peers - (session._c_object, ctypes.byref(list_size))) - if not bool(cert_list) and list_size.value != 0: - raise gnutls.errors.GNUTLSError(u"error getting peer" - u" certificate") - if list_size.value == 0: - return None - cert = cert_list[0] - return ctypes.string_at(cert.data, cert.size) - - @staticmethod - def fingerprint(openpgp): - "Convert an OpenPGP bytestring to a hexdigit fingerprint" - # New GnuTLS "datum" with the OpenPGP public key - datum = (gnutls.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 - (u"Verify failed")) - # New buffer for the fingerprint - buf = ctypes.create_string_buffer(20) - buf_len = ctypes.c_size_t() - # Get the fingerprint from the certificate into the buffer - (gnutls.library.functions - .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), - ctypes.byref(buf_len))) - # Deinit the certificate - gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) - # Convert the buffer to a Python bytestring - fpr = ctypes.string_at(buf, buf_len.value) - # Convert the bytestring to hexadecimal notation - hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr) - 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: - self.handle_error(request, address) - self.close_request(request) - - def process_request(self, request, address): - """Start a new process to process the request.""" - multiprocessing.Process(target = self.sub_process_main, - args = (request, address)).start() - -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() - - super(MultiprocessingMixInWithPipe, - self).process_request(request, client_address) - self.child_pipe.close() - self.add_pipe(parent_pipe) - - def add_pipe(self, parent_pipe): - """Dummy function; override as necessary""" - pass - -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")) + # Use a fallback default, since this MUST be set. + priority = self.server.settings.get("priority", "NORMAL") + (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) + for c in self.server.clients: + if c.fingerprint == fpr: + client = c + break + else: + 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 + ## This won't work here, since we're in a fork. + # client.bump_timeout() + 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.ForkingMixIn, + SocketServer.TCPServer, object): + """IPv6 TCP server. Accepts 'None' as address and/or port. Attributes: + settings: Server settings + clients: Set() of Client objects enabled: Boolean; whether this server is activated yet - interface: None or a network interface name (string) - use_ipv6: Boolean; to use IPv6 or not """ - def __init__(self, server_address, RequestHandlerClass, - interface=None, use_ipv6=True): - self.interface = interface - if use_ipv6: - self.address_family = socket.AF_INET6 - 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"] + self.enabled = False + super(IPv6_TCPServer, 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.""" - if self.interface is not None: - if SO_BINDTODEVICE is None: - logger.error(u"SO_BINDTODEVICE does not exist;" - u" cannot bind to interface %s", - self.interface) - else: - try: - self.socket.setsockopt(socket.SOL_SOCKET, - SO_BINDTODEVICE, - str(self.interface - + u'\0')) - except socket.error, error: - if error[0] == errno.EPERM: - logger.error(u"No permission to" - u" bind to interface %s", - self.interface) - elif error[0] == errno.ENOPROTOOPT: - logger.error(u"SO_BINDTODEVICE not available;" - u" cannot bind to interface %s", - self.interface) - else: - raise + 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.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 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 = u"::" # in6addr_any - else: - any_address = socket.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: +# 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 gobject.MainLoop event loop. - """ - def __init__(self, server_address, RequestHandlerClass, - interface=None, use_ipv6=True, clients=None, - gnutls_priority=None, use_dbus=True): - self.enabled = False - self.clients = clients - if self.clients is None: - self.clients = set() - self.use_dbus = use_dbus - self.gnutls_priority = gnutls_priority - IPv6_TCPServer.__init__(self, server_address, - RequestHandlerClass, - interface = interface, - use_ipv6 = use_ipv6) +# (self.settings +# ["interface"])) + return super(IPv6_TCPServer, self).server_bind() def server_activate(self): if self.enabled: - return socketserver.TCPServer.server_activate(self) + return super(IPv6_TCPServer, self).server_activate() def enable(self): self.enabled = True - def add_pipe(self, parent_pipe): - # Call "handle_ipc" for both data and EOF events - gobject.io_add_watch(parent_pipe.fileno(), - gobject.IO_IN | gobject.IO_HUP, - functools.partial(self.handle_ipc, - parent_pipe = parent_pipe)) - - def handle_ipc(self, source, condition, parent_pipe=None, - client_object=None): - condition_names = { - gobject.IO_IN: u"IN", # There is data to read. - gobject.IO_OUT: u"OUT", # Data can be written (without - # blocking). - gobject.IO_PRI: u"PRI", # There is urgent data to read. - gobject.IO_ERR: u"ERR", # Error condition. - gobject.IO_HUP: u"HUP" # Hung up (the connection has been - # broken, usually for pipes and - # sockets). - } - conditions_string = ' | '.join(name - for cond, name in - condition_names.iteritems() - if cond & condition) - # error or the other end of multiprocessing.Pipe has closed - if condition & (gobject.IO_ERR | condition & gobject.IO_HUP): - return False - - # Read a request from the child - request = parent_pipe.recv() - command = request[0] - - if command == 'init': - fpr = request[1] - address = request[2] - - for c in self.clients: - if c.fingerprint == fpr: - client = c - break - else: - logger.warning(u"Client not found for fingerprint: %s, ad" - u"dress: %s", fpr, address) - if self.use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientNotFound(fpr, address[0]) - parent_pipe.send(False) - return False - - gobject.io_add_watch(parent_pipe.fileno(), - gobject.IO_IN | gobject.IO_HUP, - functools.partial(self.handle_ipc, - parent_pipe = parent_pipe, - 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 callable(client_object.__getattribute__(attrname)): - 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 string_to_delta(interval): """Parse a string and return a datetime.timedelta - - >>> string_to_delta(u'7d') + + >>> string_to_delta('7d') datetime.timedelta(7) - >>> string_to_delta(u'60s') + >>> string_to_delta('60s') datetime.timedelta(0, 60) - >>> string_to_delta(u'60m') + >>> string_to_delta('60m') datetime.timedelta(0, 3600) - >>> string_to_delta(u'24h') + >>> string_to_delta('24h') datetime.timedelta(1) >>> string_to_delta(u'1w') datetime.timedelta(7) - >>> string_to_delta(u'5m 30s') + >>> string_to_delta('5m 30s') datetime.timedelta(0, 330) """ timevalue = datetime.timedelta(0) @@ -1611,46 +817,67 @@ elif suffix == u"w": delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value) else: - raise ValueError(u"Unknown suffix %r" % suffix) - except (ValueError, IndexError), e: - raise ValueError(e.message) + raise ValueError + except (ValueError, IndexError): + raise ValueError timevalue += delta return timevalue +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 - - Note: This function cannot accept a unicode string.""" + """Call the C function if_nametoindex(), or equivalent""" global if_nametoindex try: if_nametoindex = (ctypes.cdll.LoadLibrary - (ctypes.util.find_library(u"c")) + (ctypes.util.find_library("c")) .if_nametoindex) except (OSError, AttributeError): - logger.warning(u"Doing if_nametoindex the hard way") + 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 - with contextlib.closing(socket.socket()) as s: + with closing(socket.socket()) as s: ifreq = fcntl.ioctl(s, SIOCGIFINDEX, - struct.pack(str(u"16s16x"), - interface)) - interface_index = struct.unpack(str(u"I"), - ifreq[16:20])[0] + struct.pack("16s16x", interface)) + 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() os.setsid() if not nochdir: - os.chdir(u"/") + os.chdir("/") if os.fork(): sys.exit() if not noclose: @@ -1658,8 +885,7 @@ 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, - u"%s not a character device" - % os.path.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()) @@ -1668,37 +894,26 @@ def main(): - - ################################################################## - # Parsing of options, both command line and config file - - parser = optparse.OptionParser(version = "%%prog %s" % version) - parser.add_option("-i", u"--interface", type=u"string", - metavar="IF", help=u"Bind to interface IF") - parser.add_option("-a", u"--address", type=u"string", - help=u"Address to listen for requests on") - parser.add_option("-p", u"--port", type=u"int", - help=u"Port number to receive requests on") - parser.add_option("--check", action=u"store_true", - help=u"Run self-test") - parser.add_option("--debug", action=u"store_true", - help=u"Debug mode; run in foreground and log to" - u" terminal") - parser.add_option("--debuglevel", type=u"string", metavar="LEVEL", - help=u"Debug level for stdout output") - parser.add_option("--priority", type=u"string", help=u"GnuTLS" - u" priority string (see GnuTLS documentation)") - parser.add_option("--servicename", type=u"string", - metavar=u"NAME", help=u"Zeroconf service name") - parser.add_option("--configdir", type=u"string", - default=u"/etc/mandos", metavar=u"DIR", - help=u"Directory to search for configuration" - u" files") - parser.add_option("--no-dbus", action=u"store_false", - dest=u"use_dbus", help=u"Do not provide D-Bus" - u" system bus interface") - parser.add_option("--no-ipv6", action=u"store_false", - dest=u"use_ipv6", help=u"Do not use IPv6") + 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 = parser.parse_args()[0] if options.check: @@ -1707,320 +922,235 @@ sys.exit() # Default values for config file for server-global settings - server_defaults = { u"interface": u"", - u"address": u"", - u"port": u"", - u"debug": u"False", - u"priority": - u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", - u"servicename": u"Mandos", - u"use_dbus": u"True", - u"use_ipv6": u"True", - u"debuglevel": u"", + 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, - u"mandos.conf")) + server_config.read(os.path.join(options.configdir, "mandos.conf")) # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() - # Use the appropriate methods on the non-string config options - for option in (u"debug", u"use_dbus", u"use_ipv6"): - server_settings[option] = server_config.getboolean(u"DEFAULT", - option) - if server_settings["port"]: - server_settings["port"] = server_config.getint(u"DEFAULT", - u"port") + # 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 (u"interface", u"address", u"port", u"debug", - u"priority", u"servicename", u"configdir", - u"use_dbus", u"use_ipv6", u"debuglevel"): + for option in ("interface", "address", "port", "debug", + "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 type(server_settings[option]) is str: - server_settings[option] = unicode(server_settings[option]) # Now we have our good server settings in "server_settings" - ################################################################## - - # For convenience - debug = server_settings[u"debug"] - debuglevel = server_settings[u"debuglevel"] - use_dbus = server_settings[u"use_dbus"] - use_ipv6 = server_settings[u"use_ipv6"] - - if server_settings[u"servicename"] != u"Mandos": + debug = server_settings["debug"] + + if not debug: + syslogger.setLevel(logging.WARNING) + console.setLevel(logging.WARNING) + + if server_settings["servicename"] != "Mandos": syslogger.setFormatter(logging.Formatter - (u'Mandos (%s) [%%(process)d]:' - u' %%(levelname)s: %%(message)s' - % server_settings[u"servicename"])) + ('Mandos (%s): %%(levelname)s:' + ' %%(message)s' + % server_settings["servicename"])) # Parse config file with clients - client_defaults = { u"timeout": u"1h", - u"interval": u"5m", - u"checker": u"fping -q -- %%(host)s", - u"host": u"", - u"approval_delay": u"0s", - u"approval_duration": u"1s", + 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[u"configdir"], - u"clients.conf")) - - global mandos_dbus_service - mandos_dbus_service = None - - tcp_server = MandosServer((server_settings[u"address"], - server_settings[u"port"]), - ClientHandler, - interface=(server_settings[u"interface"] - or None), - use_ipv6=use_ipv6, - gnutls_priority= - server_settings[u"priority"], - use_dbus=use_dbus) - if not debug: - pidfilename = u"/var/run/mandos.pid" - try: - pidfile = open(pidfilename, u"w") - except IOError: - logger.error(u"Could not open file %r", pidfilename) - - try: - uid = pwd.getpwnam(u"_mandos").pw_uid - gid = pwd.getpwnam(u"_mandos").pw_gid + client_config = ConfigParser.SafeConfigParser(client_defaults) + client_config.read(os.path.join(server_settings["configdir"], + "clients.conf")) + + clients = Set() + tcp_server = IPv6_TCPServer((server_settings["address"], + server_settings["port"]), + TCP_handler, + settings=server_settings, + clients=clients) + pidfilename = "/var/run/mandos.pid" + try: + pidfile = open(pidfilename, "w") + except IOError, error: + logger.error("Could not open file %r", pidfilename) + + try: + uid = pwd.getpwnam("_mandos").pw_uid except KeyError: try: - uid = pwd.getpwnam(u"mandos").pw_uid - gid = pwd.getpwnam(u"mandos").pw_gid + uid = pwd.getpwnam("mandos").pw_uid except KeyError: try: - uid = pwd.getpwnam(u"nobody").pw_uid - gid = pwd.getpwnam(u"nobody").pw_gid + uid = pwd.getpwnam("nobody").pw_uid except KeyError: uid = 65534 + try: + gid = pwd.getpwnam("_mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("nogroup").pw_gid + except KeyError: gid = 65534 try: + os.setuid(uid) os.setgid(gid) - os.setuid(uid) except OSError, error: if error[0] != errno.EPERM: raise error - if not debug and not debuglevel: - syslogger.setLevel(logging.WARNING) - console.setLevel(logging.WARNING) - if debuglevel: - level = getattr(logging, debuglevel.upper()) - syslogger.setLevel(level) - console.setLevel(level) - - if debug: - # Enable all possible GnuTLS debugging - - # "Use a log level over 10 to enable all debugging options." - # - GnuTLS manual - gnutls.library.functions.gnutls_global_set_log_level(11) - - @gnutls.library.types.gnutls_log_func - def debug_gnutls(level, string): - logger.debug(u"GnuTLS: %s", string[:-1]) - - (gnutls.library.functions - .gnutls_global_set_log_function(debug_gnutls)) - - # 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) - + global service + service = AvahiService(name = server_settings["servicename"], + servicetype = "_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 = 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(u"se.bsnet.fukt.Mandos", - bus, do_not_queue=True) - except dbus.exceptions.NameExistsException, e: - logger.error(unicode(e) + u", disabling D-Bus") - use_dbus = False - server_settings[u"use_dbus"] = False - tcp_server.use_dbus = False - protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET - service = AvahiService(name = server_settings[u"servicename"], - servicetype = u"_mandos._tcp", - protocol = protocol, bus = bus) - if server_settings["interface"]: - service.interface = (if_nametoindex - (str(server_settings[u"interface"]))) - - if not debug: + bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus) + + clients.update(Set(Client(name = section, + 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() - - global multiprocessing_manager - multiprocessing_manager = multiprocessing.Manager() - - client_class = Client - if use_dbus: - client_class = functools.partial(ClientDBus, bus = bus) - def client_config_items(config, section): - special_settings = { - "approved_by_default": - lambda: config.getboolean(section, - "approved_by_default"), - } - for name, value in config.items(section): - try: - yield (name, special_settings[name]()) - except KeyError: - yield (name, value) - - tcp_server.clients.update(set( - client_class(name = section, - config= dict(client_config_items( - client_config, section))) - for section in client_config.sections())) - if not tcp_server.clients: - logger.warning(u"No clients defined") - + + try: + pid = os.getpid() + pidfile.write(str(pid) + "\n") + pidfile.close() + del pidfile + except IOError: + logger.error(u"Could not write to file %r with PID %d", + pidfilename, pid) + except NameError: + # "pidfile" was never created + pass + del pidfilename + + def cleanup(): + "Cleanup function; run on exit" + 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) + if not debug: - try: - with pidfile: - pid = os.getpid() - pidfile.write(str(pid) + "\n") - del pidfile - except IOError: - logger.error(u"Could not write to file %r with PID %d", - pidfilename, pid) - except NameError: - # "pidfile" was never created - pass - del pidfilename - 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()) - if use_dbus: - class MandosDBusService(dbus.service.Object): - """A D-Bus proxy object""" - def __init__(self): - dbus.service.Object.__init__(self, bus, u"/") - _interface = u"se.bsnet.fukt.Mandos" - - @dbus.service.signal(_interface, signature=u"o") - def ClientAdded(self, objpath): - "D-Bus signal" - pass - - @dbus.service.signal(_interface, signature=u"ss") - def ClientNotFound(self, fingerprint, address): - "D-Bus signal" - pass - - @dbus.service.signal(_interface, signature=u"os") - def ClientRemoved(self, objpath, name): - "D-Bus signal" - pass - - @dbus.service.method(_interface, out_signature=u"ao") - def GetAllClients(self): - "D-Bus method" - return dbus.Array(c.dbus_object_path - for c in tcp_server.clients) - - @dbus.service.method(_interface, - out_signature=u"a{oa{sv}}") - def GetAllClientsWithProperties(self): - "D-Bus method" - return dbus.Dictionary( - ((c.dbus_object_path, c.GetAll(u"")) - for c in tcp_server.clients), - signature=u"oa{sv}") - - @dbus.service.method(_interface, in_signature=u"o") - def RemoveClient(self, object_path): - "D-Bus method" - for c in tcp_server.clients: - if c.dbus_object_path == object_path: - tcp_server.clients.remove(c) - c.remove_from_connection() - # Don't signal anything except ClientRemoved - c.disable(quiet=True) - # Emit D-Bus signal - self.ClientRemoved(object_path, c.name) - return - raise KeyError(object_path) - - del _interface - - mandos_dbus_service = MandosDBusService() - - def cleanup(): - "Cleanup function; run on exit" - service.cleanup() - - while tcp_server.clients: - client = tcp_server.clients.pop() - if use_dbus: - client.remove_from_connection() - client.disable_hook = None - # Don't signal anything except ClientRemoved - client.disable(quiet=True) - if use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientRemoved(client.dbus_object_path, - client.name) - - atexit.register(cleanup) - - for client in tcp_server.clients: - if use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientAdded(client.dbus_object_path) - client.enable() + class MandosServer(dbus.service.Object): + """A D-Bus proxy object""" + def __init__(self): + dbus.service.Object.__init__(self, bus, + "/Mandos") + _interface = u"org.mandos_system.Mandos" + + @dbus.service.signal(_interface, signature="oa{sv}") + def ClientAdded(self, objpath, properties): + "D-Bus signal" + pass + + @dbus.service.signal(_interface, signature="o") + def ClientRemoved(self, objpath): + "D-Bus signal" + pass + + @dbus.service.method(_interface, out_signature="ao") + def GetAllClients(self): + return dbus.Array(c.dbus_object_path for c in clients) + + @dbus.service.method(_interface, out_signature="a{oa{sv}}") + def GetAllClientsWithProperties(self): + return dbus.Dictionary( + ((c.dbus_object_path, c.GetAllProperties()) + for c in clients), + signature="oa{sv}") + + @dbus.service.method(_interface, in_signature="o") + def RemoveClient(self, object_path): + for c in clients: + if c.dbus_object_path == object_path: + c.stop() + clients.remove(c) + return + raise KeyError + + del _interface + + mandos_server = MandosServer() + + for client in clients: + # Emit D-Bus signal + mandos_server.ClientAdded(client.dbus_object_path, + client.GetAllProperties()) + client.start() tcp_server.enable() tcp_server.server_activate() # Find out what port we got service.port = tcp_server.socket.getsockname()[1] - if use_ipv6: - logger.info(u"Now listening on address %r, port %d," - " flowinfo %d, scope_id %d" - % tcp_server.socket.getsockname()) - else: # IPv4 - logger.info(u"Now listening on address %r, port %d" - % tcp_server.socket.getsockname()) + 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: # From the Avahi example code + server.connect_to_signal("StateChanged", server_state_changed) try: - service.activate() + server_state_changed(server.GetState()) except dbus.exceptions.DBusException, error: logger.critical(u"DBusException: %s", error) - cleanup() sys.exit(1) # End of Avahi example code @@ -2032,16 +1162,11 @@ logger.debug(u"Starting main loop") main_loop.run() except AvahiError, error: - logger.critical(u"AvahiError: %s", error) - cleanup() + logger.critical(u"AvahiError: %s" + unicode(error)) sys.exit(1) except KeyboardInterrupt: if debug: - print >> sys.stderr - logger.debug(u"Server received KeyboardInterrupt") - logger.debug(u"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 2010-09-27 18:57:12 +0000 +++ mandos-clients.conf.xml 2008-10-07 21:31:09 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/clients.conf"> - + %common; ]> @@ -33,8 +33,6 @@ 2008 - 2009 - 2010 Teddy Hogeborn Björn Påhlsson @@ -64,8 +62,9 @@ >mandos 8, read by it at startup. The file needs to list all clients that should be able to use - the service. All clients listed will be regarded as enabled, - even if a client was disabled in a previous run of the server. + 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 @@ -101,53 +100,53 @@ - - - - This option is optional. - - - How long to wait for external approval before resorting to - use the value. The - default is 0s, 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. + + + 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. + + + + + + + + + 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 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. @@ -170,7 +169,7 @@ PATH will be searched. The default value for the checker command is fping %%(host)s. + >-- %(host)s. In addition to normal start time expansion, this option @@ -197,70 +196,6 @@ - - - - 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 5 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. - - - - - @@ -291,30 +226,42 @@ - - - - This option is optional. - - - The timeout is how long the server will wait (for either a - successful checker run or a client receiving its secret) - until a client is disabled and not allowed 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. + + + + 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. + + + + + + + + + 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. @@ -357,28 +304,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, - 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 @@ -421,7 +350,7 @@ [DEFAULT] timeout = 1h interval = 5m -checker = fping -q -- %%(host)s +checker = fping -q -- %(host)s # Client "foo" [foo] @@ -450,8 +379,6 @@ fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 secfile = /etc/mandos/bar-secret timeout = 15m -approved_by_default = False -approval_delay = 30s === removed file 'mandos-ctl' --- mandos-ctl 2010-09-28 18:57:31 +0000 +++ mandos-ctl 1970-01-01 00:00:00 +0000 @@ -1,337 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; coding: utf-8 -*- -# -# Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2008-2010 Teddy Hogeborn -# Copyright © 2008-2010 Björn Påhlsson -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Contact the authors at . -# - -from __future__ import division -import sys -import dbus -from optparse import OptionParser -import locale -import datetime -import re -import os - -locale.setlocale(locale.LC_ALL, u'') - -tablewords = { - 'Name': u'Name', - 'Enabled': u'Enabled', - 'Timeout': u'Timeout', - 'LastCheckedOK': u'Last Successful Check', - 'LastApprovalRequest': u'Last Approval Request', - 'Created': u'Created', - 'Interval': u'Interval', - 'Host': u'Host', - 'Fingerprint': u'Fingerprint', - 'CheckerRunning': u'Check Is Running', - 'LastEnabled': u'Last Enabled', - 'ApprovalPending': u'Approval Is Pending', - 'ApprovedByDefault': u'Approved By Default', - 'ApprovalDelay': u"Approval Delay", - 'ApprovalDuration': u"Approval Duration", - 'Checker': u'Checker', - } -defaultkeywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK') -domain = 'se.bsnet.fukt' -busname = domain + '.Mandos' -server_path = '/' -server_interface = domain + '.Mandos' -client_interface = domain + '.Mandos.Client' -version = "1.2" - -def timedelta_to_milliseconds(td): - "Convert a datetime.timedelta object to milliseconds" - return ((td.days * 24 * 60 * 60 * 1000) - + (td.seconds * 1000) - + (td.microseconds // 1000)) - -def milliseconds_to_string(ms): - td = datetime.timedelta(0, 0, 0, ms) - return (u"%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d" - % { "days": "%dT" % td.days if td.days else "", - "hours": td.seconds // 3600, - "minutes": (td.seconds % 3600) // 60, - "seconds": td.seconds % 60, - }) - - -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(u'1w') - datetime.timedelta(7) - >>> string_to_delta('5m 30s') - datetime.timedelta(0, 330) - """ - timevalue = datetime.timedelta(0) - regexp = re.compile("\d+[dsmhw]") - - for s in regexp.findall(interval): - try: - suffix = unicode(s[-1]) - value = int(s[:-1]) - if suffix == u"d": - delta = datetime.timedelta(value) - elif suffix == u"s": - delta = datetime.timedelta(0, value) - elif suffix == u"m": - delta = datetime.timedelta(0, 0, 0, 0, value) - elif suffix == u"h": - delta = datetime.timedelta(0, 0, 0, 0, 0, value) - elif suffix == u"w": - delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value) - else: - raise ValueError - except (ValueError, IndexError): - raise ValueError - timevalue += delta - return timevalue - -def print_clients(clients, keywords): - def valuetostring(value, keyword): - if type(value) is dbus.Boolean: - return u"Yes" if value else u"No" - if keyword in (u"Timeout", u"Interval", u"ApprovalDelay", - u"ApprovalDuration"): - return milliseconds_to_string(value) - return unicode(value) - - # Create format string to print table rows - format_string = u' '.join(u'%%-%ds' % - max(len(tablewords[key]), - max(len(valuetostring(client[key], - key)) - for client in - clients)) - for key in keywords) - # Print header line - print format_string % tuple(tablewords[key] for key in keywords) - for client in clients: - print format_string % tuple(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.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 = OptionParser(version = "%%prog %s" % version) - parser.add_option("-a", "--all", action="store_true", - help="Select all clients") - parser.add_option("-v", "--verbose", action="store_true", - help="Print all fields") - parser.add_option("-e", "--enable", action="store_true", - help="Enable client") - parser.add_option("-d", "--disable", action="store_true", - help="disable client") - parser.add_option("-b", "--bump-timeout", action="store_true", - help="Bump timeout for client") - parser.add_option("--start-checker", action="store_true", - help="Start checker for client") - parser.add_option("--stop-checker", action="store_true", - help="Stop checker for client") - parser.add_option("-V", "--is-enabled", action="store_true", - help="Check if client is enabled") - parser.add_option("-r", "--remove", action="store_true", - help="Remove client") - parser.add_option("-c", "--checker", type="string", - help="Set checker command for client") - parser.add_option("-t", "--timeout", type="string", - help="Set timeout for client") - parser.add_option("-i", "--interval", type="string", - help="Set checker interval for client") - parser.add_option("--approve-by-default", action="store_true", - dest=u"approved_by_default", - help="Set client to be approved by default") - parser.add_option("--deny-by-default", action="store_false", - dest=u"approved_by_default", - help="Set client to be denied by default") - parser.add_option("--approval-delay", type="string", - help="Set delay before client approve/deny") - parser.add_option("--approval-duration", type="string", - help="Set duration of one client approval") - parser.add_option("-H", "--host", type="string", - help="Set host for client") - parser.add_option("-s", "--secret", type="string", - help="Set password blob (file) for client") - parser.add_option("-A", "--approve", action="store_true", - help="Approve any current client request") - parser.add_option("-D", "--deny", action="store_true", - help="Deny any current client request") - options, client_names = parser.parse_args() - - if has_actions(options) and not client_names and not 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 or with' - ' --all.') - if options.all and not has_actions(options): - parser.error('--all requires an action.') - - try: - bus = dbus.SystemBus() - mandos_dbus_objc = bus.get_object(busname, server_path) - except dbus.exceptions.DBusException: - print >> sys.stderr, "Could not connect to Mandos server" - sys.exit(1) - - mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface = server_interface) - - #block stderr since dbus library prints to stderr - null = os.open(os.path.devnull, os.O_RDWR) - stderrcopy = os.dup(sys.stderr.fileno()) - os.dup2(null, sys.stderr.fileno()) - os.close(null) - try: - try: - mandos_clients = mandos_serv.GetAllClientsWithProperties() - finally: - #restore stderr - os.dup2(stderrcopy, sys.stderr.fileno()) - os.close(stderrcopy) - except dbus.exceptions.DBusException, e: - print >> sys.stderr, "Access denied: Accessing mandos server through dbus." - sys.exit(1) - - # Compile dict of (clients: properties) to process - clients={} - - if options.all or not client_names: - clients = dict((bus.get_object(busname, path), properties) - for path, properties in - mandos_clients.iteritems()) - else: - for name in client_names: - for path, client in mandos_clients.iteritems(): - if client['Name'] == name: - client_objc = bus.get_object(busname, path) - clients[client_objc] = client - break - else: - print >> sys.stderr, "Client not found on server: %r" % name - sys.exit(1) - - if not has_actions(options) and clients: - if options.verbose: - keywords = ('Name', 'Enabled', 'Timeout', - 'LastCheckedOK', 'Created', 'Interval', - 'Host', 'Fingerprint', 'CheckerRunning', - 'LastEnabled', 'ApprovalPending', - 'ApprovedByDefault', - 'LastApprovalRequest', 'ApprovalDelay', - 'ApprovalDuration', 'Checker') - else: - keywords = defaultkeywords - - print_clients(clients.values(), keywords) - else: - # Process each client in the list by all selected options - for client in clients: - if options.remove: - mandos_serv.RemoveClient(client.__dbus_object_path__) - if options.enable: - client.Enable(dbus_interface=client_interface) - if options.disable: - client.Disable(dbus_interface=client_interface) - if options.bump_timeout: - client.CheckedOK(dbus_interface=client_interface) - if options.start_checker: - client.StartChecker(dbus_interface=client_interface) - if options.stop_checker: - client.StopChecker(dbus_interface=client_interface) - if options.is_enabled: - sys.exit(0 if client.Get(client_interface, - u"Enabled", - dbus_interface=dbus.PROPERTIES_IFACE) - else 1) - if options.checker: - client.Set(client_interface, u"Checker", options.checker, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.host: - client.Set(client_interface, u"Host", options.host, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.interval: - client.Set(client_interface, u"Interval", - timedelta_to_milliseconds - (string_to_delta(options.interval)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_delay: - client.Set(client_interface, u"ApprovalDelay", - timedelta_to_milliseconds - (string_to_delta(options. - approval_delay)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_duration: - client.Set(client_interface, u"ApprovalDuration", - timedelta_to_milliseconds - (string_to_delta(options. - approval_duration)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.timeout: - client.Set(client_interface, u"Timeout", - timedelta_to_milliseconds - (string_to_delta(options.timeout)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.secret: - client.Set(client_interface, u"Secret", - dbus.ByteArray(open(options.secret, - u'rb').read()), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approved_by_default is not None: - client.Set(client_interface, u"ApprovedByDefault", - dbus.Boolean(options - .approved_by_default), - dbus_interface=dbus.PROPERTIES_IFACE) - 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 2010-09-25 23:52:17 +0000 +++ mandos-ctl.xml 1970-01-01 00:00:00 +0000 @@ -1,570 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8 - - - - &COMMANDNAME; - - Control the operation of the Mandos server - - - - - - &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CLIENT - - - - - &COMMANDNAME; - - - - - - - CLIENT - - - - - &COMMANDNAME; - - - - - CLIENT - - - &COMMANDNAME; - - - - - - - &COMMANDNAME; - - - - - - - - - DESCRIPTION - - &COMMANDNAME; is a program to control 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 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. - - - - - - - - - - Check if a single client is enabled or not, and exit with - a successful exit status only if the client is enabled. - - - - - - - - - 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. - - - - - - - - - - - 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 - - mandos - 8, - mandos-clients.conf - 5, - mandos-monitor - 8 - - - -
- - - - - === modified file 'mandos-keygen' --- mandos-keygen 2010-10-01 18:40:55 +0000 +++ mandos-keygen 2008-11-11 16:07:18 +0000 @@ -2,8 +2,8 @@ # # Mandos key generator - create a new OpenPGP key for a Mandos client # -# Copyright © 2008-2010 Teddy Hogeborn -# Copyright © 2008-2010 Björn Påhlsson +# Copyright © 2008 Teddy Hogeborn +# Copyright © 2008 Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ # Contact the authors at . # -VERSION="1.2" +VERSION="1.0.2" KEYDIR="/etc/keys/mandos" KEYTYPE=DSA @@ -147,7 +147,7 @@ echo "Invalid key length" >&2 exit 1 fi - + if [ -z "$KEYEXPIRE" ]; then echo "Empty key expiration" >&2 exit 1 @@ -172,7 +172,7 @@ if [ -n "$KEYEMAIL" ]; then KEYEMAILLINE="Name-Email: $KEYEMAIL" fi - + # Create temporary gpg batch file BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`" fi @@ -195,8 +195,6 @@ stty echo; \ " EXIT -set -e - umask 077 if [ "$mode" = keygen ]; then @@ -219,27 +217,12 @@ %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 - # Generate a new key in the key rings gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always \ --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 @@ -295,14 +278,13 @@ stty -echo echo -n "Enter passphrase: " >&2 first="$(head --lines=1 | tr --delete '\n')" - echo >&2 - echo -n "Repeat passphrase: " >&2 + echo -n -e "\nRepeat passphrase: " >&2 second="$(head --lines=1 | tr --delete '\n')" echo >&2 stty echo if [ "$first" != "$second" ]; then - echo "Passphrase mismatch" >&2 - touch "$RINGDIR"/mismatch + echo -e "Passphrase mismatch" >&2 + false else echo -n "$first" fi @@ -310,9 +292,9 @@ --homedir "$RINGDIR" --trust-model always --armor --encrypt \ --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \ > "$SECFILE" - if [ -e "$RINGDIR"/mismatch ]; then - rm --force "$RINGDIR"/mismatch - exit 1 + status="${PIPESTATUS[0]}" + if [ "$status" -ne 0 ]; then + exit "$status" fi cat <<-EOF === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2009-01-04 21:54:55 +0000 +++ mandos-keygen.xml 2008-10-04 01:55:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson === removed file 'mandos-monitor' --- mandos-monitor 2010-09-28 18:57:31 +0000 +++ mandos-monitor 1970-01-01 00:00:00 +0000 @@ -1,733 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; coding: utf-8 -*- -# -# Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2009,2010 Teddy Hogeborn -# Copyright © 2009,2010 Björn Påhlsson -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Contact the authors at . -# - -from __future__ import division, absolute_import, with_statement - -import sys -import os -import signal - -import datetime - -import urwid.curses_display -import urwid - -from dbus.mainloop.glib import DBusGMainLoop -import gobject - -import dbus - -import UserList - -import locale - -locale.setlocale(locale.LC_ALL, u'') - -import logging -logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL) - -# Some useful constants -domain = 'se.bsnet.fukt' -server_interface = domain + '.Mandos' -client_interface = domain + '.Mandos.Client' -version = "1.2" - -# Always run in monochrome mode -urwid.curses_display.curses.has_colors = lambda : False - -# Urwid doesn't support blinking, but we want it. Since we have no -# use for underline on its own, we make underline also always blink. -urwid.curses_display.curses.A_UNDERLINE |= ( - urwid.curses_display.curses.A_BLINK) - -def isoformat_to_datetime(iso): - "Parse an ISO 8601 date string to a datetime.datetime()" - if not iso: - return None - d, t = iso.split(u"T", 1) - year, month, day = d.split(u"-", 2) - hour, minute, second = t.split(u":", 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, *args, **kwargs): - self.proxy = proxy_object # Mandos Client proxy object - - self.properties = dict() - self.proxy.connect_to_signal(u"PropertyChanged", - self.property_changed, - client_interface, - byte_arrays=True) - - self.properties.update( - self.proxy.GetAll(client_interface, - dbus_interface = dbus.PROPERTIES_IFACE)) - - #XXX This break good super behaviour! -# super(MandosClientPropertyCache, self).__init__( -# *args, **kwargs) - - def property_changed(self, property=None, value=None): - """This is called whenever we get a PropertyChanged signal - It updates the changed property in the "properties" dict. - """ - # Update properties dict with new value - self.properties[property] = value - - -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, *args, **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 - self._update_timer_callback_lock = 0 - self.last_checker_failed = False - - # The widget shown normally - self._text_widget = urwid.Text(u"") - # The widget shown when we have focus - self._focus_text_widget = urwid.Text(u"") - super(MandosClientWidget, self).__init__( - update_hook=update_hook, delete_hook=delete_hook, - *args, **kwargs) - self.update() - self.opened = False - - last_checked_ok = isoformat_to_datetime(self.properties - [u"LastCheckedOK"]) - if last_checked_ok is None: - self.last_checker_failed = True - else: - self.last_checker_failed = ((datetime.datetime.utcnow() - - last_checked_ok) - > datetime.timedelta - (milliseconds= - self.properties - [u"Interval"])) - - if self.last_checker_failed: - self.using_timer(True) - - if self.need_approval: - self.using_timer(True) - - self.proxy.connect_to_signal(u"CheckerCompleted", - self.checker_completed, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"CheckerStarted", - self.checker_started, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"GotSecret", - self.got_secret, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"NeedApproval", - self.need_approval, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"Rejected", - self.rejected, - client_interface, - byte_arrays=True) - - def property_changed(self, property=None, value=None): - super(self, MandosClientWidget).property_changed(property, - value) - if property == u"ApprovalPending": - using_timer(bool(value)) - - def using_timer(self, flag): - """Call this method with True or False when timer should be - activated or deactivated. - """ - old = self._update_timer_callback_lock - if flag: - self._update_timer_callback_lock += 1 - else: - self._update_timer_callback_lock -= 1 - if old == 0 and self._update_timer_callback_lock: - self._update_timer_callback_tag = (gobject.timeout_add - (1000, - self.update_timer)) - elif old and self._update_timer_callback_lock == 0: - gobject.source_remove(self._update_timer_callback_tag) - self._update_timer_callback_tag = None - - def checker_completed(self, exitstatus, condition, command): - if exitstatus == 0: - if self.last_checker_failed: - self.last_checker_failed = False - self.using_timer(False) - #self.logger(u'Checker for client %s (command "%s")' - # u' was successful' - # % (self.properties[u"Name"], command)) - self.update() - return - # Checker failed - if not self.last_checker_failed: - self.last_checker_failed = True - self.using_timer(True) - if os.WIFEXITED(condition): - self.logger(u'Checker for client %s (command "%s")' - u' failed with exit code %s' - % (self.properties[u"Name"], command, - os.WEXITSTATUS(condition))) - elif os.WIFSIGNALED(condition): - self.logger(u'Checker for client %s (command "%s")' - u' was killed by signal %s' - % (self.properties[u"Name"], command, - os.WTERMSIG(condition))) - elif os.WCOREDUMP(condition): - self.logger(u'Checker for client %s (command "%s")' - u' dumped core' - % (self.properties[u"Name"], command)) - else: - self.logger(u'Checker for client %s completed' - u' mysteriously') - self.update() - - def checker_started(self, command): - #self.logger(u'Client %s started checker "%s"' - # % (self.properties[u"Name"], unicode(command))) - pass - - def got_secret(self): - self.last_checker_failed = False - self.logger(u'Client %s received its secret' - % self.properties[u"Name"]) - - def need_approval(self, timeout, default): - if not default: - message = u'Client %s needs approval within %s seconds' - else: - message = u'Client %s will get its secret in %s seconds' - self.logger(message - % (self.properties[u"Name"], timeout/1000)) - self.using_timer(True) - - def rejected(self, reason): - self.logger(u'Client %s was rejected; reason: %s' - % (self.properties[u"Name"], reason)) - - def selectable(self): - """Make this a "selectable" widget. - This overrides the method from urwid.FlowWidget.""" - return True - - def rows(self, (maxcol,), 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((maxcol,), 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 = { u"normal": u"standout", - u"bold": u"bold-standout", - u"underline-blink": - u"underline-blink-standout", - u"bold-underline-blink": - u"bold-underline-blink-standout", - } - - # Rebuild focus and non-focus widgets using current properties - - # Base part of a client. Name! - base = (u'%(name)s: ' - % {u"name": self.properties[u"Name"]}) - if not self.properties[u"Enabled"]: - message = u"DISABLED" - elif self.properties[u"ApprovalPending"]: - timeout = datetime.timedelta(milliseconds - = self.properties - [u"ApprovalDelay"]) - last_approval_request = isoformat_to_datetime( - self.properties[u"LastApprovalRequest"]) - if last_approval_request is not None: - timer = timeout - (datetime.datetime.utcnow() - - last_approval_request) - else: - timer = datetime.timedelta() - if self.properties[u"ApprovedByDefault"]: - message = u"Approval in %s. (d)eny?" - else: - message = u"Denial in %s. (a)pprove?" - message = message % unicode(timer).rsplit(".", 1)[0] - elif self.last_checker_failed: - timeout = datetime.timedelta(milliseconds - = self.properties - [u"Timeout"]) - last_ok = isoformat_to_datetime( - max((self.properties[u"LastCheckedOK"] - or self.properties[u"Created"]), - self.properties[u"LastEnabled"])) - timer = timeout - (datetime.datetime.utcnow() - last_ok) - message = (u'A checker has failed! Time until client' - u' gets disabled: %s' - % unicode(timer).rsplit(".", 1)[0]) - else: - message = u"enabled" - self._text = "%s%s" % (base, message) - - if not urwid.supports_unicode(): - self._text = self._text.encode("ascii", "replace") - textlist = [(u"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 gobject" - self.update() - return True # Keep calling this - - def delete(self): - if self._update_timer_callback_tag is not None: - gobject.source_remove(self._update_timer_callback_tag) - self._update_timer_callback_tag = None - if self.delete_hook is not None: - self.delete_hook(self) - - def render(self, (maxcol,), focus=False): - """Render differently if we have focus. - This overrides the method from urwid.FlowWidget""" - return self.current_widget(focus).render((maxcol,), - focus=focus) - - def keypress(self, (maxcol,), key): - """Handle keys. - This overrides the method from urwid.FlowWidget""" - if key == u"+": - self.proxy.Enable(dbus_interface = client_interface) - elif key == u"-": - self.proxy.Disable(dbus_interface = client_interface) - elif key == u"a": - self.proxy.Approve(dbus.Boolean(True, variant_level=1), - dbus_interface = client_interface) - elif key == u"d": - self.proxy.Approve(dbus.Boolean(False, variant_level=1), - dbus_interface = client_interface) - elif key == u"R" or key == u"_" or key == u"ctrl k": - self.server_proxy_object.RemoveClient(self.proxy - .object_path) - elif key == u"s": - self.proxy.StartChecker(dbus_interface = client_interface) - elif key == u"S": - self.proxy.StopChecker(dbus_interface = client_interface) - elif key == u"C": - self.proxy.CheckedOK(dbus_interface = client_interface) - # xxx -# elif key == u"p" or key == "=": -# self.proxy.pause() -# elif key == u"u" or key == ":": -# self.proxy.unpause() -# elif key == u"RET": -# self.open() - else: - return key - - def property_changed(self, property=None, value=None, - *args, **kwargs): - """Call self.update() if old value is not new value. - This overrides the method from MandosClientPropertyCache""" - property_name = unicode(property) - old_value = self.properties.get(property_name) - super(MandosClientWidget, self).property_changed( - property=property, value=value, *args, **kwargs) - if self.properties.get(property_name) != old_value: - 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, (maxcol, maxrow), key): - ret = super(ConstrainedListBox, self).keypress((maxcol, - maxrow), key) - if ret in (u"up", u"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): - DBusGMainLoop(set_as_default=True) - - self.screen = urwid.curses_display.Screen() - - self.screen.register_palette(( - (u"normal", - u"default", u"default", None), - (u"bold", - u"default", u"default", u"bold"), - (u"underline-blink", - u"default", u"default", u"underline"), - (u"standout", - u"default", u"default", u"standout"), - (u"bold-underline-blink", - u"default", u"default", (u"bold", u"underline")), - (u"bold-standout", - u"default", u"default", (u"bold", u"standout")), - (u"underline-blink-standout", - u"default", u"default", (u"underline", u"standout")), - (u"bold-underline-blink-standout", - u"default", u"default", (u"bold", u"underline", - u"standout")), - )) - - if urwid.supports_unicode(): - self.divider = u"─" # \u2500 - #self.divider = u"━" # \u2501 - else: - #self.divider = u"-" # \u002d - self.divider = u"_" # \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 - - # 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 = u"any" - - self.rebuild() - self.log_message_raw((u"bold", - u"Mandos Monitor version " + version)) - self.log_message_raw((u"bold", - u"q: Quit ?: Help")) - - self.busname = domain + '.Mandos' - self.main_loop = gobject.MainLoop() - self.bus = dbus.SystemBus() - mandos_dbus_objc = self.bus.get_object( - self.busname, u"/", follow_name_owner_changes=True) - self.mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface - = server_interface) - try: - mandos_clients = (self.mandos_serv - .GetAllClientsWithProperties()) - except dbus.exceptions.DBusException: - mandos_clients = dbus.Dictionary() - - (self.mandos_serv - .connect_to_signal(u"ClientRemoved", - self.find_and_remove_client, - dbus_interface=server_interface, - byte_arrays=True)) - (self.mandos_serv - .connect_to_signal(u"ClientAdded", - self.add_new_client, - dbus_interface=server_interface, - byte_arrays=True)) - (self.mandos_serv - .connect_to_signal(u"ClientNotFound", - self.client_not_found, - dbus_interface=server_interface, - byte_arrays=True)) - for path, client in mandos_clients.iteritems(): - 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) - - def client_not_found(self, fingerprint, address): - self.log_message((u"Client with address %s and fingerprint %s" - u" could not be found" % (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) - pass - self.topwidget = urwid.Pile(self.uilist) - - def log_message(self, message): - timestamp = datetime.datetime.now().isoformat() - self.log_message_raw(timestamp + u": " + message) - - def log_message_raw(self, markup): - """Add a log message to the log buffer.""" - self.log.append(urwid.Text(markup, wrap=self.log_wrap)) - if (self.max_log_length - and len(self.log) > self.max_log_length): - del self.log[0:len(self.log)-self.max_log_length-1] - self.logbox.set_focus(len(self.logbox.body.contents), - coming_from=u"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(u"Log visibility changed to: " - # + unicode(self.log_visible)) - - def change_log_display(self): - """Change type of log display. - Currently, this toggles wrapping of text lines.""" - if self.log_wrap == u"clip": - self.log_wrap = u"any" - else: - self.log_wrap = u"clip" - for textwidget in self.log: - textwidget.set_wrap_mode(self.log_wrap) - #self.log_message(u"Wrap mode: " + self.log_wrap) - - def find_and_remove_client(self, path, name): - """Find an client from its object path and remove it. - - This is connected to the ClientRemoved signal from the - Mandos server object.""" - try: - client = self.clients_dict[path] - except KeyError: - # not found? - return - self.remove_client(client, path) - - def add_new_client(self, path): - client_proxy_object = self.bus.get_object(self.busname, path) - self.add_client(MandosClientWidget(server_proxy_object - =self.mandos_serv, - proxy_object - =client_proxy_object, - update_hook - =self.refresh, - delete_hook - =self.remove_client, - logger - =self.log_message), - 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(None, lambda c: c.properties[u"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] - if not self.clients_dict: - # Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker - # is completely emptied, we need to recreate it. - self.clients = urwid.SimpleListWalker([]) - self.rebuild() - 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.refresh() - self._input_callback_tag = (gobject.io_add_watch - (sys.stdin.fileno(), - gobject.IO_IN, - self.process_input)) - self.main_loop.run() - # Main loop has finished, we should close everything now - gobject.source_remove(self._input_callback_tag) - self.screen.stop() - - def stop(self): - self.main_loop.quit() - - def process_input(self, source, condition): - keys = self.screen.get_input() - translations = { u"ctrl n": u"down", # Emacs - u"ctrl p": u"up", # Emacs - u"ctrl v": u"page down", # Emacs - u"meta v": u"page up", # Emacs - u" ": u"page down", # less - u"f": u"page down", # less - u"b": u"page up", # less - u"j": u"down", # vi - u"k": u"up", # vi - } - for key in keys: - try: - key = translations[key] - except KeyError: # :-) - pass - - if key == u"q" or key == u"Q": - self.stop() - break - elif key == u"window resize": - self.size = self.screen.get_cols_rows() - self.refresh() - elif key == u"\f": # Ctrl-L - self.refresh() - elif key == u"l" or key == u"D": - self.toggle_log_display() - self.refresh() - elif key == u"w" or key == u"i": - self.change_log_display() - self.refresh() - elif key == u"?" or key == u"f1" or key == u"esc": - if not self.log_visible: - self.log_visible = True - self.rebuild() - self.log_message_raw((u"bold", - u" ". - join((u"q: Quit", - u"?: Help", - u"l: Log window toggle", - u"TAB: Switch window", - u"w: Wrap (log)")))) - self.log_message_raw((u"bold", - u" " - .join((u"Clients:", - u"+: Enable", - u"-: Disable", - u"R: Remove", - u"s: Start new checker", - u"S: Stop checker", - u"C: Checker OK", - u"a: Approve", - u"d: Deny")))) - self.refresh() - elif key == u"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 == u"end" or key == u"meta >" or key == u"G" - # or key == u">"): - # pass # xxx end-of-buffer - #elif (key == u"home" or key == u"meta <" or key == u"g" - # or key == u"<"): - # pass # xxx beginning-of-buffer - #elif key == u"ctrl e" or key == u"$": - # pass # xxx move-end-of-line - #elif key == u"ctrl a" or key == u"^": - # pass # xxx move-beginning-of-line - #elif key == u"ctrl b" or key == u"meta (" or key == u"h": - # pass # xxx left - #elif key == u"ctrl f" or key == u"meta )" or key == u"l": - # pass # xxx right - #elif key == u"a": - # pass # scroll up log - #elif key == u"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, e: - ui.log_message(unicode(e)) - ui.screen.stop() - raise === removed file 'mandos-monitor.xml' --- mandos-monitor.xml 2010-09-30 06:24:20 +0000 +++ mandos-monitor.xml 1970-01-01 00:00:00 +0000 @@ -1,233 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - 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 - - - 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 service name of - 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 - - mandos - 8, - mandos-ctl - 8 - - - -
- - - - - === modified file 'mandos-options.xml' --- mandos-options.xml 2009-02-13 05:38:21 +0000 +++ mandos-options.xml 2008-09-30 03:19:39 +0000 @@ -26,12 +26,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.
@@ -68,22 +66,4 @@ 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. - - === modified file 'mandos.conf' --- mandos.conf 2009-02-13 05:38:21 +0000 +++ mandos.conf 2008-08-18 23:55:28 +0000 @@ -36,9 +36,3 @@ # 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 === modified file 'mandos.conf.xml' --- mandos.conf.xml 2009-02-25 01:14:29 +0000 +++ mandos.conf.xml 2008-09-30 07:23:39 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/mandos.conf"> - + %common; ]> @@ -33,7 +33,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -131,28 +130,6 @@ - - - - - - - - - - - - - - @@ -190,13 +167,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 servicename = Daena -use_dbus = False -use_ipv6 = True @@ -237,7 +212,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. === modified file 'mandos.lsm' --- mandos.lsm 2010-09-28 18:57:31 +0000 +++ mandos.lsm 2008-12-10 01:26:02 +0000 @@ -1,7 +1,7 @@ Begin4 Title: Mandos -Version: 1.2 -Entered-date: 2010-09-28 +Version: 1.0.2 +Entered-date: 2008-10-28 Description: The Mandos system allows computers to have encrypted root file systems and at the same time be capable of remote and/or unattended reboots. @@ -12,10 +12,10 @@ Maintained-by: teddy@fukt.bsnet.se (Teddy Hogeborn), belorn@fukt.bsnet.se (Björn Påhlsson) Primary-site: http://www.fukt.bsnet.se/mandos - 132K mandos_1.2.orig.tar.gz + 89K mandos_1.0.2.orig.tar.gz Alternate-site: ftp://ftp.fukt.bsnet.se/pub/mandos - 132K mandos_1.2.orig.tar.gz -Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.5, and + 89K mandos_1.0.2.orig.tar.gz +Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.4, 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 === modified file 'mandos.xml' --- mandos.xml 2010-09-27 17:39:03 +0000 +++ mandos.xml 2008-10-04 01:55:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,8 +32,6 @@ 2008 - 2009 - 2010 Teddy Hogeborn Björn Påhlsson @@ -86,13 +84,6 @@ DIRECTORY - - - - - - &COMMANDNAME; @@ -198,24 +189,6 @@ - - - - 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. - - - - - @@ -255,23 +228,6 @@ - - - - - - - See also . - - - - - - - - - - @@ -349,35 +305,12 @@ 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, + 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. A client successfully - receiving its password will also be treated as a successful - checker run. - - - - - 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. - - + 5. + @@ -390,16 +323,6 @@ - - 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 @@ -460,8 +383,8 @@ /var/run/mandos.pid - The file containing the process id of the - &COMMANDNAME; process started last. + The file containing the process id of + &COMMANDNAME;. @@ -495,9 +418,15 @@ backtrace. This could be considered a feature. - Currently, if a client is disabled due to having timed out, the - server does not record this fact onto permanent storage. This - has some security implications, see . + 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. @@ -506,7 +435,7 @@ Debug mode is conflated with running in the foreground. - The console log messages do not show a time stamp. + The console log messages does not show a time stamp. This server does not check the expire time of clients’ OpenPGP @@ -585,18 +514,19 @@ If a client is compromised, its downtime should be duly noted - by the server which would therefore disable the client. 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 enabled, 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. + 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 === modified file 'plugin-runner.c' --- plugin-runner.c 2010-09-26 21:27:28 +0000 +++ plugin-runner.c 2008-11-11 16:07:18 +0000 @@ -1,9 +1,9 @@ -/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */ +/* -*- coding: utf-8 -*- */ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,14 +23,14 @@ */ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(), - asprintf(), O_CLOEXEC */ + 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(), +#include /* perror, fileno(), fprintf(), stderr, STDOUT_FILENO */ -#include /* DIR, fdopendir(), stat(), struct +#include /* DIR, opendir(), stat(), struct stat, waitpid(), WIFEXITED(), WEXITSTATUS(), wait(), pid_t, uid_t, gid_t, getuid(), getgid(), @@ -38,11 +38,10 @@ #include /* fd_set, select(), FD_ZERO(), FD_SET(), FD_ISSET(), FD_CLR */ #include /* wait(), waitpid(), WIFEXITED(), - WEXITSTATUS(), WTERMSIG(), - WCOREDUMP() */ + WEXITSTATUS() */ #include /* struct stat, stat(), S_ISREG() */ #include /* and, or, not */ -#include /* DIR, struct dirent, fdopendir(), +#include /* DIR, struct dirent, opendir(), readdir(), closedir(), dirfd() */ #include /* struct stat, stat(), S_ISREG(), fcntl(), setuid(), setgid(), @@ -53,8 +52,7 @@ close() */ #include /* fcntl(), F_GETFD, F_SETFD, FD_CLOEXEC */ -#include /* strsep, strlen(), asprintf(), - strsignal(), strcmp(), strncmp() */ +#include /* strsep, strlen(), asprintf() */ #include /* errno */ #include /* struct argp_option, struct argp_state, struct argp, @@ -64,14 +62,8 @@ #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() */ #define BUFFER_SIZE 256 @@ -88,7 +80,7 @@ char **environ; int envc; bool disabled; - + /* Variables used for running processes*/ pid_t pid; int fd; @@ -96,8 +88,8 @@ size_t buffer_size; size_t buffer_length; bool eof; - volatile sig_atomic_t completed; - int status; + volatile bool completed; + volatile int status; struct plugin *next; } plugin; @@ -106,61 +98,45 @@ /* Gets an existing plugin based on name, or if none is found, creates a new one */ 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))){ + /* Check for exiting 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))){ 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, + .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; @@ -174,19 +150,14 @@ static bool add_to_char_array(const char *new, char ***array, int *len){ /* Resize the pointed-to array to hold one more pointer */ - do { - *array = realloc(*array, sizeof(char *) - * (size_t) ((*len) + 2)); - } while(*array == NULL and errno == EINTR); + *array = realloc(*array, sizeof(char *) + * (size_t) ((*len) + 2)); /* Malloc check */ if(*array == NULL){ return false; } /* 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; } @@ -218,10 +189,7 @@ if(strncmp(*e, def, namelen + 1) == 0){ /* It already exists */ if(replace){ - char *new; - do { - new = realloc(*e, strlen(def) + 1); - } while(new == NULL and errno == EINTR); + char *new = realloc(*e, strlen(def) + 1); if(new == NULL){ return false; } @@ -237,24 +205,22 @@ /* * 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. */ static int set_cloexec_flag(int fd){ - int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0)); + 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); } /* Mark processes as completed when they exit, and save their exit status. */ static void handle_sigchld(__attribute__((unused)) int sig){ - int old_errno = errno; while(true){ plugin *proc = plugin_list; int status; @@ -264,11 +230,11 @@ 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 */ @@ -280,9 +246,8 @@ continue; } proc->status = status; - proc->completed = 1; + proc->completed = true; } - errno = old_errno; } /* Prints out a password to stdout */ @@ -310,7 +275,7 @@ } free(plugin_node->environ); free(plugin_node->buffer); - + /* Removes the plugin from the singly-linked list */ if(plugin_node == plugin_list){ /* First one - simple */ @@ -344,7 +309,6 @@ 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; @@ -359,14 +323,14 @@ 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; } @@ -404,161 +368,130 @@ .doc = "Group ID the plugins will run as", .group = 3 }, { .name = "debug", .key = 132, .doc = "Debug mode", .group = 4 }, - /* - * 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 } }; - error_t parse_opt(int key, char *arg, struct argp_state *state){ - errno = 0; - switch(key){ - char *tmp; - intmax_t tmpmax; + error_t parse_opt (int key, char *arg, __attribute__((unused)) + struct argp_state *state) { + switch (key) { case 'g': /* --global-options */ - { - char *plugin_option; - while((plugin_option = strsep(&arg, ",")) != NULL){ - if(not add_argument(getplugin(NULL), plugin_option)){ - break; + if (arg != NULL){ + char *p; + while((p = strsep(&arg, ",")) != NULL){ + if(p[0] == '\0'){ + continue; + } + if(not add_argument(getplugin(NULL), p)){ + perror("add_argument"); + return ARGP_ERR_UNKNOWN; } } } break; case 'G': /* --global-env */ - add_environment(getplugin(NULL), arg, true); + if(arg == NULL){ + break; + } + if(not add_environment(getplugin(NULL), arg, true)){ + perror("add_environment"); + } 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; + if (arg != NULL){ + char *p_name = strsep(&arg, ":"); + if(p_name[0] == '\0' or arg == NULL){ + break; + } + char *opt = strsep(&arg, ":"); + if(opt[0] == '\0' or opt == NULL){ + break; + } + char *p; + while((p = strsep(&opt, ",")) != NULL){ + if(p[0] == '\0'){ + continue; + } + if(not add_argument(getplugin(p_name), p)){ + perror("add_argument"); + return ARGP_ERR_UNKNOWN; } } } break; case 'E': /* --env-for */ + if(arg == NULL){ + break; + } { char *envdef = strchr(arg, ':'); if(envdef == NULL){ - argp_error(state, "No colon in \"%s\"", arg); - errno = EINVAL; break; } *envdef = '\0'; - envdef++; - if(arg[0] == '\0'){ - argp_error(state, "Empty plugin name"); - errno = EINVAL; - break; + if(not add_environment(getplugin(arg), envdef+1, true)){ + perror("add_environment"); } - add_environment(getplugin(arg), envdef, true); } break; case 'd': /* --disable */ - { + if (arg != NULL){ plugin *p = getplugin(arg); - if(p != NULL){ - p->disabled = true; + if(p == NULL){ + return ARGP_ERR_UNKNOWN; } + p->disabled = true; } break; case 'e': /* --enable */ - { + if (arg != NULL){ plugin *p = getplugin(arg); - if(p != NULL){ - p->disabled = false; + if(p == NULL){ + return ARGP_ERR_UNKNOWN; } + p->disabled = false; } break; case 128: /* --plugin-dir */ free(plugindir); plugindir = strdup(arg); + if(plugindir == NULL){ + perror("strdup"); + } break; case 129: /* --config-file */ /* This is already done by parse_opt_config_file() */ break; case 130: /* --userid */ - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (uid_t)tmpmax){ - argp_error(state, "Bad user ID number: \"%s\", using %" - PRIdMAX, arg, (intmax_t)uid); - break; - } - uid = (uid_t)tmpmax; + uid = (uid_t)strtol(arg, NULL, 10); break; case 131: /* --groupid */ - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (gid_t)tmpmax){ - argp_error(state, "Bad group ID number: \"%s\", using %" - PRIdMAX, arg, (intmax_t)gid); - break; - } - gid = (gid_t)tmpmax; + gid = (gid_t)strtol(arg, NULL, 10); break; case 132: /* --debug */ debug = true; 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; + if(arg[0] != '\0'){ + fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); } + break; + case ARGP_KEY_END: + break; default: return ARGP_ERR_UNKNOWN; } - return errno; /* Set to 0 at start */ + return 0; } /* 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){ + error_t parse_opt_config_file (int key, char *arg, + __attribute__((unused)) + struct argp_state *state) { + switch (key) { case 'g': /* --global-options */ case 'G': /* --global-env */ case 'o': /* --options-for */ @@ -570,19 +503,20 @@ case 129: /* --config-file */ free(argfile); argfile = strdup(arg); - break; + if(argfile == NULL){ + perror("strdup"); + } + break; case 130: /* --userid */ case 131: /* --groupid */ case 132: /* --debug */ - case '?': /* --help */ - case -3: /* --usage */ - case 'V': /* --version */ case ARGP_KEY_ARG: + case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } - return errno; + return 0; } struct argp argp = { .options = options, @@ -590,22 +524,12 @@ .args_doc = "", .doc = "Mandos plugin runner -- Run plugins" }; - /* Parse using parse_opt_config_file() in order to get the custom + /* Parse using the 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, ARGP_IN_ORDER, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; goto fallback; } @@ -613,28 +537,29 @@ 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){ @@ -642,7 +567,7 @@ if(sret == -1){ break; } - + line = org_line; arg = strsep(&line, comment_delim); while((p = strsep(&arg, whitespace_delims)) != NULL){ @@ -651,8 +576,8 @@ } 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; } @@ -661,70 +586,43 @@ custom_argv = realloc(custom_argv, sizeof(char *) * ((unsigned int) custom_argc + 1)); if(custom_argv == NULL){ - error(0, errno, "realloc"); - exitstatus = EX_OSERR; + 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 { /* 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 there was any arguments from configuration file, + pass them to parser as command 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; + ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER, + 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; 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; + ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; goto fallback; } @@ -735,7 +633,7 @@ 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); } @@ -743,58 +641,37 @@ } /* Strip permissions down to nobody */ + ret = setuid(uid); + if (ret == -1){ + perror("setuid"); + } 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("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 */ { - int dir_fd = -1; - if(plugindir == NULL){ - dir_fd = open(PDIR, O_RDONLY | -#ifdef O_CLOEXEC - O_CLOEXEC -#else /* not O_CLOEXEC */ - 0 -#endif /* not O_CLOEXEC */ - ); - } else { - dir_fd = open(plugindir, O_RDONLY | -#ifdef O_CLOEXEC - O_CLOEXEC -#else /* not O_CLOEXEC */ - 0 -#endif /* not O_CLOEXEC */ - ); - } - if(dir_fd == -1){ - error(0, errno, "Could not open plugin dir"); - exitstatus = EX_UNAVAILABLE; - 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"); - TEMP_FAILURE_RETRY(close(dir_fd)); - exitstatus = EX_OSERR; - goto fallback; - } -#endif /* O_CLOEXEC */ - - dir = fdopendir(dir_fd); - if(dir == NULL){ - error(0, errno, "Could not open plugin dir"); - TEMP_FAILURE_RETRY(close(dir_fd)); - exitstatus = EX_OSERR; - goto fallback; + 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; + } } } @@ -802,15 +679,13 @@ /* Read and execute any executable in the plugin directory*/ while(true){ - do { - dirst = readdir(dir); - } while(dirst == NULL and errno == EINTR); + dirst = readdir(dir); /* All directory entries have been processed */ if(dirst == NULL){ - if(errno == EBADF){ - error(0, errno, "readdir"); - exitstatus = EX_IOERR; + if (errno == EBADF){ + perror("readdir"); + exitstatus = EXIT_FAILURE; goto fallback; } break; @@ -846,7 +721,7 @@ 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) + and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf) == 0)){ if(debug){ fprintf(stderr, "Ignoring plugin dir entry \"%s\"" @@ -861,31 +736,27 @@ continue; } } - + char *filename; if(plugindir == NULL){ - ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s", - dirst->d_name)); + ret = asprintf(&filename, PDIR "/%s", dirst->d_name); } else { - ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s", - plugindir, - dirst->d_name)); + ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name); } if(ret < 0){ - error(0, errno, "asprintf"); + perror("asprintf"); continue; } - ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st)); - if(ret == -1){ - error(0, errno, "stat"); + ret = stat(filename, &st); + if (ret == -1){ + perror("stat"); free(filename); continue; } - + /* Ignore non-executable files */ - if(not S_ISREG(st.st_mode) - or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){ + if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){ if(debug){ fprintf(stderr, "Ignoring plugin dir entry \"%s\"" " with bad type or mode\n", filename); @@ -896,7 +767,7 @@ plugin *p = getplugin(dirst->d_name); if(p == NULL){ - error(0, errno, "getplugin"); + perror("getplugin"); free(filename); continue; } @@ -914,13 +785,13 @@ 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"); + perror("add_environment"); } } } @@ -931,67 +802,62 @@ if(p->environ[0] != NULL){ for(char **e = environ; *e != NULL; e++){ if(not add_environment(p, *e, false)){ - error(0, errno, "add_environment"); + perror("add_environment"); } } } int pipefd[2]; - ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd)); - if(ret == -1){ - error(0, errno, "pipe"); - exitstatus = EX_OSERR; + ret = pipe(pipefd); + if (ret == -1){ + perror("pipe"); + exitstatus = EXIT_FAILURE; goto fallback; } /* Ask OS to automatic close the pipe on exec */ ret = set_cloexec_flag(pipefd[0]); if(ret < 0){ - error(0, errno, "set_cloexec_flag"); - exitstatus = EX_OSERR; + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; goto fallback; } ret = set_cloexec_flag(pipefd[1]); if(ret < 0){ - error(0, errno, "set_cloexec_flag"); - exitstatus = EX_OSERR; + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; goto fallback; } /* 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; + 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); + pid_t pid = fork(); if(pid == -1){ - error(0, errno, "fork"); - exitstatus = EX_OSERR; + 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); 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(dirfd(dir) < 0){ @@ -1001,31 +867,28 @@ } if(p->environ[0] == NULL){ if(execv(filename, p->argv) < 0){ - error(0, errno, "execv for %s", filename); - _exit(EX_OSERR); + perror("execv"); + _exit(EXIT_FAILURE); } } else { if(execve(filename, p->argv, p->environ) < 0){ - error(0, errno, "execve for %s", filename); - _exit(EX_OSERR); + perror("execve"); + _exit(EXIT_FAILURE); } } /* no return */ } /* Parent process */ - TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of - pipe */ + close(pipefd[1]); /* Close unused write end of pipe */ free(filename); plugin *new_plugin = getplugin(dirst->d_name); - if(new_plugin == NULL){ - error(0, errno, "getplugin"); - ret = (int)(TEMP_FAILURE_RETRY - (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, - NULL))); + if (new_plugin == NULL){ + perror("getplugin"); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); + perror("sigprocmask"); } - exitstatus = EX_OSERR; + exitstatus = EXIT_FAILURE; goto fallback; } @@ -1034,26 +897,22 @@ /* 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); /* Spurious warning from - -Wconversion */ + FD_SET(new_plugin->fd, &rfds_all); - if(maxfd < new_plugin->fd){ + if (maxfd < new_plugin->fd){ maxfd = new_plugin->fd; } } - TEMP_FAILURE_RETRY(closedir(dir)); + closedir(dir); dir = NULL; - free_plugin(getplugin(NULL)); for(plugin *p = plugin_list; p != NULL; p = p->next){ if(p->pid != 0){ @@ -1070,51 +929,44 @@ while(plugin_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;){ /* 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 %s [%" PRIdMAX "] dumped" - " core\n", proc->name, (intmax_t) (proc->pid)); + fprintf(stderr, "Plugin %d dumped core\n", + (unsigned int) (proc->pid)); } } /* Remove the plugin */ - FD_CLR(proc->fd, &rfds_all); /* Spurious warning from - -Wconversion */ - + 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; } @@ -1123,12 +975,11 @@ proc = next_plugin; /* 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; + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; goto fallback; } @@ -1144,16 +995,14 @@ 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)){ /* Spurious - warning from - -Wconversion */ + if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* This process had nothing to say at this time */ proc = proc->next; continue; @@ -1162,37 +1011,34 @@ if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){ proc->buffer = realloc(proc->buffer, proc->buffer_size + (size_t) BUFFER_SIZE); - if(proc->buffer == NULL){ - error(0, errno, "malloc"); - exitstatus = EX_OSERR; + if (proc->buffer == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; goto fallback; } 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(plugin_list == NULL or exitstatus != EXIT_SUCCESS){ /* Fallback if all plugins failed, none are found or an error occured */ bool bret; @@ -1206,16 +1052,16 @@ } bret = print_out_password(passwordbuffer, len); 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){ @@ -1236,17 +1082,17 @@ ret = kill(p->pid, SIGTERM); if(ret == -1 and errno != ESRCH){ /* Set-uid proccesses might not get closed */ - error(0, errno, "kill"); + perror("kill"); } } } /* 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(); === modified file 'plugin-runner.conf' --- plugin-runner.conf 2009-04-17 08:26:17 +0000 +++ plugin-runner.conf 2008-10-05 17:38:31 +0000 @@ -1,10 +1,11 @@ -## 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. +## This is the configuration file for plugin-runner. It should be +## installed as "/etc/mandos/plugin-runner.conf", which 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 +## +## The default network interface for mandos-client(8mandos) is +## "eth0". Uncomment this line and change it if necessary. +## +#--options-for=mandos-client:--interface=eth0 === modified file 'plugin-runner.xml' --- plugin-runner.xml 2009-01-18 06:41:57 +0000 +++ plugin-runner.xml 2008-09-30 07:23:39 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -579,7 +578,7 @@ -cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=seckey.txt +&COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=/etc/keys/mandos/pubkey.txt,--seckey=/etc/keys/mandos/seckey.txt === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2010-09-26 18:32:58 +0000 +++ plugins.d/askpass-fifo.c 2008-11-11 16:07:18 +0000 @@ -1,9 +1,9 @@ /* -*- coding: utf-8 -*- */ /* - * Askpass-FIFO - Read a password from a FIFO and output it + * Passprompt - Read a password from a FIFO and output it * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 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 @@ -19,26 +19,21 @@ * along with this program. If not, see * . * - * Contact the authors at . + * Contact the authors at and + * . */ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ #include /* 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 /* EXIT_FAILURE, NULL, size_t, free(), +#include /* errno, EEXIST */ +#include /* perror() */ +#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 */ int main(__attribute__((unused))int argc, @@ -48,47 +43,17 @@ /* Create FIFO */ const char passfifo[] = "/lib/cryptsetup/passfifo"; - ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); - if(ret == -1){ - int e = errno; - error(0, errno, "mkfifo"); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - return EX_OSFILE; - case ENAMETOOLONG: - case ENOSPC: - case EROFS: - default: - return EX_OSERR; - case ENOENT: - return EX_UNAVAILABLE; /* no "/lib/cryptsetup"? */ - case EEXIST: - break; /* not an error */ - } + ret = TEMP_FAILURE_RETRY(mkfifo(passfifo, S_IRUSR | S_IWUSR)); + if(ret == -1 and errno != EEXIST){ + perror("mkfifo"); + return EXIT_FAILURE; } /* Open FIFO */ - int fifo_fd = open(passfifo, O_RDONLY); + int fifo_fd = TEMP_FAILURE_RETRY(open(passfifo, O_RDONLY)); if(fifo_fd == -1){ - int e = errno; - error(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; - } + perror("open"); + return EXIT_FAILURE; } /* Read from FIFO */ @@ -97,78 +62,44 @@ { size_t buf_allocated = 0; const size_t blocksize = 1024; - do { + do{ if(buf_len + blocksize > buf_allocated){ char *tmp = realloc(buf, buf_allocated + blocksize); if(tmp == NULL){ - error(0, errno, "realloc"); + perror("realloc"); free(buf); - return EX_OSERR; + return EXIT_FAILURE; } buf = tmp; buf_allocated += blocksize; } - sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len); + sret = TEMP_FAILURE_RETRY(read(fifo_fd, buf + buf_len, + buf_allocated - buf_len)); if(sret == -1){ - int e = errno; + perror("read"); free(buf); - errno = e; - error(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; - } + return EXIT_FAILURE; } buf_len += (size_t)sret; - } while(sret != 0); + }while(sret != 0); } /* Close FIFO */ - close(fifo_fd); + TEMP_FAILURE_RETRY(close(fifo_fd)); /* Print password to stdout */ size_t written = 0; while(written < buf_len){ - sret = write(STDOUT_FILENO, buf + written, buf_len - written); + sret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf + written, + buf_len - written)); if(sret == -1){ - int e = errno; + perror("write"); free(buf); - errno = e; - error(0, errno, "write"); - switch(e){ - case EBADF: - case EFAULT: - case EINVAL: - return EX_OSFILE; - case EFBIG: - case EIO: - case ENOSPC: - default: - return EX_IOERR; - } + return EXIT_FAILURE; } written += (size_t)sret; } free(buf); - ret = close(STDOUT_FILENO); - if(ret == -1){ - int e = errno; - error(0, errno, "close"); - switch(e){ - case EBADF: - return EX_OSFILE; - case EIO: - default: - return EX_IOERR; - } - } return EXIT_SUCCESS; } === modified file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/askpass-fifo.xml 2008-10-04 20:09:53 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2010-09-26 21:27:28 +0000 +++ plugins.d/mandos-client.c 2008-11-11 16:07:18 +0000 @@ -1,6 +1,6 @@ /* -*- coding: utf-8 -*- */ /* - * Mandos-client - get and decrypt data from a Mandos server + * Mandos client - get and decrypt data from a Mandos server * * This program is partly derived from an example program for an Avahi * service browser, downloaded from @@ -9,8 +9,8 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 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 @@ -30,64 +30,48 @@ */ /* Needed by GPGME, specifically gpgme_data_seek() */ -#ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE -#endif -#ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 -#endif #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ #include /* fprintf(), stderr, fwrite(), - stdout, ferror(), remove() */ + stdout, ferror() */ #include /* uint16_t, uint32_t */ #include /* NULL, size_t, ssize_t */ -#include /* free(), EXIT_SUCCESS, srand(), - strtof(), abort() */ -#include /* bool, false, true */ +#include /* free(), EXIT_SUCCESS, EXIT_FAILURE, + srand() */ +#include /* bool, true */ #include /* memset(), strcmp(), strlen(), strerror(), asprintf(), strcpy() */ -#include /* ioctl */ +#include /* ioctl */ #include /* socket(), inet_pton(), sockaddr, sockaddr_in6, PF_INET6, - SOCK_STREAM, uid_t, gid_t, open(), - opendir(), DIR */ + SOCK_STREAM, INET6_ADDRSTRLEN, + uid_t, gid_t, open(), opendir(), DIR */ #include /* open() */ #include /* socket(), struct sockaddr_in6, - inet_pton(), connect() */ + struct in6_addr, inet_pton(), + connect() */ #include /* open() */ -#include /* opendir(), struct dirent, readdir() - */ -#include /* PRIu16, PRIdMAX, intmax_t, - strtoimax() */ +#include /* opendir(), struct dirent, readdir() */ +#include /* PRIu16 */ #include /* assert() */ #include /* perror(), errno */ -#include /* nanosleep(), time(), sleep() */ +#include /* time() */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS, if_indextoname(), if_nametoindex(), IF_NAMESIZE */ -#include /* IN6_IS_ADDR_LINKLOCAL, - INET_ADDRSTRLEN, INET6_ADDRSTRLEN - */ +#include #include /* close(), SEEK_SET, off_t, write(), - getuid(), getgid(), seteuid(), - setgid(), pause() */ + getuid(), getgid(), setuid(), + setgid() */ #include /* inet_pton(), htons */ -#include /* not, or, and */ +#include /* not, and */ #include /* struct argp_option, error_t, struct argp_state, struct argp, argp_parse(), ARGP_KEY_ARG, ARGP_KEY_END, ARGP_ERR_UNKNOWN */ -#include /* sigemptyset(), sigaddset(), - sigaction(), SIGTERM, sig_atomic_t, - raise() */ -#include /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE, - EX_NOHOST, EX_IOERR, EX_PROTOCOL */ - -#ifdef __linux__ -#include /* klogctl() */ -#endif /* __linux__ */ /* Avahi */ /* All Avahi types, constants and functions @@ -106,8 +90,7 @@ gnutls_* init_gnutls_session(), GNUTLS_* */ -#include - /* gnutls_certificate_set_openpgp_key_file(), +#include /* gnutls_certificate_set_openpgp_key_file(), GNUTLS_OPENPGP_FMT_BASE64 */ /* GPGME */ @@ -127,8 +110,6 @@ static const char mandos_protocol_version[] = "1"; const char *argp_program_version = "mandos-client " VERSION; const char *argp_program_bug_address = ""; -static const char sys_class_net[] = "/sys/class/net"; -char *connect_to = NULL; /* Used for passing in values through the Avahi callback functions */ typedef struct { @@ -141,24 +122,16 @@ gpgme_ctx_t ctx; } mandos_context; -/* global context so signal handler can reach it*/ -mandos_context mc = { .simple_poll = NULL, .server = NULL, - .dh_bits = 1024, .priority = "SECURE256" - ":!CTYPE-X.509:+CTYPE-OPENPGP" }; - -sig_atomic_t quit_now = 0; -int signal_received = 0; - /* - * Make additional room in "buffer" for at least BUFFER_SIZE more - * bytes. "buffer_capacity" is how much is currently allocated, + * Make room in "buffer" for at least BUFFER_SIZE additional bytes. + * "buffer_capacity" is how much is currently allocated, * "buffer_length" is how much is already used. */ -size_t incbuffer(char **buffer, size_t buffer_length, +size_t adjustbuffer(char **buffer, size_t buffer_length, size_t buffer_capacity){ - if(buffer_length + BUFFER_SIZE > buffer_capacity){ + if (buffer_length + BUFFER_SIZE > buffer_capacity){ *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE); - if(buffer == NULL){ + if (buffer == NULL){ return 0; } buffer_capacity += BUFFER_SIZE; @@ -169,41 +142,41 @@ /* * Initialize GPGME. */ -static bool init_gpgme(const char *seckey, +static bool init_gpgme(mandos_context *mc, const char *seckey, const char *pubkey, const char *tempdir){ + int ret; gpgme_error_t rc; gpgme_engine_info_t engine_info; /* - * Helper function to insert pub and seckey to the engine keyring. + * Helper function to insert pub and seckey to the enigne keyring. */ bool import_key(const char *filename){ - int ret; int fd; gpgme_data_t pgp_data; - fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); + fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); if(fd == -1){ perror("open"); return false; } rc = gpgme_data_new_from_fd(&pgp_data, fd); - if(rc != GPG_ERR_NO_ERROR){ + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - rc = gpgme_op_import(mc.ctx, pgp_data); - if(rc != GPG_ERR_NO_ERROR){ + rc = gpgme_op_import(mc->ctx, pgp_data); + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_op_import: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - ret = (int)TEMP_FAILURE_RETRY(close(fd)); + ret = TEMP_FAILURE_RETRY(close(fd)); if(ret == -1){ perror("close"); } @@ -211,22 +184,22 @@ return true; } - if(debug){ - fprintf(stderr, "Initializing GPGME\n"); + if (debug){ + fprintf(stderr, "Initialize gpgme\n"); } /* Init GPGME */ gpgme_check_version(NULL); rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); - if(rc != GPG_ERR_NO_ERROR){ + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return false; } /* Set GPGME home directory for the OpenPGP engine only */ - rc = gpgme_get_engine_info(&engine_info); - if(rc != GPG_ERR_NO_ERROR){ + rc = gpgme_get_engine_info (&engine_info); + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return false; @@ -245,41 +218,42 @@ } /* Create new GPGME "context" */ - rc = gpgme_new(&(mc.ctx)); - if(rc != GPG_ERR_NO_ERROR){ + rc = gpgme_new(&(mc->ctx)); + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_new: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - if(not import_key(pubkey) or not import_key(seckey)){ + if (not import_key(pubkey) or not import_key(seckey)){ return false; } - return true; + return true; } /* * Decrypt OpenPGP data. * Returns -1 on error */ -static ssize_t pgp_packet_decrypt(const char *cryptotext, - size_t crypto_size, - char **plaintext){ +static ssize_t pgp_packet_decrypt (const mandos_context *mc, + const char *cryptotext, + size_t crypto_size, + char **plaintext){ gpgme_data_t dh_crypto, dh_plain; gpgme_error_t rc; ssize_t ret; size_t plaintext_capacity = 0; ssize_t plaintext_length = 0; - if(debug){ + if (debug){ fprintf(stderr, "Trying to decrypt OpenPGP data\n"); } /* Create new GPGME data buffer from memory cryptotext */ rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size, 0); - if(rc != GPG_ERR_NO_ERROR){ + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); return -1; @@ -287,7 +261,7 @@ /* Create new empty GPGME data buffer for the plaintext */ rc = gpgme_data_new(&dh_plain); - if(rc != GPG_ERR_NO_ERROR){ + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_data_new: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); gpgme_data_release(dh_crypto); @@ -296,15 +270,15 @@ /* Decrypt data from the cryptotext data buffer to the plaintext data buffer */ - rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain); - if(rc != GPG_ERR_NO_ERROR){ + rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain); + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); plaintext_length = -1; - if(debug){ + if (debug){ gpgme_decrypt_result_t result; - result = gpgme_op_decrypt_result(mc.ctx); - if(result == NULL){ + result = gpgme_op_decrypt_result(mc->ctx); + if (result == NULL){ fprintf(stderr, "gpgme_op_decrypt_result failed\n"); } else { fprintf(stderr, "Unsupported algorithm: %s\n", @@ -316,14 +290,16 @@ } gpgme_recipient_t recipient; recipient = result->recipients; - while(recipient != NULL){ - fprintf(stderr, "Public key algorithm: %s\n", - gpgme_pubkey_algo_name(recipient->pubkey_algo)); - fprintf(stderr, "Key ID: %s\n", recipient->keyid); - fprintf(stderr, "Secret key available: %s\n", - recipient->status == GPG_ERR_NO_SECKEY - ? "No" : "Yes"); - recipient = recipient->next; + if(recipient){ + while(recipient != NULL){ + fprintf(stderr, "Public key algorithm: %s\n", + gpgme_pubkey_algo_name(recipient->pubkey_algo)); + fprintf(stderr, "Key ID: %s\n", recipient->keyid); + fprintf(stderr, "Secret key available: %s\n", + recipient->status == GPG_ERR_NO_SECKEY + ? "No" : "Yes"); + recipient = recipient->next; + } } } } @@ -335,7 +311,7 @@ } /* Seek back to the beginning of the GPGME plaintext data buffer */ - if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){ + if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){ perror("gpgme_data_seek"); plaintext_length = -1; goto decrypt_end; @@ -343,11 +319,11 @@ *plaintext = NULL; while(true){ - plaintext_capacity = incbuffer(plaintext, + plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length, plaintext_capacity); - if(plaintext_capacity == 0){ - perror("incbuffer"); + if (plaintext_capacity == 0){ + perror("adjustbuffer"); plaintext_length = -1; goto decrypt_end; } @@ -355,7 +331,7 @@ ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length, BUFFER_SIZE); /* Print the data, if any */ - if(ret == 0){ + if (ret == 0){ /* EOF */ break; } @@ -385,10 +361,9 @@ return plaintext_length; } -static const char * safer_gnutls_strerror(int value){ - const char *ret = gnutls_strerror(value); /* Spurious warning from - -Wunreachable-code */ - if(ret == NULL) +static const char * safer_gnutls_strerror (int value) { + const char *ret = gnutls_strerror (value); /* Spurious warning */ + if (ret == NULL) ret = "(unknown)"; return ret; } @@ -399,7 +374,8 @@ fprintf(stderr, "GnuTLS: %s", string); } -static int init_gnutls_global(const char *pubkeyfilename, +static int init_gnutls_global(mandos_context *mc, + const char *pubkeyfilename, const char *seckeyfilename){ int ret; @@ -408,13 +384,13 @@ } ret = gnutls_global_init(); - if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "GnuTLS global_init: %s\n", - safer_gnutls_strerror(ret)); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "GnuTLS global_init: %s\n", + safer_gnutls_strerror(ret)); return -1; } - if(debug){ + if (debug){ /* "Use a log level over 10 to enable all debugging options." * - GnuTLS manual */ @@ -423,14 +399,12 @@ } /* OpenPGP credentials */ - gnutls_certificate_allocate_credentials(&mc.cred); - if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning - from - -Wunreachable-code - */ - safer_gnutls_strerror(ret)); - gnutls_global_deinit(); + gnutls_certificate_allocate_credentials(&mc->cred); + if (ret != GNUTLS_E_SUCCESS){ + fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious + warning */ + safer_gnutls_strerror(ret)); + gnutls_global_deinit (); return -1; } @@ -441,9 +415,9 @@ } ret = gnutls_certificate_set_openpgp_key_file - (mc.cred, pubkeyfilename, seckeyfilename, + (mc->cred, pubkeyfilename, seckeyfilename, GNUTLS_OPENPGP_FMT_BASE64); - if(ret != GNUTLS_E_SUCCESS){ + if (ret != GNUTLS_E_SUCCESS) { fprintf(stderr, "Error[%d] while reading the OpenPGP key pair ('%s'," " '%s')\n", ret, pubkeyfilename, seckeyfilename); @@ -453,82 +427,67 @@ } /* GnuTLS server initialization */ - ret = gnutls_dh_params_init(&mc.dh_params); - if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "Error in GnuTLS DH parameter initialization:" - " %s\n", safer_gnutls_strerror(ret)); + ret = gnutls_dh_params_init(&mc->dh_params); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "Error in GnuTLS DH parameter initialization:" + " %s\n", safer_gnutls_strerror(ret)); goto globalfail; } - ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits); - if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "Error in GnuTLS prime generation: %s\n", - safer_gnutls_strerror(ret)); + ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "Error in GnuTLS prime generation: %s\n", + safer_gnutls_strerror(ret)); goto globalfail; } - gnutls_certificate_set_dh_params(mc.cred, mc.dh_params); + gnutls_certificate_set_dh_params(mc->cred, mc->dh_params); return 0; globalfail: - gnutls_certificate_free_credentials(mc.cred); + gnutls_certificate_free_credentials(mc->cred); gnutls_global_deinit(); - gnutls_dh_params_deinit(mc.dh_params); + gnutls_dh_params_deinit(mc->dh_params); return -1; } -static int init_gnutls_session(gnutls_session_t *session){ +static int init_gnutls_session(mandos_context *mc, + gnutls_session_t *session){ int ret; /* GnuTLS session creation */ - do { - ret = gnutls_init(session, GNUTLS_SERVER); - if(quit_now){ - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + ret = gnutls_init(session, GNUTLS_SERVER); + if (ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error in GnuTLS session initialization: %s\n", safer_gnutls_strerror(ret)); } { const char *err; - do { - ret = gnutls_priority_set_direct(*session, mc.priority, &err); - if(quit_now){ - gnutls_deinit(*session); - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + ret = gnutls_priority_set_direct(*session, mc->priority, &err); + if (ret != GNUTLS_E_SUCCESS) { fprintf(stderr, "Syntax error at: %s\n", err); fprintf(stderr, "GnuTLS error: %s\n", safer_gnutls_strerror(ret)); - gnutls_deinit(*session); + gnutls_deinit (*session); return -1; } } - do { - ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, - mc.cred); - if(quit_now){ - gnutls_deinit(*session); - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, + mc->cred); + if (ret != GNUTLS_E_SUCCESS) { fprintf(stderr, "Error setting GnuTLS credentials: %s\n", safer_gnutls_strerror(ret)); - gnutls_deinit(*session); + gnutls_deinit (*session); return -1; } /* ignore client certificate if any. */ - gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE); + gnutls_certificate_server_set_request (*session, + GNUTLS_CERT_IGNORE); - gnutls_dh_set_prime_bits(*session, mc.dh_bits); + gnutls_dh_set_prime_bits (*session, mc->dh_bits); return 0; } @@ -540,137 +499,66 @@ /* Called when a Mandos server is found */ static int start_mandos_communication(const char *ip, uint16_t port, AvahiIfIndex if_index, - int af){ - int ret, tcp_sd = -1; - ssize_t sret; - union { - struct sockaddr_in in; - struct sockaddr_in6 in6; - } to; + mandos_context *mc){ + int ret, tcp_sd; + union { struct sockaddr in; struct sockaddr_in6 in6; } to; char *buffer = NULL; - char *decrypted_buffer = NULL; + char *decrypted_buffer; size_t buffer_length = 0; size_t buffer_capacity = 0; + ssize_t decrypted_buffer_size; size_t written; - int retval = -1; + int retval = 0; + char interface[IF_NAMESIZE]; gnutls_session_t session; - int pf; /* Protocol family */ - - errno = 0; - - if(quit_now){ - errno = EINTR; - return -1; - } - - switch(af){ - case AF_INET6: - pf = PF_INET6; - break; - case AF_INET: - pf = PF_INET; - break; - default: - fprintf(stderr, "Bad address family: %d\n", af); - errno = EINVAL; - return -1; - } - - ret = init_gnutls_session(&session); - if(ret != 0){ + + ret = init_gnutls_session (mc, &session); + if (ret != 0){ return -1; } if(debug){ - fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16 + fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16 "\n", ip, port); } - tcp_sd = socket(pf, SOCK_STREAM, 0); - if(tcp_sd < 0){ - int e = errno; + tcp_sd = socket(PF_INET6, SOCK_STREAM, 0); + if(tcp_sd < 0) { perror("socket"); - errno = e; - goto mandos_end; + return -1; } - if(quit_now){ - errno = EINTR; - goto mandos_end; + if(debug){ + if(if_indextoname((unsigned int)if_index, interface) == NULL){ + perror("if_indextoname"); + return -1; + } + fprintf(stderr, "Binding to interface %s\n", interface); } memset(&to, 0, sizeof(to)); - if(af == AF_INET6){ - to.in6.sin6_family = (sa_family_t)af; - ret = inet_pton(af, ip, &to.in6.sin6_addr); - } else { /* IPv4 */ - to.in.sin_family = (sa_family_t)af; - ret = inet_pton(af, ip, &to.in.sin_addr); - } - if(ret < 0 ){ - int e = errno; + to.in6.sin6_family = AF_INET6; + /* It would be nice to have a way to detect if we were passed an + IPv4 address here. Now we assume an IPv6 address. */ + ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr); + if (ret < 0 ){ perror("inet_pton"); - errno = e; - goto mandos_end; + return -1; } if(ret == 0){ - int e = errno; fprintf(stderr, "Bad address: %s\n", ip); - errno = e; - goto mandos_end; - } - if(af == AF_INET6){ - to.in6.sin6_port = htons(port); /* Spurious warnings from - -Wconversion and - -Wunreachable-code */ - - if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */ - (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and - -Wunreachable-code*/ - if(if_index == AVAHI_IF_UNSPEC){ - fprintf(stderr, "An IPv6 link-local address is incomplete" - " without a network interface\n"); - errno = EINVAL; - goto mandos_end; - } - /* Set the network interface number as scope */ - to.in6.sin6_scope_id = (uint32_t)if_index; - } - } else { - to.in.sin_port = htons(port); /* Spurious warnings from - -Wconversion and - -Wunreachable-code */ - } + return -1; + } + to.in6.sin6_port = htons(port); /* Spurious warning */ - if(quit_now){ - errno = EINTR; - goto mandos_end; - } + to.in6.sin6_scope_id = (uint32_t)if_index; if(debug){ - if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){ - char interface[IF_NAMESIZE]; - if(if_indextoname((unsigned int)if_index, interface) == NULL){ - perror("if_indextoname"); - } else { - fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n", - ip, interface, port); - } - } else { - fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip, - port); - } - char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ? - INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = ""; - const char *pcret; - if(af == AF_INET6){ - pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr, - sizeof(addrstr)); - } else { - pcret = inet_ntop(af, &(to.in.sin_addr), addrstr, - sizeof(addrstr)); - } - if(pcret == NULL){ + fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip, + port); + char addrstr[INET6_ADDRSTRLEN] = ""; + if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr, + sizeof(addrstr)) == NULL){ perror("inet_ntop"); } else { if(strcmp(addrstr, ip) != 0){ @@ -679,156 +567,101 @@ } } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - if(af == AF_INET6){ - ret = connect(tcp_sd, &to.in6, sizeof(to)); - } else { - ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */ - } - if(ret < 0){ - if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){ - int e = errno; - perror("connect"); - errno = e; - } - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; + ret = connect(tcp_sd, &to.in, sizeof(to)); + if (ret < 0){ + perror("connect"); + return -1; } const char *out = mandos_protocol_version; written = 0; - while(true){ + while (true){ size_t out_size = strlen(out); - ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written, + ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written, out_size - written)); - if(ret == -1){ - int e = errno; + if (ret == -1){ perror("write"); - errno = e; + retval = -1; goto mandos_end; } written += (size_t)ret; if(written < out_size){ continue; } else { - if(out == mandos_protocol_version){ + if (out == mandos_protocol_version){ written = 0; out = "\r\n"; } else { break; } } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } } if(debug){ fprintf(stderr, "Establishing TLS session with %s\n", ip); } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd); - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - do { - ret = gnutls_handshake(session); - if(quit_now){ - errno = EINTR; - goto mandos_end; - } + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd); + + do{ + ret = gnutls_handshake (session); } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - if(ret != GNUTLS_E_SUCCESS){ + if (ret != GNUTLS_E_SUCCESS){ if(debug){ fprintf(stderr, "*** GnuTLS Handshake failed ***\n"); - gnutls_perror(ret); + gnutls_perror (ret); } - errno = EPROTO; + retval = -1; goto mandos_end; } /* Read OpenPGP packet that contains the wanted password */ if(debug){ - fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n", + fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip); } while(true){ - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - buffer_capacity = incbuffer(&buffer, buffer_length, + buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity); - if(buffer_capacity == 0){ - int e = errno; - perror("incbuffer"); - errno = e; - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - sret = gnutls_record_recv(session, buffer+buffer_length, - BUFFER_SIZE); - if(sret == 0){ + if (buffer_capacity == 0){ + perror("adjustbuffer"); + retval = -1; + goto mandos_end; + } + + ret = gnutls_record_recv(session, buffer+buffer_length, + BUFFER_SIZE); + if (ret == 0){ break; } - if(sret < 0){ - switch(sret){ + if (ret < 0){ + switch(ret){ case GNUTLS_E_INTERRUPTED: case GNUTLS_E_AGAIN: break; case GNUTLS_E_REHANDSHAKE: - do { - ret = gnutls_handshake(session); - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } + do{ + ret = gnutls_handshake (session); } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - if(ret < 0){ + if (ret < 0){ fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n"); - gnutls_perror(ret); - errno = EPROTO; + gnutls_perror (ret); + retval = -1; goto mandos_end; } break; default: fprintf(stderr, "Unknown error while reading data from" " encrypted session with Mandos server\n"); - gnutls_bye(session, GNUTLS_SHUT_RDWR); - errno = EIO; + retval = -1; + gnutls_bye (session, GNUTLS_SHUT_RDWR); goto mandos_end; } } else { - buffer_length += (size_t) sret; + buffer_length += (size_t) ret; } } @@ -836,80 +669,51 @@ fprintf(stderr, "Closing TLS session\n"); } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - do { - ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - - if(buffer_length > 0){ - ssize_t decrypted_buffer_size; - decrypted_buffer_size = pgp_packet_decrypt(buffer, + gnutls_bye (session, GNUTLS_SHUT_RDWR); + + if (buffer_length > 0){ + decrypted_buffer_size = pgp_packet_decrypt(mc, buffer, buffer_length, &decrypted_buffer); - if(decrypted_buffer_size >= 0){ - + if (decrypted_buffer_size >= 0){ written = 0; while(written < (size_t) decrypted_buffer_size){ - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - ret = (int)fwrite(decrypted_buffer + written, 1, - (size_t)decrypted_buffer_size - written, - stdout); + ret = (int)fwrite (decrypted_buffer + written, 1, + (size_t)decrypted_buffer_size - written, + stdout); if(ret == 0 and ferror(stdout)){ - int e = errno; if(debug){ fprintf(stderr, "Error writing encrypted data: %s\n", strerror(errno)); } - errno = e; - goto mandos_end; + retval = -1; + break; } written += (size_t)ret; } - retval = 0; + free(decrypted_buffer); + } else { + retval = -1; } + } else { + retval = -1; } /* Shutdown procedure */ mandos_end: - { - int e = errno; - free(decrypted_buffer); - free(buffer); - if(tcp_sd >= 0){ - ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd)); - } - if(ret == -1){ - if(e == 0){ - e = errno; - } - perror("close"); - } - gnutls_deinit(session); - if(quit_now){ - e = EINTR; - retval = -1; - } - errno = e; + free(buffer); + ret = TEMP_FAILURE_RETRY(close(tcp_sd)); + if(ret == -1){ + perror("close"); } + gnutls_deinit (session); return retval; } static void resolve_callback(AvahiSServiceResolver *r, AvahiIfIndex interface, - AvahiProtocol proto, + AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, @@ -920,22 +724,19 @@ AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata){ + void* userdata) { + mandos_context *mc = userdata; assert(r); /* Called whenever a service has been resolved successfully or timed out */ - if(quit_now){ - return; - } - - switch(event){ + switch (event) { default: case AVAHI_RESOLVER_FAILURE: fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'" " of type '%s' in domain '%s': %s\n", name, type, domain, - avahi_strerror(avahi_server_errno(mc.server))); + avahi_strerror(avahi_server_errno(mc->server))); break; case AVAHI_RESOLVER_FOUND: @@ -944,45 +745,41 @@ avahi_address_snprint(ip, sizeof(ip), address); if(debug){ fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %" - PRIdMAX ") on port %" PRIu16 "\n", name, host_name, - ip, (intmax_t)interface, port); + PRIu16 ") on port %d\n", name, host_name, ip, + interface, port); } - int ret = start_mandos_communication(ip, port, interface, - avahi_proto_to_af(proto)); - if(ret == 0){ - avahi_simple_poll_quit(mc.simple_poll); + int ret = start_mandos_communication(ip, port, interface, mc); + if (ret == 0){ + avahi_simple_poll_quit(mc->simple_poll); } } } avahi_s_service_resolver_free(r); } -static void browse_callback(AvahiSServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *type, - const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags - flags, - AVAHI_GCC_UNUSED void* userdata){ +static void browse_callback( AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags + flags, + void* userdata) { + mandos_context *mc = userdata; assert(b); /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - if(quit_now){ - return; - } - - switch(event){ + switch (event) { default: case AVAHI_BROWSER_FAILURE: fprintf(stderr, "(Avahi browser) %s\n", - avahi_strerror(avahi_server_errno(mc.server))); - avahi_simple_poll_quit(mc.simple_poll); + avahi_strerror(avahi_server_errno(mc->server))); + avahi_simple_poll_quit(mc->simple_poll); return; case AVAHI_BROWSER_NEW: @@ -991,11 +788,12 @@ the callback function is called the Avahi server will free the resolver for us. */ - if(avahi_s_service_resolver_new(mc.server, interface, protocol, - name, type, domain, protocol, 0, - resolve_callback, NULL) == NULL) + if (!(avahi_s_service_resolver_new(mc->server, interface, + protocol, name, type, domain, + AVAHI_PROTO_INET6, 0, + resolve_callback, mc))) fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n", - name, avahi_strerror(avahi_server_errno(mc.server))); + name, avahi_strerror(avahi_server_errno(mc->server))); break; case AVAHI_BROWSER_REMOVE: @@ -1010,809 +808,337 @@ } } -/* stop main loop after sigterm has been called */ -static void handle_sigterm(int sig){ - if(quit_now){ - return; - } - quit_now = 1; - signal_received = sig; - int old_errno = errno; - if(mc.simple_poll != NULL){ - avahi_simple_poll_quit(mc.simple_poll); - } - errno = old_errno; -} - -/* - * This function determines if a directory entry in /sys/class/net - * corresponds to an acceptable network device. - * (This function is passed to scandir(3) as a filter function.) - */ -int good_interface(const struct dirent *if_entry){ - ssize_t ssret; - char *flagname = NULL; - int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net, - if_entry->d_name); - if(ret < 0){ - perror("asprintf"); - return 0; - } - if(if_entry->d_name[0] == '.'){ - return 0; - } - int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY)); - if(flags_fd == -1){ - perror("open"); - return 0; - } - typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */ - /* read line from flags_fd */ - ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */ - char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */ - flagstring[(size_t)to_read] = '\0'; - if(flagstring == NULL){ - perror("malloc"); - close(flags_fd); - return 0; - } - while(to_read > 0){ - ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring, - (size_t)to_read)); - if(ssret == -1){ - perror("read"); - free(flagstring); - close(flags_fd); - return 0; - } - to_read -= ssret; - if(ssret == 0){ - break; - } - } - close(flags_fd); - intmax_t tmpmax; - char *tmp; - errno = 0; - tmpmax = strtoimax(flagstring, &tmp, 0); - if(errno != 0 or tmp == flagstring or (*tmp != '\0' - and not (isspace(*tmp))) - or tmpmax != (ifreq_flags)tmpmax){ - if(debug){ - fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n", - flagstring, if_entry->d_name); - } - free(flagstring); - return 0; - } - free(flagstring); - ifreq_flags flags = (ifreq_flags)tmpmax; - /* Reject the loopback device */ - if(flags & IFF_LOOPBACK){ - if(debug){ - fprintf(stderr, "Rejecting loopback interface \"%s\"\n", - if_entry->d_name); - } - return 0; - } - /* Accept point-to-point devices only if connect_to is specified */ - if(connect_to != NULL and (flags & IFF_POINTOPOINT)){ - if(debug){ - fprintf(stderr, "Accepting point-to-point interface \"%s\"\n", - if_entry->d_name); - } - return 1; - } - /* Otherwise, reject non-broadcast-capable devices */ - if(not (flags & IFF_BROADCAST)){ - if(debug){ - fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n", - if_entry->d_name); - } - return 0; - } - /* Accept this device */ - if(debug){ - fprintf(stderr, "Interface \"%s\" is acceptable\n", - if_entry->d_name); - } - return 1; -} - int main(int argc, char *argv[]){ - AvahiSServiceBrowser *sb = NULL; - int error; - int ret; - intmax_t tmpmax; - char *tmp; - int exitcode = EXIT_SUCCESS; - const char *interface = ""; - struct ifreq network; - int sd = -1; - bool take_down_interface = false; - uid_t uid; - gid_t gid; - char tempdir[] = "/tmp/mandosXXXXXX"; - bool tempdir_created = false; - AvahiIfIndex if_index = AVAHI_IF_UNSPEC; - const char *seckey = PATHDIR "/" SECKEY; - const char *pubkey = PATHDIR "/" PUBKEY; - - bool gnutls_initialized = false; - bool gpgme_initialized = false; - float delay = 2.5f; - - struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL }; - struct sigaction sigterm_action = { .sa_handler = handle_sigterm }; - - uid = getuid(); - gid = getgid(); - - /* Lower any group privileges we might have, just to be safe */ - errno = 0; - ret = setgid(gid); - if(ret == -1){ - perror("setgid"); - } - - /* Lower user privileges (temporarily) */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - - if(quit_now){ - goto end; - } - - { - struct argp_option options[] = { - { .name = "debug", .key = 128, - .doc = "Debug mode", .group = 3 }, - { .name = "connect", .key = 'c', - .arg = "ADDRESS:PORT", - .doc = "Connect directly to a specific Mandos server", - .group = 1 }, - { .name = "interface", .key = 'i', - .arg = "NAME", - .doc = "Network interface that will be used to search for" - " Mandos servers", - .group = 1 }, - { .name = "seckey", .key = 's', - .arg = "FILE", - .doc = "OpenPGP secret key file base name", - .group = 1 }, - { .name = "pubkey", .key = 'p', - .arg = "FILE", - .doc = "OpenPGP public key file base name", - .group = 2 }, - { .name = "dh-bits", .key = 129, - .arg = "BITS", - .doc = "Bit length of the prime number used in the" - " Diffie-Hellman key exchange", - .group = 2 }, - { .name = "priority", .key = 130, - .arg = "STRING", - .doc = "GnuTLS priority string for the TLS handshake", - .group = 1 }, - { .name = "delay", .key = 131, - .arg = "SECONDS", - .doc = "Maximum delay to wait for interface startup", - .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 } - }; - - error_t parse_opt(int key, char *arg, - struct argp_state *state){ - errno = 0; - switch(key){ - case 128: /* --debug */ - debug = true; - break; - case 'c': /* --connect */ - connect_to = arg; - break; - case 'i': /* --interface */ - interface = arg; - break; - case 's': /* --seckey */ - seckey = arg; - break; - case 'p': /* --pubkey */ - pubkey = arg; - break; - case 129: /* --dh-bits */ - errno = 0; - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (typeof(mc.dh_bits))tmpmax){ - argp_error(state, "Bad number of DH bits"); - } - mc.dh_bits = (typeof(mc.dh_bits))tmpmax; - break; - case 130: /* --priority */ - mc.priority = arg; - break; - case 131: /* --delay */ - errno = 0; - delay = strtof(arg, &tmp); - if(errno != 0 or tmp == arg or *tmp != '\0'){ - argp_error(state, "Bad delay"); - } - break; - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - case '?': /* --help */ - argp_state_help(state, state->out_stream, - (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR) - & ~(unsigned int)ARGP_HELP_EXIT_OK); - case -3: /* --usage */ - argp_state_help(state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR); - case 'V': /* --version */ - fprintf(state->out_stream, "%s\n", argp_program_version); - exit(argp_err_exit_status); - break; - default: - return ARGP_ERR_UNKNOWN; - } - return errno; - } - - struct argp argp = { .options = options, .parser = parse_opt, - .args_doc = "", - .doc = "Mandos client -- Get and decrypt" - " passwords from a Mandos server" }; - ret = argp_parse(&argp, argc, argv, - ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - perror("argp_parse"); - exitcode = EX_OSERR; - goto end; - case EINVAL: - exitcode = EX_USAGE; - goto end; - } - } - - if(not debug){ - avahi_set_log_function(empty_log); - } - - if(interface[0] == '\0'){ - struct dirent **direntries; - ret = scandir(sys_class_net, &direntries, good_interface, - alphasort); - if(ret >= 1){ - /* Pick the first good interface */ - interface = strdup(direntries[0]->d_name); - if(debug){ - fprintf(stderr, "Using interface \"%s\"\n", interface); - } - if(interface == NULL){ - perror("malloc"); - free(direntries); - exitcode = EXIT_FAILURE; - goto end; - } - free(direntries); - } else { - free(direntries); - fprintf(stderr, "Could not find a network interface\n"); - exitcode = EXIT_FAILURE; - goto end; - } - } - - /* Initialize Avahi early so avahi_simple_poll_quit() can be called - from the signal handler */ - /* Initialize the pseudo-RNG for Avahi */ - srand((unsigned int) time(NULL)); - mc.simple_poll = avahi_simple_poll_new(); - if(mc.simple_poll == NULL){ - fprintf(stderr, "Avahi: Failed to create simple poll object.\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } - - sigemptyset(&sigterm_action.sa_mask); - ret = sigaddset(&sigterm_action.sa_mask, SIGINT); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - ret = sigaddset(&sigterm_action.sa_mask, SIGHUP); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - ret = sigaddset(&sigterm_action.sa_mask, SIGTERM); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - /* Need to check if the handler is SIG_IGN before handling: - | [[info:libc:Initial Signal Actions]] | - | [[info:libc:Basic Signal Handling]] | - */ - ret = sigaction(SIGINT, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - ret = sigaction(SIGHUP, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - ret = sigaction(SIGTERM, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - - /* If the interface is down, bring it up */ - if(strcmp(interface, "none") != 0){ + AvahiSServiceBrowser *sb = NULL; + int error; + int ret; + int exitcode = EXIT_SUCCESS; + const char *interface = "eth0"; + struct ifreq network; + int sd; + uid_t uid; + gid_t gid; + char *connect_to = NULL; + char tempdir[] = "/tmp/mandosXXXXXX"; + AvahiIfIndex if_index = AVAHI_IF_UNSPEC; + const char *seckey = PATHDIR "/" SECKEY; + const char *pubkey = PATHDIR "/" PUBKEY; + + mandos_context mc = { .simple_poll = NULL, .server = NULL, + .dh_bits = 1024, .priority = "SECURE256" + ":!CTYPE-X.509:+CTYPE-OPENPGP" }; + bool gnutls_initalized = false; + bool gpgme_initalized = false; + + { + struct argp_option options[] = { + { .name = "debug", .key = 128, + .doc = "Debug mode", .group = 3 }, + { .name = "connect", .key = 'c', + .arg = "ADDRESS:PORT", + .doc = "Connect directly to a specific Mandos server", + .group = 1 }, + { .name = "interface", .key = 'i', + .arg = "NAME", + .doc = "Interface that will be used to search for Mandos" + " servers", + .group = 1 }, + { .name = "seckey", .key = 's', + .arg = "FILE", + .doc = "OpenPGP secret key file base name", + .group = 1 }, + { .name = "pubkey", .key = 'p', + .arg = "FILE", + .doc = "OpenPGP public key file base name", + .group = 2 }, + { .name = "dh-bits", .key = 129, + .arg = "BITS", + .doc = "Bit length of the prime number used in the" + " Diffie-Hellman key exchange", + .group = 2 }, + { .name = "priority", .key = 130, + .arg = "STRING", + .doc = "GnuTLS priority string for the TLS handshake", + .group = 1 }, + { .name = NULL } + }; + + 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. */ + switch (key) { + case 128: /* --debug */ + debug = true; + break; + case 'c': /* --connect */ + connect_to = arg; + break; + case 'i': /* --interface */ + interface = arg; + break; + case 's': /* --seckey */ + seckey = arg; + break; + case 'p': /* --pubkey */ + pubkey = arg; + break; + case 129: /* --dh-bits */ + errno = 0; + mc.dh_bits = (unsigned int) strtol(arg, NULL, 10); + if (errno){ + perror("strtol"); + exit(EXIT_FAILURE); + } + break; + case 130: /* --priority */ + mc.priority = arg; + break; + case ARGP_KEY_ARG: + argp_usage (state); + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "", + .doc = "Mandos client -- Get and decrypt" + " passwords from a Mandos server" }; + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitcode = EXIT_FAILURE; + goto end; + } + } + + /* If the interface is down, bring it up */ + { + sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if(sd < 0) { + perror("socket"); + exitcode = EXIT_FAILURE; + goto end; + } + strcpy(network.ifr_name, interface); + ret = ioctl(sd, SIOCGIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCGIFFLAGS"); + exitcode = EXIT_FAILURE; + goto end; + } + if((network.ifr_flags & IFF_UP) == 0){ + network.ifr_flags |= IFF_UP; + ret = ioctl(sd, SIOCSIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCSIFFLAGS"); + exitcode = EXIT_FAILURE; + goto end; + } + } + ret = TEMP_FAILURE_RETRY(close(sd)); + if(ret == -1){ + perror("close"); + } + } + + uid = getuid(); + gid = getgid(); + + ret = setuid(uid); + if (ret == -1){ + perror("setuid"); + } + + setgid(gid); + if (ret == -1){ + perror("setgid"); + } + + ret = init_gnutls_global(&mc, pubkey, seckey); + if (ret == -1){ + fprintf(stderr, "init_gnutls_global failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gnutls_initalized = true; + } + + if(mkdtemp(tempdir) == NULL){ + perror("mkdtemp"); + tempdir[0] = '\0'; + goto end; + } + + if(not init_gpgme(&mc, pubkey, seckey, tempdir)){ + fprintf(stderr, "gpgme_initalized failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gpgme_initalized = true; + } + if_index = (AvahiIfIndex) if_nametoindex(interface); if(if_index == 0){ fprintf(stderr, "No such interface: \"%s\"\n", interface); - exitcode = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Re-raise priviliges */ - errno = 0; - ret = seteuid(0); - if(ret == -1){ - perror("seteuid"); - } - -#ifdef __linux__ - /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO - messages about the network interface to mess up the prompt */ - ret = klogctl(8, NULL, 5); - bool restore_loglevel = true; - if(ret == -1){ - restore_loglevel = false; - perror("klogctl"); - } -#endif /* __linux__ */ - - sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if(sd < 0){ - perror("socket"); - exitcode = EX_OSERR; -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - strcpy(network.ifr_name, interface); - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - exitcode = EX_OSERR; - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - if((network.ifr_flags & IFF_UP) == 0){ - network.ifr_flags |= IFF_UP; - take_down_interface = true; - ret = ioctl(sd, SIOCSIFFLAGS, &network); - if(ret == -1){ - take_down_interface = false; - perror("ioctl SIOCSIFFLAGS +IFF_UP"); - exitcode = EX_OSERR; -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - } - /* sleep checking until interface is running */ - for(int i=0; i < delay * 4; i++){ - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); - } else if(network.ifr_flags & IFF_RUNNING){ - break; - } - struct timespec sleeptime = { .tv_nsec = 250000000 }; - ret = nanosleep(&sleeptime, NULL); - if(ret == -1 and errno != EINTR){ - perror("nanosleep"); - } - } - if(not take_down_interface){ - /* We won't need the socket anymore */ - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } - } -#ifdef __linux__ - if(restore_loglevel){ - /* Restores kernel loglevel to default */ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - if(take_down_interface){ - /* Lower privileges */ - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - } else { - /* Lower privileges permanently */ - ret = setuid(uid); - if(ret == -1){ - perror("setuid"); - } - } - } - - if(quit_now){ - goto end; - } - - ret = init_gnutls_global(pubkey, seckey); - if(ret == -1){ - fprintf(stderr, "init_gnutls_global failed\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } else { - gnutls_initialized = true; - } - - if(quit_now){ - goto end; - } - - tempdir_created = true; - if(mkdtemp(tempdir) == NULL){ - tempdir_created = false; - perror("mkdtemp"); - goto end; - } - - if(quit_now){ - goto end; - } - - if(not init_gpgme(pubkey, seckey, tempdir)){ - fprintf(stderr, "init_gpgme failed\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } else { - gpgme_initialized = true; - } - - if(quit_now){ - goto end; - } - - if(connect_to != NULL){ - /* Connect directly, do not use Zeroconf */ - /* (Mainly meant for debugging) */ - char *address = strrchr(connect_to, ':'); - if(address == NULL){ - fprintf(stderr, "No colon in address\n"); - exitcode = EX_USAGE; - goto end; - } - - if(quit_now){ - goto end; - } - - uint16_t port; - errno = 0; - tmpmax = strtoimax(address+1, &tmp, 10); - if(errno != 0 or tmp == address+1 or *tmp != '\0' - or tmpmax != (uint16_t)tmpmax){ - fprintf(stderr, "Bad port number\n"); - exitcode = EX_USAGE; - goto end; - } - - if(quit_now){ - goto end; - } - - port = (uint16_t)tmpmax; - *address = '\0'; - address = connect_to; - /* Colon in address indicates IPv6 */ - int af; - if(strchr(address, ':') != NULL){ - af = AF_INET6; - } else { - af = AF_INET; - } - - if(quit_now){ - goto end; - } - - while(not quit_now){ - ret = start_mandos_communication(address, port, if_index, af); - if(quit_now or ret == 0){ - break; - } - sleep(15); - }; - - if (not quit_now){ - exitcode = EXIT_SUCCESS; - } - - goto end; - } - - if(quit_now){ - goto end; - } - - { - AvahiServerConfig config; - /* Do not publish any local Zeroconf records */ - avahi_server_config_init(&config); - config.publish_hinfo = 0; - config.publish_addresses = 0; - config.publish_workstation = 0; - config.publish_domain = 0; - - /* Allocate a new server */ - mc.server = avahi_server_new(avahi_simple_poll_get - (mc.simple_poll), &config, NULL, - NULL, &error); - - /* Free the Avahi configuration data */ - avahi_server_config_free(&config); - } - - /* Check if creating the Avahi server object succeeded */ - if(mc.server == NULL){ - fprintf(stderr, "Failed to create Avahi server: %s\n", - avahi_strerror(error)); - exitcode = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Create the Avahi service browser */ - sb = avahi_s_service_browser_new(mc.server, if_index, - AVAHI_PROTO_UNSPEC, "_mandos._tcp", - NULL, 0, browse_callback, NULL); - if(sb == NULL){ - fprintf(stderr, "Failed to create service browser: %s\n", - avahi_strerror(avahi_server_errno(mc.server))); - exitcode = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Run the main loop */ - - if(debug){ - fprintf(stderr, "Starting Avahi loop search\n"); - } - - avahi_simple_poll_loop(mc.simple_poll); - + exit(EXIT_FAILURE); + } + + if(connect_to != NULL){ + /* Connect directly, do not use Zeroconf */ + /* (Mainly meant for debugging) */ + char *address = strrchr(connect_to, ':'); + if(address == NULL){ + fprintf(stderr, "No colon in address\n"); + exitcode = EXIT_FAILURE; + goto end; + } + errno = 0; + uint16_t port = (uint16_t) strtol(address+1, NULL, 10); + if(errno){ + perror("Bad port number"); + exitcode = EXIT_FAILURE; + goto end; + } + *address = '\0'; + address = connect_to; + ret = start_mandos_communication(address, port, if_index, &mc); + if(ret < 0){ + exitcode = EXIT_FAILURE; + } else { + exitcode = EXIT_SUCCESS; + } + goto end; + } + + if (not debug){ + avahi_set_log_function(empty_log); + } + + /* Initialize the pseudo-RNG for Avahi */ + srand((unsigned int) time(NULL)); + + /* Allocate main Avahi loop object */ + mc.simple_poll = avahi_simple_poll_new(); + if (mc.simple_poll == NULL) { + fprintf(stderr, "Avahi: Failed to create simple poll" + " object.\n"); + exitcode = EXIT_FAILURE; + goto end; + } + + { + AvahiServerConfig config; + /* Do not publish any local Zeroconf records */ + avahi_server_config_init(&config); + config.publish_hinfo = 0; + config.publish_addresses = 0; + config.publish_workstation = 0; + config.publish_domain = 0; + + /* Allocate a new server */ + mc.server = avahi_server_new(avahi_simple_poll_get + (mc.simple_poll), &config, NULL, + NULL, &error); + + /* Free the Avahi configuration data */ + avahi_server_config_free(&config); + } + + /* Check if creating the Avahi server object succeeded */ + if (mc.server == NULL) { + fprintf(stderr, "Failed to create Avahi server: %s\n", + avahi_strerror(error)); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Create the Avahi service browser */ + sb = avahi_s_service_browser_new(mc.server, if_index, + AVAHI_PROTO_INET6, + "_mandos._tcp", NULL, 0, + browse_callback, &mc); + if (sb == NULL) { + fprintf(stderr, "Failed to create service browser: %s\n", + avahi_strerror(avahi_server_errno(mc.server))); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Run the main loop */ + + if (debug){ + fprintf(stderr, "Starting Avahi loop search\n"); + } + + avahi_simple_poll_loop(mc.simple_poll); + end: - - if(debug){ - fprintf(stderr, "%s exiting\n", argv[0]); - } - - /* Cleanup things */ - if(sb != NULL) - avahi_s_service_browser_free(sb); - - if(mc.server != NULL) - avahi_server_free(mc.server); - - if(mc.simple_poll != NULL) - avahi_simple_poll_free(mc.simple_poll); - - if(gnutls_initialized){ - gnutls_certificate_free_credentials(mc.cred); - gnutls_global_deinit(); - gnutls_dh_params_deinit(mc.dh_params); - } - - if(gpgme_initialized){ - gpgme_release(mc.ctx); - } - - /* Take down the network interface */ - if(take_down_interface){ - /* Re-raise priviliges */ - errno = 0; - ret = seteuid(0); - if(ret == -1){ - perror("seteuid"); - } - if(geteuid() == 0){ - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); - } else if(network.ifr_flags & IFF_UP) { - network.ifr_flags &= ~(short)IFF_UP; /* clear flag */ - ret = ioctl(sd, SIOCSIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCSIFFLAGS -IFF_UP"); - } - } - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } - /* Lower privileges permanently */ - errno = 0; - ret = setuid(uid); - if(ret == -1){ - perror("setuid"); - } - } - } - - /* Removes the temp directory used by GPGME */ - if(tempdir_created){ - DIR *d; - struct dirent *direntry; - d = opendir(tempdir); - if(d == NULL){ - if(errno != ENOENT){ + + if (debug){ + fprintf(stderr, "%s exiting\n", argv[0]); + } + + /* Cleanup things */ + if (sb != NULL) + avahi_s_service_browser_free(sb); + + if (mc.server != NULL) + avahi_server_free(mc.server); + + if (mc.simple_poll != NULL) + avahi_simple_poll_free(mc.simple_poll); + + if (gnutls_initalized){ + gnutls_certificate_free_credentials(mc.cred); + gnutls_global_deinit (); + gnutls_dh_params_deinit(mc.dh_params); + } + + if(gpgme_initalized){ + gpgme_release(mc.ctx); + } + + /* Removes the temp directory used by GPGME */ + if(tempdir[0] != '\0'){ + DIR *d; + struct dirent *direntry; + d = opendir(tempdir); + if(d == NULL){ perror("opendir"); - } - } else { - while(true){ - direntry = readdir(d); - if(direntry == NULL){ - break; - } - /* Skip "." and ".." */ - if(direntry->d_name[0] == '.' - and (direntry->d_name[1] == '\0' - or (direntry->d_name[1] == '.' - and direntry->d_name[2] == '\0'))){ - continue; - } - char *fullname = NULL; - ret = asprintf(&fullname, "%s/%s", tempdir, - direntry->d_name); - if(ret < 0){ - perror("asprintf"); - continue; - } - ret = remove(fullname); - if(ret == -1){ - fprintf(stderr, "remove(\"%s\"): %s\n", fullname, - strerror(errno)); - } - free(fullname); - } - closedir(d); - } - ret = rmdir(tempdir); - if(ret == -1 and errno != ENOENT){ - perror("rmdir"); - } - } - - if(quit_now){ - sigemptyset(&old_sigterm_action.sa_mask); - old_sigterm_action.sa_handler = SIG_DFL; - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &old_sigterm_action, - NULL)); - if(ret == -1){ - perror("sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - perror("raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return exitcode; + } else { + while(true){ + direntry = readdir(d); + if(direntry == NULL){ + break; + } + if (direntry->d_type == DT_REG){ + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", tempdir, + direntry->d_name); + if(ret < 0){ + perror("asprintf"); + continue; + } + ret = unlink(fullname); + if(ret == -1){ + fprintf(stderr, "unlink(\"%s\"): %s", + fullname, strerror(errno)); + } + free(fullname); + } + } + closedir(d); + } + ret = rmdir(tempdir); + if(ret == -1){ + perror("rmdir"); + } + } + + return exitcode; } === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2010-09-26 18:32:58 +0000 +++ plugins.d/mandos-client.xml 2008-10-04 01:55:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -93,10 +92,6 @@ - - - - @@ -126,16 +121,11 @@ &COMMANDNAME; is a client program that communicates with mandos8 - to get a password. In slightly more detail, this client program - brings up a network interface, uses the interface’s IPv6 - link-local address to get network connectivity, uses Zeroconf to - find servers on the local network, and communicates with servers - using TLS with an OpenPGP key to ensure authenticity and - confidentiality. This client program keeps running, trying all - servers on the network, until it receives a satisfactory reply - or a TERM signal is received. If no servers are found, or after - all servers have been tried, it waits indefinitely for new - servers to appear. + to get a password. It uses IPv6 link-local addresses to get + network connectivity, Zeroconf to find servers, and TLS with an + OpenPGP key to ensure authenticity and confidentiality. It + keeps running, trying all servers on the network, until it + receives a satisfactory reply or a TERM signal is received. This program is not meant to be run directly; it is really meant @@ -195,38 +185,21 @@ - + Network interface that will be brought up and scanned for - Mandos servers to connect to. The default is the empty - string, which will automatically choose an appropriate - interface. + Mandos servers to connect to. The default it + eth0. If the option is used, this specifies the interface to use to connect to the address given. - - Note that since this program will normally run in the - initial RAM disk environment, the interface must be an - interface which exists at that stage. Thus, the interface - can not be a pseudo-interface such as br0 - or tun0; such interfaces will not exist - until much later in the boot process, and can not be used - by this program. - - - NAME can be the string - none; this will not use - any specific interface, and will not bring up an interface - on startup. This is not recommended, and only meant for - advanced users. - @@ -277,22 +250,6 @@ - - - - - - After bringing the network interface up, the program waits - for the interface to arrive in a running - state before proceeding. During this time, the kernel log - level will be lowered to reduce clutter on the system - console, alleviating any other plugins which might be - using the system console. This option sets the upper - limit of seconds to wait. The default is 2.5 seconds. - - - @@ -454,15 +411,15 @@ Run in debug mode, with a custom key, and do not use Zeroconf - to locate a server; connect directly to the IPv6 link-local - address fe80::aede:48ff:fe71:f6f2, port 4711, - using interface eth2: + to locate a server; connect directly to the IPv6 address + 2001:db8:f983:bd0b:30de:ae4a:71f2:f672, + port 4711, using interface eth2: -&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect fe80::aede:48ff:fe71:f6f2:4711 --interface eth2 +&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect 2001:db8:f983:bd0b:30de:ae4a:71f2:f672:4711 --interface eth2 === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2010-09-26 18:32:58 +0000 +++ plugins.d/password-prompt.c 2008-11-11 16:07:18 +0000 @@ -1,9 +1,9 @@ -/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */ +/* -*- coding: utf-8 -*- */ /* - * Password-prompt - Read a password from the terminal and print it + * Passprompt - Read a password from the terminal and print it * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 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 @@ -19,7 +19,8 @@ * along with this program. If not, see * . * - * Contact the authors at . + * Contact the authors at and + * . */ #define _GNU_SOURCE /* getline() */ @@ -32,47 +33,35 @@ #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 */ #include /* EXIT_SUCCESS, EXIT_FAILURE, - getenv() */ + getopt_long, getenv() */ #include /* fprintf(), stderr, getline(), - stdin, feof(), fputc() - */ -#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 /* strlen, rindex */ +#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 */ -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 = ""; -static void termination_handler(int signum){ - if(quit_now){ - return; - } - quit_now = 1; - signal_received = signum; +static void termination_handler(__attribute__((unused))int signum){ + quit_now = true; } int main(int argc, char **argv){ - ssize_t sret; - int ret; + ssize_t ret; size_t n; struct termios t_new, t_old; char *buffer = NULL; @@ -88,167 +77,111 @@ .doc = "Prefix shown before the prompt", .group = 2 }, { .name = "debug", .key = 128, .doc = "Debug mode", .group = 3 }, - /* - * 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 } }; - - error_t parse_opt (int key, char *arg, struct argp_state *state){ - errno = 0; - switch (key){ + + 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. */ + switch (key) { case 'p': prefix = arg; break; case 128: debug = true; break; - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - case '?': /* --help */ - argp_state_help(state, state->out_stream, - (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR) - & ~(unsigned int)ARGP_HELP_EXIT_OK); - case -3: /* --usage */ - argp_state_help(state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR); - case 'V': /* --version */ - fprintf(state->out_stream, "%s\n", argp_program_version); - exit(argp_err_exit_status); + case ARGP_KEY_ARG: + argp_usage (state); + break; + case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } - return errno; + return 0; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos password-prompt -- Read and" " output a password" }; - ret = argp_parse(&argp, argc, argv, - ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - error(0, errno, "argp_parse"); - return EX_OSERR; - case EINVAL: - return EX_USAGE; + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + return EXIT_FAILURE; } } - - if(debug){ + + if (debug){ fprintf(stderr, "Starting %s\n", argv[0]); } - if(debug){ + if (debug){ fprintf(stderr, "Storing current terminal attributes\n"); } - if(tcgetattr(STDIN_FILENO, &t_old) != 0){ - int e = errno; - error(0, errno, "tcgetattr"); - switch(e){ - case EBADF: - case ENOTTY: - return EX_UNAVAILABLE; - default: - return EX_OSERR; - } + if (tcgetattr(STDIN_FILENO, &t_old) != 0){ + perror("tcgetattr"); + return EXIT_FAILURE; } sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - /* Need to check if the handler is SIG_IGN before handling: - | [[info:libc:Initial Signal Actions]] | - | [[info:libc:Basic Signal Handling]] | - */ + sigaddset(&new_action.sa_mask, SIGINT); + sigaddset(&new_action.sa_mask, SIGHUP); + sigaddset(&new_action.sa_mask, SIGTERM); ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } - if(old_action.sa_handler != SIG_IGN){ + if (old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } - if(old_action.sa_handler != SIG_IGN){ + if (old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } - if(old_action.sa_handler != SIG_IGN){ + if (old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; + perror("sigaction"); + return EXIT_FAILURE; } } - if(debug){ + if (debug){ fprintf(stderr, "Removing echo flag from terminal attributes\n"); } t_new = t_old; - t_new.c_lflag &= ~(tcflag_t)ECHO; - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ - int e = errno; - error(0, errno, "tcsetattr-echo"); - switch(e){ - case EBADF: - case ENOTTY: - return EX_UNAVAILABLE; - case EINVAL: - default: - return EX_OSERR; - } + t_new.c_lflag &= ~ECHO; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ + perror("tcsetattr-echo"); + return EXIT_FAILURE; } - - if(debug){ + + if (debug){ fprintf(stderr, "Waiting for input from stdin \n"); } while(true){ - if(quit_now){ + if (quit_now){ if(debug){ fprintf(stderr, "Interrupted by signal, exiting.\n"); } @@ -260,134 +193,78 @@ fprintf(stderr, "%s ", prefix); } { - const char *cryptsource = getenv("CRYPTTAB_SOURCE"); - const char *crypttarget = getenv("CRYPTTAB_NAME"); - /* Before cryptsetup 1.1.0~rc2 */ - if(cryptsource == NULL){ - cryptsource = getenv("cryptsource"); - } - if(crypttarget == NULL){ - crypttarget = getenv("crypttarget"); - } - const char *const prompt1 = "Unlocking the disk"; - const char *const prompt2 = "Enter passphrase"; + const char *cryptsource = getenv("cryptsource"); + const char *crypttarget = getenv("crypttarget"); + const char *const prompt + = "Enter passphrase to unlock the disk"; if(cryptsource == NULL){ if(crypttarget == NULL){ - fprintf(stderr, "%s to unlock the disk: ", prompt2); + fprintf(stderr, "%s: ", prompt); } else { - fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget, - prompt2); + fprintf(stderr, "%s (%s): ", prompt, crypttarget); } } else { if(crypttarget == NULL){ - fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource, - prompt2); + fprintf(stderr, "%s %s: ", prompt, cryptsource); } else { - fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource, - crypttarget, prompt2); + fprintf(stderr, "%s %s (%s): ", prompt, cryptsource, + crypttarget); } } } - sret = getline(&buffer, &n, stdin); - if(sret > 0){ + ret = getline(&buffer, &n, stdin); + if (ret > 0){ status = EXIT_SUCCESS; /* Make n = data size instead of allocated buffer size */ - n = (size_t)sret; + n = (size_t)ret; /* Strip final newline */ - if(n > 0 and buffer[n-1] == '\n'){ + if(n>0 and buffer[n-1] == '\n'){ buffer[n-1] = '\0'; /* not strictly necessary */ n--; } size_t written = 0; while(written < n){ - sret = write(STDOUT_FILENO, buffer + written, n - written); - if(sret < 0){ - int e = errno; - error(0, errno, "write"); - switch(e){ - case EBADF: - case EFAULT: - case EINVAL: - case EFBIG: - case EIO: - case ENOSPC: - default: - status = EX_IOERR; - break; - case EINTR: - status = EXIT_FAILURE; - break; - } - break; - } - written += (size_t)sret; - } - sret = close(STDOUT_FILENO); - if(sret == -1){ - int e = errno; - error(0, errno, "close"); - switch(e){ - case EBADF: - status = EX_OSFILE; - break; - case EIO: - default: - status = EX_IOERR; - break; - } + ret = write(STDOUT_FILENO, buffer + written, n - written); + if(ret < 0){ + perror("write"); + status = EXIT_FAILURE; + break; + } + written += (size_t)ret; } break; } - if(sret < 0){ - int e = errno; - if(errno != EINTR and not feof(stdin)){ - error(0, errno, "getline"); - switch(e){ - case EBADF: - status = EX_UNAVAILABLE; - case EIO: - case EINVAL: - default: - status = EX_IOERR; - break; - } + if (ret < 0){ + if (errno != EINTR and not feof(stdin)){ + perror("getline"); + status = EXIT_FAILURE; break; } } - /* if(sret == 0), then the only sensible thing to do is to retry to + /* if(ret == 0), then the only sensible thing to do is to retry to read from stdin */ fputc('\n', stderr); if(debug and not quit_now){ - /* If quit_now is nonzero, we were interrupted by a signal, and + /* If quit_now is true, we were interrupted by a signal, and will print that later, so no need to show this too. */ fprintf(stderr, "getline() returned 0, retrying.\n"); } } - + free(buffer); - if(debug){ + if (debug){ fprintf(stderr, "Restoring terminal attributes\n"); } - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ - error(0, errno, "tcsetattr+echo"); - } - - if(quit_now){ - sigemptyset(&old_action.sa_mask); - old_action.sa_handler = SIG_DFL; - ret = sigaction(signal_received, &old_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - } - raise(signal_received); - } - - if(debug){ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ + perror("tcsetattr+echo"); + } + + if (debug){ fprintf(stderr, "%s is exiting with status %d\n", argv[0], status); } - if(status == EXIT_SUCCESS or status == EX_OK){ + if(status == EXIT_SUCCESS){ fputc('\n', stderr); } === modified file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2009-10-30 16:23:43 +0000 +++ plugins.d/password-prompt.xml 2008-10-04 01:55:56 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -183,8 +182,8 @@ ENVIRONMENT - CRYPTTAB_SOURCE - CRYPTTAB_NAME + cryptsource + crypttarget If set, these environment variables will be assumed to === removed file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2010-09-26 18:32:58 +0000 +++ plugins.d/plymouth.c 1970-01-01 00:00:00 +0000 @@ -1,431 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Usplash - Read a password from usplash and output it - * - * Copyright © 2010 Teddy Hogeborn - * Copyright © 2010 Björn Påhlsson - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * . - * - * Contact the authors at . - */ - -#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - kill(), SIG_IGN */ -#include /* bool, false, true */ -#include /* open(), O_RDONLY */ -#include /* and, or, not*/ -#include /* size_t, ssize_t, pid_t, struct - dirent, waitpid() */ -#include /* waitpid() */ -#include /* NULL */ -#include /* strchr(), memcmp() */ -#include /* asprintf(), perror(), fopen(), - fscanf() */ -#include /* close(), readlink(), read(), - fork(), setsid(), chdir(), dup2(), - STDERR_FILENO, execv(), access() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv() */ -#include /* scandir(), alphasort() */ -#include /* intmax_t, strtoumax(), SCNuMAX */ -#include /* struct stat, lstat() */ -#include /* EX_OSERR, EX_UNAVAILABLE */ -#include /* error() */ -#include /* TEMP_FAILURE_RETRY */ -#include /* argz_count(), argz_extract() */ - -sig_atomic_t interrupted_by_signal = 0; -const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid"; -const char plymouth_path[] = "/bin/plymouth"; -const char plymouthd_path[] = "/sbin/plymouthd"; -const char *plymouthd_default_argv[] = {"/sbin/plymouthd", - "--mode=boot", - "--attach-to-session", - "--pid-file=" - "/dev/.initramfs/" - "plymouth.pid", - NULL }; - -static void termination_handler(__attribute__((unused))int signum){ - if(interrupted_by_signal){ - return; - } - interrupted_by_signal = 1; -} - -/* Create prompt string */ -char *makeprompt(void){ - int ret = 0; - char *prompt; - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char prompt_start[] = "Enter passphrase to unlock the disk"; - - if(cryptsource == NULL){ - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s: ", prompt_start); - } else { - ret = asprintf(&prompt, "%s (%s): ", prompt_start, - crypttarget); - } - } else { - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); - } else { - ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, - cryptsource, crypttarget); - } - } - if(ret == -1){ - return NULL; - } - return prompt; -} - -void kill_and_wait(pid_t pid){ - TEMP_FAILURE_RETRY(kill(pid, SIGTERM)); - TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); -} - -bool become_a_daemon(void){ - int ret = setuid(geteuid()); - if(ret == -1){ - error(0, errno, "setuid"); - } - - setsid(); - ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - return false; - } - ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - return false; - } - return true; -} - -bool exec_and_wait(pid_t *pid_return, const char *path, - const char **argv, bool interruptable, - bool daemonize){ - int status; - int ret; - pid_t pid; - pid = fork(); - if(pid == -1){ - error(0, errno, "fork"); - return false; - } - if(pid == 0){ - /* Child */ - if(daemonize){ - if(not become_a_daemon()){ - _exit(EX_OSERR); - } - } - - char **new_argv = NULL; - char *tmp; - int i = 0; - for (; argv[i]!=(char *)NULL; i++){ - tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1)); - if (tmp == NULL){ - error(0, errno, "realloc"); - free(new_argv); - _exit(EX_OSERR); - } - new_argv = (char **)tmp; - new_argv[i] = strdup(argv[i]); - } - new_argv[i] = (char *) NULL; - - execv(path, (char *const *)new_argv); - error(0, errno, "execv"); - _exit(EXIT_FAILURE); - } - if(pid_return != NULL){ - *pid_return = pid; - } - do { - ret = waitpid(pid, &status, 0); - } while(ret == -1 and errno == EINTR - and ((not interrupted_by_signal) - or (not interruptable))); - if(interrupted_by_signal and interruptable){ - return false; - } - if(ret == -1){ - error(0, errno, "waitpid"); - return false; - } - if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){ - return true; - } - return false; -} - -int is_plymouth(const struct dirent *proc_entry){ - int ret; - { - uintmax_t maxvalue; - char *tmp; - errno = 0; - maxvalue = strtoumax(proc_entry->d_name, &tmp, 10); - - if(errno != 0 or *tmp != '\0' - or maxvalue != (uintmax_t)((pid_t)maxvalue)){ - return 0; - } - } - char exe_target[sizeof(plymouth_path)]; - char *exe_link; - ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - return 0; - } - - struct stat exe_stat; - ret = lstat(exe_link, &exe_stat); - if(ret == -1){ - free(exe_link); - if(errno != ENOENT){ - error(0, errno, "lstat"); - } - return 0; - } - - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - return 0; - } - - ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - if((sret != (ssize_t)sizeof(plymouth_path)-1) or - (memcmp(plymouth_path, exe_target, - sizeof(plymouth_path)-1) != 0)){ - return 0; - } - return 1; -} - -pid_t get_pid(void){ - int ret; - FILE *pidfile = fopen(plymouth_pid, "r"); - uintmax_t maxvalue = 0; - if(pidfile != NULL){ - ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue); - if(ret != 1){ - maxvalue = 0; - } - fclose(pidfile); - } - if(maxvalue == 0){ - struct dirent **direntries; - ret = scandir("/proc", &direntries, is_plymouth, alphasort); - sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue); - } - pid_t pid; - pid = (pid_t)maxvalue; - if((uintmax_t)pid == maxvalue){ - return pid; - } - - return 0; -} - -const char **getargv(pid_t pid){ - int cl_fd; - char *cmdline_filename; - ssize_t sret; - int ret; - - ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline", - (uintmax_t)pid); - if(ret == -1){ - error(0, errno, "asprintf"); - return NULL; - } - - /* Open /proc//cmdline */ - cl_fd = open(cmdline_filename, O_RDONLY); - free(cmdline_filename); - if(cl_fd == -1){ - error(0, errno, "open"); - return NULL; - } - - size_t cmdline_allocated = 0; - size_t cmdline_len = 0; - char *cmdline = NULL; - char *tmp; - const size_t blocksize = 1024; - do { - /* Allocate more space? */ - if(cmdline_len + blocksize > cmdline_allocated){ - tmp = realloc(cmdline, cmdline_allocated + blocksize); - if(tmp == NULL){ - error(0, errno, "realloc"); - free(cmdline); - close(cl_fd); - return NULL; - } - cmdline = tmp; - cmdline_allocated += blocksize; - } - - /* Read data */ - sret = read(cl_fd, cmdline + cmdline_len, - cmdline_allocated - cmdline_len); - if(sret == -1){ - error(0, errno, "read"); - free(cmdline); - close(cl_fd); - return NULL; - } - cmdline_len += (size_t)sret; - } while(sret != 0); - ret = close(cl_fd); - if(ret == -1){ - error(0, errno, "close"); - free(cmdline); - return NULL; - } - - /* we got cmdline and cmdline_len, ignore rest... */ - char **argv = malloc((argz_count(cmdline, cmdline_len) + 1) - * sizeof(char *)); /* Get number of args */ - if(argv == NULL){ - error(0, errno, "argv = malloc()"); - free(cmdline); - return NULL; - } - argz_extract(cmdline, cmdline_len, argv); /* Create argv */ - return (const char **)argv; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - char *prompt; - char *prompt_arg; - pid_t plymouth_command_pid; - int ret; - bool bret; - - /* test -x /bin/plymouth */ - ret = access(plymouth_path, X_OK); - if(ret == -1){ - /* Plymouth is probably not installed. Don't print an error - message, just exit. */ - exit(EX_UNAVAILABLE); - } - - { /* Add signal handlers */ - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; - *sig != 0; sig++){ - ret = sigaddset(&new_action.sa_mask, *sig); - if(ret == -1){ - error(EX_OSERR, errno, "sigaddset"); - } - ret = sigaction(*sig, NULL, &old_action); - if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(*sig, &new_action, NULL); - if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); - } - } - } - } - - /* plymouth --ping */ - bret = exec_and_wait(&plymouth_command_pid, plymouth_path, - (const char *[]) - { plymouth_path, "--ping", NULL }, - true, false); - if(not bret){ - if(interrupted_by_signal){ - kill_and_wait(plymouth_command_pid); - exit(EXIT_FAILURE); - } - /* Plymouth is probably not running. Don't print an error - message, just exit. */ - exit(EX_UNAVAILABLE); - } - - prompt = makeprompt(); - ret = asprintf(&prompt_arg, "--prompt=%s", prompt); - free(prompt); - if(ret == -1){ - error(EX_OSERR, errno, "asprintf"); - } - - /* plymouth ask-for-password --prompt="$prompt" */ - bret = exec_and_wait(&plymouth_command_pid, - plymouth_path, (const char *[]) - { plymouth_path, "ask-for-password", - prompt_arg, NULL }, - true, false); - free(prompt_arg); - if(bret){ - exit(EXIT_SUCCESS); - } - if(not interrupted_by_signal){ - /* exec_and_wait failed for some other reason */ - exit(EXIT_FAILURE); - } - kill_and_wait(plymouth_command_pid); - - const char **plymouthd_argv; - pid_t pid = get_pid(); - if(pid == 0){ - error(0, 0, "plymouthd pid not found"); - plymouthd_argv = plymouthd_default_argv; - } else { - plymouthd_argv = getargv(pid); - } - - bret = exec_and_wait(NULL, plymouth_path, (const char *[]) - { plymouth_path, "quit", NULL }, - false, false); - if(not bret){ - exit(EXIT_FAILURE); - } - bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, - false, true); - if(not bret){ - exit(EXIT_FAILURE); - } - exec_and_wait(NULL, plymouth_path, (const char *[]) - { plymouth_path, "show-splash", NULL }, - false, false); - exit(EXIT_FAILURE); -} === removed file 'plugins.d/plymouth.xml' --- plugins.d/plymouth.xml 2010-09-26 18:32:58 +0000 +++ plugins.d/plymouth.xml 1970-01-01 00:00:00 +0000 @@ -1,278 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use plymouth to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - plymouth8 - and outputs any given password to standard - output. If no plymouth8 - process can be found, this program will immediately exit with an - exit code indicating failure. - - - 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. - - - If this program is killed (presumably by - plugin-runner - 8mandos because some other - plugin provided the password), it cannot tell - plymouth8 - to abort requesting a password, because - plymouth - 8 does not support this. - Therefore, this program will then kill the - running plymouth - 8 process and start a - new one using the same command line - arguments as the old one was using. - - - - - 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. - - - - - ENVIRONMENT - - - cryptsource - crypttarget - - - If set, these environment variables will be assumed to - contain the source device name and the target device - mapper name, respectively, and will be shown as part of - the prompt. - - - These variables will normally be inherited from - plugin-runner - 8mandos, which will - normally have inherited them from - /scripts/local-top/cryptroot in the - initial RAM disk environment, which will - have set them from parsing kernel arguments and - /conf/conf.d/cryptroot (also in the - initial RAM disk environment), which in turn will have been - created when the initial RAM disk image was created by - /usr/share/initramfs-tools/hooks/cryptroot, by - extracting the information of the root file system from - /etc/crypttab. - - - This behavior is meant to exactly mirror the behavior of - askpass, the default password prompter. - - - - - - - - FILES - - - /bin/plymouth - - - This is the command run to retrieve a password from - plymouth - 8. - - - - - /proc - - - To find the running plymouth8 - , this directory will be searched for - numeric entries which will be assumed to be directories. - In all those directories, the exe and - cmdline entries will be used to - determine the name of the running binary, effective user - and group ID, and the command line - arguments. See proc5 - . - - - - - /sbin/plymouthd - - - This is the name of the binary which will be searched for - in the process list. See plymouth8 - . - - - - - - - - BUGS - - Killing the plymouth8 - daemon and starting a new one is ugly, but necessary as long as - it does not support aborting a password request. - - - - - 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 - - If this program is killed by a signal, it will kill the process - ID which at the start of this program was - determined to run plymouth8 - as root (see also ). There is a very - slight risk that, in the time between those events, that process - ID was freed and then taken up by another - process; the wrong process would then be killed. Now, this - program can only be killed by the user who started it; see - plugin-runner - 8mandos. This program - should therefore be started by a completely separate - non-privileged user, and no other programs should be allowed to - run as that special user. This means that it is not recommended - to use the user "nobody" to start this program, as other - possibly less trusted programs could be running as "nobody", and - they would then be able to kill this program, triggering the - killing of the process ID which may or may not - be plymouth - 8. - - - The only other 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 - - crypttab - 5, - plugin-runner - 8mandos, - proc - 5, - plymouth - 8 - - -
- - - - - === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2010-09-26 18:32:58 +0000 +++ plugins.d/splashy.c 2008-11-11 16:07:18 +0000 @@ -1,9 +1,9 @@ /* -*- coding: utf-8 -*- */ /* - * Splashy - Read a password from splashy and output it + * Passprompt - Read a password from splashy and output it * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 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 @@ -19,63 +19,45 @@ * along with this program. If not, see * . * - * Contact the authors at . + * Contact the authors at and + * . */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ +#define _GNU_SOURCE /* asprintf() */ #include /* sig_atomic_t, struct sigaction, sigemptyset(), sigaddset(), SIGINT, SIGHUP, SIGTERM, sigaction, SIG_IGN, kill(), SIGKILL */ #include /* NULL */ #include /* getenv() */ -#include /* asprintf() */ -#include /* EXIT_FAILURE, free(), +#include /* asprintf(), perror() */ +#include /* EXIT_FAILURE, free(), strtoul(), EXIT_SUCCESS */ #include /* pid_t, DIR, struct dirent, ssize_t */ #include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, strtoimax() */ #include /* struct stat, lstat(), S_ISLNK */ #include /* not, or, and */ #include /* readlink(), fork(), execl(), sleep(), dup2() STDERR_FILENO, - STDOUT_FILENO, _exit(), - pause() */ + STDOUT_FILENO, _exit() */ #include /* memcmp() */ -#include /* errno, EACCES, ENOTDIR, ELOOP, - ENOENT, ENAMETOOLONG, EMFILE, - ENFILE, ENOMEM, ENOEXEC, EINVAL, - E2BIG, EFAULT, EIO, ETXTBSY, - EISDIR, ELIBBAD, EPERM, EINTR, - ECHILD */ -#include /* error() */ +#include /* errno */ #include /* waitpid(), WIFEXITED(), WEXITSTATUS() */ -#include /* EX_OSERR, EX_OSFILE, - EX_UNAVAILABLE */ sig_atomic_t interrupted_by_signal = 0; -int signal_received; -static void termination_handler(int signum){ - if(interrupted_by_signal){ - return; - } +static void termination_handler(__attribute__((unused))int signum){ interrupted_by_signal = 1; - signal_received = signum; } int main(__attribute__((unused))int argc, __attribute__((unused))char **argv){ int ret = 0; + + /* Create prompt string */ char *prompt = NULL; - DIR *proc_dir = NULL; - pid_t splashy_pid = 0; - pid_t splashy_command_pid = 0; - int exitstatus = EXIT_FAILURE; - - /* Create prompt string */ { const char *const cryptsource = getenv("cryptsource"); const char *const crypttarget = getenv("crypttarget"); @@ -98,51 +80,27 @@ } } if(ret == -1){ - prompt = NULL; - exitstatus = EX_OSERR; - goto failure; + return EXIT_FAILURE; } } /* Find splashy process */ + pid_t splashy_pid = 0; { const char splashy_name[] = "/sbin/splashy"; - proc_dir = opendir("/proc"); + DIR *proc_dir = opendir("/proc"); if(proc_dir == NULL){ - int e = errno; - error(0, errno, "opendir"); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - case ENOENT: - default: - exitstatus = EX_OSFILE; - break; - case ENAMETOOLONG: - case EMFILE: - case ENFILE: - case ENOMEM: - exitstatus = EX_OSERR; - break; - } - goto failure; + free(prompt); + perror("opendir"); + return EXIT_FAILURE; } for(struct dirent *proc_ent = readdir(proc_dir); proc_ent != NULL; proc_ent = readdir(proc_dir)){ - pid_t pid; - { - intmax_t tmpmax; - char *tmp; - errno = 0; - tmpmax = strtoimax(proc_ent->d_name, &tmp, 10); - if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0' - or tmpmax != (pid_t)tmpmax){ - /* Not a process */ - continue; - } - pid = (pid_t)tmpmax; + pid_t pid = (pid_t) strtoul(proc_ent->d_name, NULL, 10); + if(pid == 0){ + /* Not a process */ + continue; } /* Find the executable name by doing readlink() on the /proc//exe link */ @@ -152,34 +110,21 @@ char *exe_link; ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); if(ret == -1){ - error(0, errno, "asprintf"); - exitstatus = EX_OSERR; - goto failure; + perror("asprintf"); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; } /* Check that it refers to a symlink owned by root:root */ struct stat exe_stat; ret = lstat(exe_link, &exe_stat); if(ret == -1){ - if(errno == ENOENT){ - free(exe_link); - continue; - } - int e = errno; - error(0, errno, "lstat"); + perror("lstat"); free(exe_link); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - default: - exitstatus = EX_OSFILE; - break; - case ENAMETOOLONG: - exitstatus = EX_OSERR; - break; - } - goto failure; + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; } if(not S_ISLNK(exe_stat.st_mode) or exe_stat.st_uid != 0 @@ -199,11 +144,10 @@ } } closedir(proc_dir); - proc_dir = NULL; } if(splashy_pid == 0){ - exitstatus = EX_UNAVAILABLE; - goto failure; + free(prompt); + return EXIT_FAILURE; } /* Set up the signal handler */ @@ -212,231 +156,136 @@ new_action = { .sa_handler = termination_handler, .sa_flags = 0 }; sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } + sigaddset(&new_action.sa_mask, SIGINT); + sigaddset(&new_action.sa_mask, SIGHUP); + sigaddset(&new_action.sa_mask, SIGTERM); ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } } - if(interrupted_by_signal){ - goto failure; - } - /* Fork off the splashy command to prompt for password */ - splashy_command_pid = fork(); - if(splashy_command_pid != 0 and interrupted_by_signal){ - goto failure; - } - if(splashy_command_pid == -1){ - error(0, errno, "fork"); - exitstatus = EX_OSERR; - goto failure; - } - /* Child */ - if(splashy_command_pid == 0){ - if(not interrupted_by_signal){ + pid_t splashy_command_pid = 0; + if(not interrupted_by_signal){ + splashy_command_pid = fork(); + if(splashy_command_pid == -1){ + if(not interrupted_by_signal){ + perror("fork"); + } + return EXIT_FAILURE; + } + /* Child */ + if(splashy_command_pid == 0){ const char splashy_command[] = "/sbin/splashy_update"; - execl(splashy_command, splashy_command, prompt, (char *)NULL); - int e = errno; - error(0, errno, "execl"); - switch(e){ - case EACCES: - case ENOENT: - case ENOEXEC: - case EINVAL: - _exit(EX_UNAVAILABLE); - case ENAMETOOLONG: - case E2BIG: - case ENOMEM: - case EFAULT: - case EIO: - case EMFILE: - case ENFILE: - case ETXTBSY: - default: - _exit(EX_OSERR); - case ENOTDIR: - case ELOOP: - case EISDIR: - case ELIBBAD: - case EPERM: - _exit(EX_OSFILE); + ret = execl(splashy_command, splashy_command, prompt, + (char *)NULL); + if(not interrupted_by_signal){ + perror("execl"); } + free(prompt); + _exit(EXIT_FAILURE); } - free(prompt); - _exit(EXIT_FAILURE); } /* Parent */ free(prompt); - prompt = NULL; - - if(interrupted_by_signal){ - goto failure; - } /* Wait for command to complete */ - { + if(not interrupted_by_signal and splashy_command_pid != 0){ int status; - do { - ret = waitpid(splashy_command_pid, &status, 0); - } while(ret == -1 and errno == EINTR - and not interrupted_by_signal); - if(interrupted_by_signal){ - goto failure; - } + ret = waitpid(splashy_command_pid, &status, 0); if(ret == -1){ - error(0, errno, "waitpid"); + if(errno != EINTR){ + perror("waitpid"); + } if(errno == ECHILD){ splashy_command_pid = 0; } } else { /* The child process has exited */ splashy_command_pid = 0; - if(WIFEXITED(status) and WEXITSTATUS(status) == 0){ + if(not interrupted_by_signal and WIFEXITED(status) + and WEXITSTATUS(status)==0){ return EXIT_SUCCESS; } } } - - failure: - - free(prompt); - - if(proc_dir != NULL){ - TEMP_FAILURE_RETRY(closedir(proc_dir)); - } - - if(splashy_command_pid != 0){ - TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM)); - - TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM)); - sleep(2); - while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){ - TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL)); - sleep(1); - } - pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork()); - if(new_splashy_pid == 0){ - /* Child; will become new splashy process */ - - /* Make the effective user ID (root) the only user ID instead of - the real user ID (_mandos) */ - ret = setuid(geteuid()); - if(ret == -1){ - error(0, errno, "setuid"); - } - - setsid(); - ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - } -/* if(fork() != 0){ */ -/* _exit(EXIT_SUCCESS); */ -/* } */ - ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - _exit(EX_OSERR); - } - - execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); - { - int e = errno; - error(0, errno, "execl"); - switch(e){ - case EACCES: - case ENOENT: - case ENOEXEC: - default: - _exit(EX_UNAVAILABLE); - case ENAMETOOLONG: - case E2BIG: - case ENOMEM: - _exit(EX_OSERR); - case ENOTDIR: - case ELOOP: - _exit(EX_OSFILE); - } - } - } - } - - if(interrupted_by_signal){ - struct sigaction signal_action; - sigemptyset(&signal_action.sa_mask); - signal_action.sa_handler = SIG_DFL; - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &signal_action, NULL)); - if(ret == -1){ - error(0, errno, "sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - error(0, errno, "raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return exitstatus; + kill(splashy_pid, SIGTERM); + if(interrupted_by_signal and splashy_command_pid != 0){ + kill(splashy_command_pid, SIGTERM); + } + sleep(2); + while(kill(splashy_pid, 0) == 0){ + kill(splashy_pid, SIGKILL); + sleep(1); + } + pid_t new_splashy_pid = fork(); + if(new_splashy_pid == 0){ + /* Child; will become new splashy process */ + + /* Make the effective user ID (root) the only user ID instead of + the real user ID (mandos) */ + ret = setuid(geteuid()); + if(ret == -1){ + perror("setuid"); + } + + setsid(); + ret = chdir("/"); +/* if(fork() != 0){ */ +/* _exit(EXIT_SUCCESS); */ +/* } */ + ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ + if(ret == -1){ + perror("dup2"); + _exit(EXIT_FAILURE); + } + + execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); + if(not interrupted_by_signal){ + perror("execl"); + } + _exit(EXIT_FAILURE); + } + + return EXIT_FAILURE; } === modified file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/splashy.xml 2008-10-04 03:11:39 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2010-09-26 18:32:58 +0000 +++ plugins.d/usplash.c 2008-11-11 16:07:18 +0000 @@ -1,9 +1,9 @@ /* -*- coding: utf-8 -*- */ /* - * Usplash - Read a password from usplash and output it + * Passprompt - Read a password from usplash and output it * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * Copyright © 2008 Teddy Hogeborn + * Copyright © 2008 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 @@ -19,10 +19,11 @@ * along with this program. If not, see * . * - * Contact the authors at . + * Contact the authors at and + * . */ -#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */ +#define _GNU_SOURCE /* asprintf() */ #include /* sig_atomic_t, struct sigaction, sigemptyset(), sigaddset(), SIGINT, SIGHUP, SIGTERM, sigaction(), @@ -31,290 +32,250 @@ #include /* open(), O_WRONLY, O_RDONLY */ #include /* and, or, not*/ #include /* errno, EINTR */ -#include #include /* size_t, ssize_t, pid_t, DIR, struct dirent */ #include /* NULL */ #include /* strlen(), memcmp() */ -#include /* asprintf()*/ +#include /* asprintf(), perror() */ #include /* close(), write(), readlink(), read(), STDOUT_FILENO, sleep(), fork(), setuid(), geteuid(), setsid(), chdir(), dup2(), STDERR_FILENO, execv() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv() */ +#include /* free(), EXIT_FAILURE, strtoul(), + realloc(), EXIT_SUCCESS, malloc(), + _exit() */ +#include /* getenv() */ #include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, strtoimax() */ #include /* struct stat, lstat(), S_ISLNK */ -#include /* EX_OSERR, EX_UNAVAILABLE */ -#include /* argz_count(), argz_extract() */ sig_atomic_t interrupted_by_signal = 0; -int signal_received; -const char usplash_name[] = "/sbin/usplash"; -static void termination_handler(int signum){ - if(interrupted_by_signal){ - return; - } +static void termination_handler(__attribute__((unused))int signum){ interrupted_by_signal = 1; - signal_received = signum; } -static bool usplash_write(int *fifo_fd_r, - const char *cmd, const char *arg){ +static bool usplash_write(const char *cmd, const char *arg){ /* - * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0" - * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0" + * usplash_write("TIMEOUT", "15") will write "TIMEOUT 15\0" + * usplash_write("PULSATE", NULL) will write "PULSATE\0" * SEE ALSO * usplash_write(8) */ int ret; - if(*fifo_fd_r == -1){ - ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY); - if(ret == -1){ + int fifo_fd; + do{ + fifo_fd = open("/dev/.initramfs/usplash_fifo", O_WRONLY); + if(fifo_fd == -1 and (errno != EINTR or interrupted_by_signal)){ return false; } - *fifo_fd_r = ret; - } + }while(fifo_fd == -1); const char *cmd_line; size_t cmd_line_len; char *cmd_line_alloc = NULL; if(arg == NULL){ cmd_line = cmd; - cmd_line_len = strlen(cmd) + 1; - } else { - do { + cmd_line_len = strlen(cmd); + }else{ + do{ ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg); - if(ret == -1){ + if(ret == -1 and (errno != EINTR or interrupted_by_signal)){ int e = errno; - TEMP_FAILURE_RETRY(close(*fifo_fd_r)); + close(fifo_fd); errno = e; return false; } - } while(ret == -1); + }while(ret == -1); cmd_line = cmd_line_alloc; cmd_line_len = (size_t)ret + 1; } size_t written = 0; - ssize_t sret = 0; - while(written < cmd_line_len){ - sret = write(*fifo_fd_r, cmd_line + written, - cmd_line_len - written); - if(sret == -1){ - int e = errno; - TEMP_FAILURE_RETRY(close(*fifo_fd_r)); - free(cmd_line_alloc); - errno = e; - return false; + while(not interrupted_by_signal and written < cmd_line_len){ + ret = write(fifo_fd, cmd_line + written, + cmd_line_len - written); + if(ret == -1){ + if(errno != EINTR or interrupted_by_signal){ + int e = errno; + close(fifo_fd); + free(cmd_line_alloc); + errno = e; + return false; + } else { + continue; + } } - written += (size_t)sret; + written += (size_t)ret; } free(cmd_line_alloc); - + do{ + ret = close(fifo_fd); + if(ret == -1 and (errno != EINTR or interrupted_by_signal)){ + return false; + } + }while(ret == -1); + if(interrupted_by_signal){ + return false; + } return true; } -/* Create prompt string */ -char *makeprompt(void){ - int ret = 0; - char *prompt; - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char prompt_start[] = "Enter passphrase to unlock the disk"; - - if(cryptsource == NULL){ - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s: ", prompt_start); - } else { - ret = asprintf(&prompt, "%s (%s): ", prompt_start, - crypttarget); - } - } else { - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); - } else { - ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, - cryptsource, crypttarget); - } - } - if(ret == -1){ - return NULL; - } - return prompt; -} - -pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){ - int ret = 0; - ssize_t sret = 0; +int main(__attribute__((unused))int argc, + __attribute__((unused))char **argv){ + int ret = 0; + ssize_t sret; + bool an_error_occured = false; + + /* Create prompt string */ + char *prompt = NULL; + { + const char *const cryptsource = getenv("cryptsource"); + const char *const crypttarget = getenv("crypttarget"); + const char prompt_start[] = "Enter passphrase to unlock the disk"; + + if(cryptsource == NULL){ + if(crypttarget == NULL){ + ret = asprintf(&prompt, "%s: ", prompt_start); + } else { + ret = asprintf(&prompt, "%s (%s): ", prompt_start, + crypttarget); + } + } else { + if(crypttarget == NULL){ + ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); + } else { + ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, + cryptsource, crypttarget); + } + } + if(ret == -1){ + return EXIT_FAILURE; + } + } + + /* Find usplash process */ + pid_t usplash_pid = 0; char *cmdline = NULL; size_t cmdline_len = 0; - DIR *proc_dir = opendir("/proc"); - if(proc_dir == NULL){ - error(0, errno, "opendir"); - return -1; - } - errno = 0; - for(struct dirent *proc_ent = readdir(proc_dir); - proc_ent != NULL; - proc_ent = readdir(proc_dir)){ - pid_t pid; - { - intmax_t tmpmax; - char *tmp; - tmpmax = strtoimax(proc_ent->d_name, &tmp, 10); - if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0' - or tmpmax != (pid_t)tmpmax){ + const char usplash_name[] = "/sbin/usplash"; + { + DIR *proc_dir = opendir("/proc"); + if(proc_dir == NULL){ + free(prompt); + perror("opendir"); + return EXIT_FAILURE; + } + for(struct dirent *proc_ent = readdir(proc_dir); + proc_ent != NULL; + proc_ent = readdir(proc_dir)){ + pid_t pid = (pid_t) strtoul(proc_ent->d_name, NULL, 10); + if(pid == 0){ /* Not a process */ - errno = 0; - continue; - } - pid = (pid_t)tmpmax; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - char exe_target[sizeof(usplash_name)]; - { - /* create file name string */ - char *exe_link; - ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - goto fail_find_usplash; - } - - /* Check that it refers to a symlink owned by root:root */ - struct stat exe_stat; - ret = lstat(exe_link, &exe_stat); - if(ret == -1){ - if(errno == ENOENT){ - free(exe_link); - continue; - } - error(0, errno, "lstat"); - free(exe_link); - goto fail_find_usplash; - } - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - continue; - } - - sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - } - /* Compare executable name */ - if((sret != ((ssize_t)sizeof(exe_target)-1)) - or (memcmp(usplash_name, exe_target, - sizeof(exe_target)-1) != 0)){ - /* Not it */ - continue; - } - /* Found usplash */ - /* Read and save the command line of usplash in "cmdline" */ - { - /* Open /proc//cmdline */ - int cl_fd; + continue; + } + /* Find the executable name by doing readlink() on the + /proc//exe link */ + char exe_target[sizeof(usplash_name)]; { - char *cmdline_filename; - ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", - proc_ent->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - goto fail_find_usplash; - } - cl_fd = open(cmdline_filename, O_RDONLY); - free(cmdline_filename); - if(cl_fd == -1){ - error(0, errno, "open"); - goto fail_find_usplash; + /* create file name string */ + char *exe_link; + ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); + if(ret == -1){ + perror("asprintf"); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + + /* Check that it refers to a symlink owned by root:root */ + struct stat exe_stat; + ret = lstat(exe_link, &exe_stat); + if(ret == -1){ + perror("lstat"); + free(exe_link); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + if(not S_ISLNK(exe_stat.st_mode) + or exe_stat.st_uid != 0 + or exe_stat.st_gid != 0){ + free(exe_link); + continue; + } + + sret = readlink(exe_link, exe_target, sizeof(exe_target)); + free(exe_link); + if(sret == -1){ + continue; } } - size_t cmdline_allocated = 0; - char *tmp; - const size_t blocksize = 1024; - do { - /* Allocate more space? */ - if(cmdline_len + blocksize > cmdline_allocated){ - tmp = realloc(cmdline, cmdline_allocated + blocksize); - if(tmp == NULL){ - error(0, errno, "realloc"); - close(cl_fd); - goto fail_find_usplash; + if((sret == ((ssize_t)sizeof(exe_target)-1)) + and (memcmp(usplash_name, exe_target, + sizeof(exe_target)-1) == 0)){ + usplash_pid = pid; + /* Read and save the command line of usplash in "cmdline" */ + { + /* Open /proc//cmdline */ + int cl_fd; + { + char *cmdline_filename; + ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", + proc_ent->d_name); + if(ret == -1){ + perror("asprintf"); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + cl_fd = open(cmdline_filename, O_RDONLY); + if(cl_fd == -1){ + perror("open"); + free(cmdline_filename); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + free(cmdline_filename); } - cmdline = tmp; - cmdline_allocated += blocksize; - } - /* Read data */ - sret = read(cl_fd, cmdline + cmdline_len, - cmdline_allocated - cmdline_len); - if(sret == -1){ - error(0, errno, "read"); + size_t cmdline_allocated = 0; + char *tmp; + const size_t blocksize = 1024; + do{ + if(cmdline_len + blocksize > cmdline_allocated){ + tmp = realloc(cmdline, cmdline_allocated + blocksize); + if(tmp == NULL){ + perror("realloc"); + free(cmdline); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + cmdline = tmp; + cmdline_allocated += blocksize; + } + sret = read(cl_fd, cmdline + cmdline_len, + cmdline_allocated - cmdline_len); + if(sret == -1){ + perror("read"); + free(cmdline); + free(prompt); + closedir(proc_dir); + return EXIT_FAILURE; + } + cmdline_len += (size_t)sret; + } while(sret != 0); close(cl_fd); - goto fail_find_usplash; } - cmdline_len += (size_t)sret; - } while(sret != 0); - ret = close(cl_fd); - if(ret == -1){ - error(0, errno, "close"); - goto fail_find_usplash; + break; } } - /* Close directory */ - ret = closedir(proc_dir); - if(ret == -1){ - error(0, errno, "closedir"); - goto fail_find_usplash; - } - /* Success */ - *cmdline_r = cmdline; - *cmdline_len_r = cmdline_len; - return pid; - } - - fail_find_usplash: - - free(cmdline); - if(proc_dir != NULL){ - int e = errno; closedir(proc_dir); - errno = e; - } - return 0; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - ssize_t sret; - int fifo_fd = -1; - int outfifo_fd = -1; - char *buf = NULL; - size_t buf_len = 0; - pid_t usplash_pid = -1; - bool usplash_accessed = false; - int status = EXIT_FAILURE; /* Default failure exit status */ - - char *prompt = makeprompt(); - if(prompt == NULL){ - status = EX_OSERR; - goto failure; - } - - /* Find usplash process */ - char *cmdline = NULL; - size_t cmdline_len = 0; - usplash_pid = find_usplash(&cmdline, &cmdline_len); + } if(usplash_pid == 0){ - status = EX_UNAVAILABLE; - goto failure; + free(prompt); + return EXIT_FAILURE; } /* Set up the signal handler */ @@ -323,259 +284,194 @@ new_action = { .sa_handler = termination_handler, .sa_flags = 0 }; sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } + sigaddset(&new_action.sa_mask, SIGINT); + sigaddset(&new_action.sa_mask, SIGHUP); + sigaddset(&new_action.sa_mask, SIGTERM); ret = sigaction(SIGINT, NULL, &old_action); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGINT, &new_action, NULL); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } ret = sigaction(SIGHUP, NULL, &old_action); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGHUP, &new_action, NULL); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } ret = sigaction(SIGTERM, NULL, &old_action); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } if(old_action.sa_handler != SIG_IGN){ ret = sigaction(SIGTERM, &new_action, NULL); if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; + perror("sigaction"); + free(prompt); + return EXIT_FAILURE; } } } - usplash_accessed = true; /* Write command to FIFO */ - if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - + if(not interrupted_by_signal){ + if(not usplash_write("TIMEOUT", "0") + and (errno != EINTR)){ + perror("usplash_write"); + an_error_occured = true; + } + } + if(not interrupted_by_signal and not an_error_occured){ + if(not usplash_write("INPUTQUIET", prompt) + and (errno != EINTR)){ + perror("usplash_write"); + an_error_occured = true; + } + } free(prompt); - prompt = NULL; - - /* Read reply from usplash */ - /* Open FIFO */ - outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); - if(outfifo_fd == -1){ - if(errno != EINTR){ - error(0, errno, "open"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - /* Read from FIFO */ - size_t buf_allocated = 0; - const size_t blocksize = 1024; - do { - /* Allocate more space */ - if(buf_len + blocksize > buf_allocated){ - char *tmp = realloc(buf, buf_allocated + blocksize); + + /* This is not really a loop; while() is used to be able to "break" + out of it; those breaks are marked "Big" */ + while(not interrupted_by_signal and not an_error_occured){ + char *buf = NULL; + size_t buf_len = 0; + + /* Open FIFO */ + int fifo_fd; + do{ + fifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); + if(fifo_fd == -1){ + if(errno != EINTR){ + perror("open"); + an_error_occured = true; + break; + } + if(interrupted_by_signal){ + break; + } + } + }while(fifo_fd == -1); + if(interrupted_by_signal or an_error_occured){ + break; /* Big */ + } + + /* Read from FIFO */ + 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){ + perror("realloc"); + an_error_occured = true; + break; + } + buf = tmp; + buf_allocated += blocksize; + } + do{ + sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len); + if(sret == -1){ + if(errno != EINTR){ + perror("read"); + an_error_occured = true; + break; + } + if(interrupted_by_signal){ + break; + } + } + }while(sret == -1); + if(interrupted_by_signal or an_error_occured){ + break; + } + + buf_len += (size_t)sret; + }while(sret != 0); + close(fifo_fd); + if(interrupted_by_signal or an_error_occured){ + break; /* Big */ + } + + if(not usplash_write("TIMEOUT", "15") + and (errno != EINTR)){ + perror("usplash_write"); + an_error_occured = true; + } + if(interrupted_by_signal or an_error_occured){ + break; /* Big */ + } + + /* Print password to stdout */ + size_t written = 0; + while(written < buf_len){ + do{ + sret = write(STDOUT_FILENO, buf + written, buf_len - written); + if(sret == -1){ + if(errno != EINTR){ + perror("write"); + an_error_occured = true; + break; + } + if(interrupted_by_signal){ + break; + } + } + }while(sret == -1); + if(interrupted_by_signal or an_error_occured){ + break; + } + written += (size_t)sret; + } + free(buf); + if(not interrupted_by_signal and not an_error_occured){ + free(cmdline); + return EXIT_SUCCESS; + } + break; /* Big */ + } /* end of non-loop while() */ + + /* If we got here, an error or interrupt must have happened */ + + /* Create argc and argv for new usplash*/ + int cmdline_argc = 0; + char **cmdline_argv = malloc(sizeof(char *)); + { + size_t position = 0; + while(position < cmdline_len){ + char **tmp = realloc(cmdline_argv, + (sizeof(char *) + * (size_t)(cmdline_argc + 2))); if(tmp == NULL){ - if(errno != EINTR){ - error(0, errno, "realloc"); - status = EX_OSERR; - } - goto failure; - } - buf = tmp; - buf_allocated += blocksize; - } - sret = read(outfifo_fd, buf + buf_len, - buf_allocated - buf_len); - if(sret == -1){ - if(errno != EINTR){ - error(0, errno, "read"); - status = EX_OSERR; - } - TEMP_FAILURE_RETRY(close(outfifo_fd)); - goto failure; - } - if(interrupted_by_signal){ - break; - } - - buf_len += (size_t)sret; - } while(sret != 0); - ret = close(outfifo_fd); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "close"); - status = EX_OSERR; - } - goto failure; - } - outfifo_fd = -1; - - if(interrupted_by_signal){ - goto failure; - } - - if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - ret = close(fifo_fd); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "close"); - status = EX_OSERR; - } - goto failure; - } - fifo_fd = -1; - - /* Print password to stdout */ - size_t written = 0; - while(written < buf_len){ - do { - sret = write(STDOUT_FILENO, buf + written, buf_len - written); - if(sret == -1){ - if(errno != EINTR){ - error(0, errno, "write"); - status = EX_OSERR; - } - goto failure; - } - } while(sret == -1); - - if(interrupted_by_signal){ - goto failure; - } - written += (size_t)sret; - } - free(buf); - buf = NULL; - - if(interrupted_by_signal){ - goto failure; - } - - free(cmdline); - return EXIT_SUCCESS; - - failure: - - free(buf); - - free(prompt); - - /* If usplash was never accessed, we can stop now */ - if(not usplash_accessed){ - return status; - } - - /* Close FIFO */ - if(fifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); - if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); - } - fifo_fd = -1; - } - - /* Close output FIFO */ - if(outfifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd)); - if(ret == -1){ - error(0, errno, "close"); - } - } - - /* Create argv for new usplash*/ - char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1) - * sizeof(char *)); /* Count args */ - if(cmdline_argv == NULL){ - error(0, errno, "malloc"); - return status; - } - argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */ - + perror("realloc"); + free(cmdline_argv); + return EXIT_FAILURE; + } + cmdline_argv = tmp; + cmdline_argv[cmdline_argc] = cmdline + position; + cmdline_argc++; + position += strlen(cmdline + position) + 1; + } + cmdline_argv[cmdline_argc] = NULL; + } /* Kill old usplash */ kill(usplash_pid, SIGTERM); sleep(2); @@ -583,76 +479,43 @@ kill(usplash_pid, SIGKILL); sleep(1); } - pid_t new_usplash_pid = fork(); if(new_usplash_pid == 0){ /* Child; will become new usplash process */ /* Make the effective user ID (root) the only user ID instead of - the real user ID (_mandos) */ + the real user ID (mandos) */ ret = setuid(geteuid()); if(ret == -1){ - error(0, errno, "setuid"); + perror("setuid"); } setsid(); ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - _exit(EX_OSERR); - } /* if(fork() != 0){ */ /* _exit(EXIT_SUCCESS); */ /* } */ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ - error(0, errno, "dup2"); - _exit(EX_OSERR); + perror("dup2"); + _exit(EXIT_FAILURE); } execv(usplash_name, cmdline_argv); if(not interrupted_by_signal){ - error(0, errno, "execv"); + perror("execv"); } free(cmdline); free(cmdline_argv); - _exit(EX_OSERR); + _exit(EXIT_FAILURE); } free(cmdline); free(cmdline_argv); sleep(2); - if(not usplash_write(&fifo_fd, "PULSATE", NULL)){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - } - } - - /* Close FIFO (again) */ - if(fifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); - if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); - } - fifo_fd = -1; - } - - if(interrupted_by_signal){ - struct sigaction signal_action = { .sa_handler = SIG_DFL }; - sigemptyset(&signal_action.sa_mask); - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &signal_action, NULL)); - if(ret == -1){ - error(0, errno, "sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - error(0, errno, "raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return status; + if(not usplash_write("PULSATE", NULL) + and (errno != EINTR)){ + perror("usplash_write"); + } + + return EXIT_FAILURE; } === modified file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/usplash.xml 2008-10-04 03:11:39 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson