=== removed directory '.bzr-builddeb' === removed file '.bzr-builddeb/default.conf' --- .bzr-builddeb/default.conf 2008-09-17 00:34:09 +0000 +++ .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -[BUILDDEB] -split = True === modified file '.bzrignore' --- .bzrignore 2008-10-03 09:32:30 +0000 +++ .bzrignore 2008-08-27 01:18:25 +0000 @@ -1,14 +1,8 @@ *.5 *.8 *.8mandos +plugin-runner +plugins.d/password-prompt +plugins.d/password-request confdir -debian/po/messages.mo -debian/po/templates.pot keydir -man -plugin-runner -plugins.d/askpass-fifo -plugins.d/mandos-client -plugins.d/password-prompt -plugins.d/splashy -plugins.d/usplash === removed file 'INSTALL' --- INSTALL 2008-10-28 18:00:20 +0000 +++ INSTALL 1970-01-01 00:00:00 +0000 @@ -1,132 +0,0 @@ --*- org -*- - -* Prerequisites - -** Operating System - - Debian 5.0 "lenny" or Ubuntu 8.04 "Hardy Heron". - - This is mostly for the support scripts which make sure that the - client is installed and started in the initial RAM disk environment - and that the initrd.img file is automatically made unreadable. The - server and client programs themselves *could* be run in other - distributions, but they *are* specific to GNU/Linux systems, and - are not intended to be portable to other Unixes. - -** Libraries - - The following libraries and packages are needed. (It is possible - that it might work with older versions of some of these, but these - versions are confirmed to work. Newer versions are almost - certainly OK.) - -*** Documentation - These are required to build the manual pages for both the server - and client: - - + DocBook 4.5 http://www.docbook.org/ - Note: DocBook 5.0 is not compatible. - + DocBook XSL stylesheets 1.71.0 - http://wiki.docbook.org/topic/DocBookXslStylesheets - - Package names: - docbook docbook-xsl - - To build just the documentation, run the command "make doc". Then - the manual page "mandos.8", for example, can be read by running - "man -l mandos.8". - -*** Mandos Server - + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ - + Avahi 0.6.16 http://www.avahi.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 - - Strongly recommended: - + fping 2.4b2-to-ipv6 http://www.fping.com/ - - Package names: - python-gnutls avahi-daemon python python-avahi python-dbus - python-ctypes - -*** Mandos Client - + initramfs-tools 0.85i - http://packages.qa.debian.org/i/initramfs-tools.html - + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ - + Avahi 0.6.16 http://www.avahi.org/ - + GnuPG 1.4.9 http://www.gnupg.org/ - + GPGME 1.1.6 http://www.gnupg.org/related_software/gpgme/ - - Package names: - initramfs-tools libgnutls-dev libavahi-core-dev gnupg - libgpgme11-dev - -* Installing the Mandos server - - 1. Do "make doc". - - 2. On the computer to run as a Mandos server, run the following - command: - For Debian: su -c 'make install-server' - For Ubuntu: sudo make install-server - - (This creates a configuration without any clients configured; you - need an actually configured client to do that; see below.) - -* Installing the Mandos client. - - 1. Do "make all doc". - - 2. On the computer to run as a Mandos client, run the following - command: - For Debian: su -c 'make install-client' - For Ubuntu: sudo make install-client - - This will also create an OpenPGP key, which will take some time - and entropy, so be patient. - - 3. Run the following command: - For Debian: su -c 'mandos-keygen --password' - For Ubuntu: sudo mandos-keygen --password - - When prompted, enter the password/passphrase for the encrypted - root file system on this client computer. The command will - output a section of text, starting with a [section header]. Copy - and append this to the file "/etc/mandos/clients.conf" *on the - server computer*. - - 4. Configure the client to use the correct network interface. The - default is "eth0", and if this needs to be adjusted, it will be - necessary to edit /etc/mandos/plugin-runner.conf to uncomment and - change the line there. If that file is changed, the initrd.img - file must be updated, possibly using the following command: - - # update-initramfs -k all -u - - 5. On the server computer, start the server by running the command - For Debian: su -c 'invoke-rc.d mandos start' - For Ubuntu: sudo invoke-rc.d mandos start - - At this point, it is possible to verify that the correct password - will be received by the client by running the command: - - # /usr/lib/mandos/plugins.d/mandos-client \ - --pubkey=/etc/keys/mandos/pubkey.txt \ - --seckey=/etc/keys/mandos/seckey.txt; echo - - This command should retrieve the password from the server, - decrypt it, and output it to standard output. - - After this, the client computer should be able to reboot without - needing a password entered on the console, as long as it does not - take more than an hour to reboot. - -* Further customizations - - You may want to tighten or loosen the timeouts in the server - configuration files; see mandos.conf(5) and mandos-clients.conf(5). - If IPsec is not used, it is suggested that a more cryptographically - secure checker program is used and configured, since without IPsec - ping packets can be faked. === modified file 'Makefile' --- Makefile 2009-01-10 03:26:15 +0000 +++ Makefile 2008-08-25 06:44:13 +0000 @@ -4,36 +4,21 @@ -Wunsafe-loop-optimizations -Wpointer-arith \ -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \ -Wconversion -Wstrict-prototypes -Wold-style-definition \ - -Wpacked -Wnested-externs -Winline -Wvolatile-register-var -# -Wunreachable-code -#DEBUG=-ggdb3 + -Wpacked -Wnested-externs -Wunreachable-code -Winline \ + -Wvolatile-register-var +DEBUG=-ggdb3 # For info about _FORTIFY_SOURCE, see # -FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC -fPIE -LINK_FORTIFY_LD=-z relro -fPIE -LINK_FORTIFY=-pie +FORTIFY=-D_FORTIFY_SOURCE=2 # -fstack-protector-all #COVERAGE=--coverage OPTIMIZE=-Os LANGUAGE=-std=gnu99 -htmldir=man -version=1.0.3 -SED=sed - -## Use these settings for a traditional /usr/local install -# PREFIX=$(DESTDIR)/usr/local -# CONFDIR=$(DESTDIR)/etc/mandos -# KEYDIR=$(DESTDIR)/etc/mandos/keys -# MANDIR=$(PREFIX)/man -# INITRAMFSTOOLS=$(DESTDIR)/etc/initramfs-tools -## - -## These settings are for a package-type install +# PREFIX=/usr/local PREFIX=$(DESTDIR)/usr +# CONFDIR=/usr/local/lib/mandos CONFDIR=$(DESTDIR)/etc/mandos -KEYDIR=$(DESTDIR)/etc/keys/mandos -MANDIR=$(PREFIX)/share/man -INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools -## +# MANDIR=/usr/local/man +MANDIR=$(DESTDIR)/usr/share/man GNUTLS_CFLAGS=$(shell libgnutls-config --cflags) GNUTLS_LIBS=$(shell libgnutls-config --libs) @@ -44,11 +29,10 @@ # 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)) + $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) +LDFLAGS=$(COVERAGE) -# Commands to format a DocBook document into a manual page +# Commands to format a DocBook refentry document into a manual page DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \ --param man.charmap.use.subset 0 \ --param make.year.ranges 1 \ @@ -58,194 +42,94 @@ /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \ $(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=xsltproc --nonet --xinclude \ - --param make.year.ranges 1 \ - --param make.single.year.ranges 1 \ - --param man.output.quietly 1 \ - --param man.authors.section.enabled 0 \ - --param citerefentry.link 1 \ - --output $@ \ - /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \ - $<; $(HTMLPOST) $@ -# Fix citerefentry links -HTMLPOST=$(SED) --in-place \ - --expression='s/\(\)\([^<]*\)\(<\/span>(\)\([^)]*\)\()<\/span><\/a>\)/\1\3.\5\2\3\4\5\6/g' - -PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \ - plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo -CPROGS=plugin-runner $(PLUGINS) -PROGS=mandos mandos-keygen mandos-list $(CPROGS) +# DocBook-to-man post-processing to fix a \n escape bug +MANPOST=sed --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g' + +PLUGINS=plugins.d/password-prompt plugins.d/password-request +PROGS=plugin-runner $(PLUGINS) DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \ - plugins.d/mandos-client.8mandos \ + plugins.d/password-request.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)) - -objects=$(addsuffix .o,$(CPROGS)) - -all: $(PROGS) mandos.lsm + mandos-clients.conf.5 + +objects=$(addsuffix .o,$(PROGS)) + +all: $(PROGS) doc: $(DOCS) -html: $(htmldocs) - -%.5: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.5.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -%.8: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.8.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -%.8mandos: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.8mandos.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -mandos.8: mandos.xml common.ent mandos-options.xml overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos.8.xhtml: mandos.xml common.ent mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -mandos-keygen.8: mandos-keygen.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos-keygen.8.xhtml: mandos-keygen.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -mandos.conf.5: mandos.conf.xml common.ent mandos-options.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos.conf.5.xhtml: mandos.conf.xml common.ent mandos-options.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -plugin-runner.8mandos: plugin-runner.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -plugin-runner.8mandos.xhtml: plugin-runner.xml common.ent \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \ - common.ent \ - mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOMAN) -plugins.d/mandos-client.8mandos.xhtml: plugins.d/mandos-client.xml \ - common.ent \ - mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -# Update all these files with version number $(version) -common.ent: Makefile - $(SED) --in-place \ - --expression='s/^\($$/\1$(version)"/' \ - $@ - -mandos: Makefile - $(SED) --in-place \ - --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@ - -mandos-keygen: Makefile - $(SED) --in-place \ - --expression='s/^\(VERSION="\)[^"]*"$$/\1$(version)"/' \ - $@ - -mandos-list: Makefile - $(SED) --in-place \ - --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@ - -mandos.lsm: Makefile - $(SED) --in-place \ - --expression='s/^\(Version:\).*/\1\t$(version)/' \ - $@ - $(SED) --in-place \ - --expression='s/^\(Entered-date:\).*/\1\t$(shell date --rfc-3339=date --reference=Makefile)/' \ - $@ - $(SED) --in-place \ - --expression='s/\(mandos_\)[0-9.]\+\(\.orig\.tar\.gz\)/\1$(version)\2/' \ - $@ - -plugins.d/mandos-client: plugins.d/mandos-client.o +%.5: %.xml + $(DOCBOOKTOMAN) + +%.8: %.xml + $(DOCBOOKTOMAN) + +%.8mandos: %.xml + $(DOCBOOKTOMAN) + +mandos.8: mandos.xml mandos-options.xml + $(DOCBOOKTOMAN) + +mandos.conf.5: mandos.conf.xml mandos-options.xml + $(DOCBOOKTOMAN) + +plugins.d/password-request: plugins.d/password-request.o $(LINK.o) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) \ $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ -.PHONY : all doc html clean distclean run-client run-server install \ +.PHONY : all doc clean distclean run-client run-server install \ install-server install-client uninstall uninstall-server \ uninstall-client purge purge-server purge-client clean: - -rm --force $(CPROGS) $(objects) $(htmldocs) $(DOCS) core + -rm --force $(PROGS) $(objects) $(DOCS) core distclean: clean mostlyclean: clean maintainer-clean: clean -rm --force --recursive keydir confdir -check: all +check: ./mandos --check -# Run the client with a local config and key -run-client: all keydir/seckey.txt keydir/pubkey.txt +# Run the server with a local key +run-client: all keydir/seckey.txt keydir/pubkey.txt \ + keydir/secring.gpg keydir/pubring.gpg ./plugin-runner --plugin-dir=plugins.d \ - --config-file=plugin-runner.conf \ - --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt \ - $(CLIENTARGS) + --options-for=password-request:--keydir=keydir # Used by run-client +keydir/secring.gpg: keydir/seckey.txt + gpg --homedir $(dir $<) --import $^ +keydir/pubring.gpg: keydir/pubkey.txt + gpg --homedir $(dir $<) --import $^ keydir/seckey.txt keydir/pubkey.txt: mandos-keygen install --directory keydir ./mandos-keygen --dir keydir --force # Run the server with a local config run-server: confdir/mandos.conf confdir/clients.conf - ./mandos --debug --configdir=confdir $(SERVERARGS) + ./mandos --debug --configdir=confdir # Used by run-server confdir/mandos.conf: mandos.conf install --directory confdir - install --mode=u=rw,go=r $^ $@ + install $^ $@ confdir/clients.conf: clients.conf keydir/seckey.txt install --directory confdir - install --mode=u=rw $< $@ + install clients.conf $@ # Add a client password ./mandos-keygen --dir keydir --password >> $@ -install: install-server install-client-nokey - -install-html: html - install --directory $(htmldir) - install --mode=u=rw,go=r --target-directory=$(htmldir) \ - $(htmldocs) +install: install-server install-client install-server: doc - install --directory $(CONFDIR) - install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos - install --mode=u=rw,go=r --target-directory=$(CONFDIR) \ - mandos.conf - install --mode=u=rw --target-directory=$(CONFDIR) \ + install --directory --parents $(CONFDIR) $(MANDIR)/man5 \ + $(MANDIR)/man8 + install --mode=0755 mandos $(PREFIX)/sbin/mandos + install --mode=0644 --target-directory=$(CONFDIR) mandos.conf + install --mode=0640 --target-directory=$(CONFDIR) \ clients.conf - install --mode=u=rwx,go=rx init.d-mandos \ - $(DESTDIR)/etc/init.d/mandos - install --mode=u=rw,go=r default-mandos \ - $(DESTDIR)/etc/default/mandos - if [ -z $(DESTDIR) ]; then \ - update-rc.d mandos defaults 25 15;\ - fi gzip --best --to-stdout mandos.8 \ > $(MANDIR)/man8/mandos.8.gz gzip --best --to-stdout mandos.conf.5 \ @@ -253,108 +137,72 @@ gzip --best --to-stdout mandos-clients.conf.5 \ > $(MANDIR)/man5/mandos-clients.conf.5.gz -install-client-nokey: all doc - install --directory $(PREFIX)/lib/mandos $(CONFDIR) - install --directory --mode=u=rwx $(KEYDIR) \ - $(PREFIX)/lib/mandos/plugins.d - if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ - install --mode=u=rwx \ - --directory "$(CONFDIR)/plugins.d"; \ - fi - install --mode=u=rwx,go=rx \ - --target-directory=$(PREFIX)/lib/mandos plugin-runner - install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ +install-client: all doc /usr/share/initramfs-tools/hooks/. + install --directory --parents $(PREFIX)/lib/mandos \ + $(CONFDIR) $(MANDIR)/man8 + install --directory --mode=0700 $(PREFIX)/lib/mandos/plugins.d + chmod u=rwx,g=,o= $(PREFIX)/lib/mandos/plugins.d + install --mode=0755 --target-directory=$(PREFIX)/lib/mandos \ + plugin-runner + install --mode=0755 --target-directory=$(PREFIX)/sbin \ mandos-keygen - install --mode=u=rwx,go=rx \ + install --mode=0755 \ --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/password-prompt - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/mandos-client - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/usplash - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/splashy - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/askpass-fifo + install --mode=4755 \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ + plugins.d/password-request install initramfs-tools-hook \ - $(INITRAMFSTOOLS)/hooks/mandos - install --mode=u=rw,go=r initramfs-tools-hook-conf \ - $(INITRAMFSTOOLS)/conf-hooks.d/mandos + /usr/share/initramfs-tools/hooks/mandos + install initramfs-tools-hook-conf \ + /usr/share/initramfs-tools/conf-hooks.d/mandos install initramfs-tools-script \ - $(INITRAMFSTOOLS)/scripts/local-top/mandos - install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR) + /usr/share/initramfs-tools/scripts/local-top/mandos gzip --best --to-stdout mandos-keygen.8 \ > $(MANDIR)/man8/mandos-keygen.8.gz gzip --best --to-stdout plugin-runner.8mandos \ > $(MANDIR)/man8/plugin-runner.8mandos.gz gzip --best --to-stdout plugins.d/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/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 - -install-client: install-client-nokey -# Post-installation stuff - -$(PREFIX)/sbin/mandos-keygen --dir "$(KEYDIR)" + gzip --best --to-stdout plugins.d/password-request.8mandos \ + > $(MANDIR)/man8/password-request.8mandos.gz + -$(PREFIX)/sbin/mandos-keygen update-initramfs -k all -u - echo "Now run mandos-keygen --password --dir $(KEYDIR)" uninstall: uninstall-server uninstall-client -uninstall-server: +uninstall-server: $(PREFIX)/sbin/mandos -rm --force $(PREFIX)/sbin/mandos \ $(MANDIR)/man8/mandos.8.gz \ $(MANDIR)/man5/mandos.conf.5.gz \ $(MANDIR)/man5/mandos-clients.conf.5.gz - update-rc.d -f mandos remove -rmdir $(CONFDIR) uninstall-client: # Refuse to uninstall client if /etc/crypttab is explicitly configured # to use it. ! grep --regexp='^ *[^ #].*keyscript=[^,=]*/mandos/' \ - $(DESTDIR)/etc/crypttab + /etc/crypttab -rm --force $(PREFIX)/sbin/mandos-keygen \ $(PREFIX)/lib/mandos/plugin-runner \ $(PREFIX)/lib/mandos/plugins.d/password-prompt \ - $(PREFIX)/lib/mandos/plugins.d/mandos-client \ - $(PREFIX)/lib/mandos/plugins.d/usplash \ - $(PREFIX)/lib/mandos/plugins.d/splashy \ - $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \ - $(INITRAMFSTOOLS)/hooks/mandos \ - $(INITRAMFSTOOLS)/conf-hooks.d/mandos \ - $(INITRAMFSTOOLS)/scripts/local-top/mandos \ + $(PREFIX)/lib/mandos/plugins.d/password-request \ + /usr/share/initramfs-tools/hooks/mandos \ + /usr/share/initramfs-tools/conf-hooks.d/mandos \ $(MANDIR)/man8/plugin-runner.8mandos.gz \ $(MANDIR)/man8/mandos-keygen.8.gz \ $(MANDIR)/man8/password-prompt.8mandos.gz \ - $(MANDIR)/man8/usplash.8mandos.gz \ - $(MANDIR)/man8/splashy.8mandos.gz \ - $(MANDIR)/man8/askpass-fifo.8mandos.gz \ - $(MANDIR)/man8/mandos-client.8mandos.gz + $(MANDIR)/man8/password-request.8mandos.gz -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \ - $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR) + $(PREFIX)/lib/mandos $(CONFDIR) update-initramfs -k all -u purge: purge-server purge-client purge-server: uninstall-server - -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf \ - $(DESTDIR)/etc/default/mandos \ - $(DESTDIR)/etc/init.d/mandos \ - $(DESTDIR)/var/run/mandos.pid + -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf -rmdir $(CONFDIR) purge-client: uninstall-client - -shred --remove $(KEYDIR)/seckey.txt - -rm --force $(CONFDIR)/plugin-runner.conf \ - $(KEYDIR)/pubkey.txt $(KEYDIR)/seckey.txt - -rmdir $(KEYDIR) $(CONFDIR)/plugins.d $(CONFDIR) + -rm --force $(CONFDIR)/seckey.txt $(CONFDIR)/pubkey.txt + -rmdir $(CONFDIR) $(CONFDIR)/plugins.d === removed file 'NEWS' --- NEWS 2009-01-06 02:42:53 +0000 +++ NEWS 1970-01-01 00:00:00 +0000 @@ -1,35 +0,0 @@ -This NEWS file records noteworthy changes, very tersely. -See the manual for detailed information. - -Version 1.0.3 (2009-01-06) -* Server -** Now tries to change to user and group "_mandos" before falling back - to trying the old values "mandos", "nobody:nogroup", and "65534". -** Now does not abort on startup even if no clients are defined in - clients.conf. - -* Client -** Plugins named "*.dpkg-bak" are now ignored. -** Hopefully fixed compilation failure on some architectures where the - C compiler does not recognize the "-z" option as a linker option. - -Version 1.0.2 (2008-10-17) -* mandos-keygen now signs the encrypted key blobs. This signature is - not currently verified by mandos-client, but this may change in the - future. - -Version 1.0.1 (2008-10-07) -* Server -** Expand environment variables and ~user in clients.conf's "secfile" - The "secfile" option in /etc/mandos/clients.conf now expands - "~user/foo" and "$ENVVAR" strings. - -* Client (plugin-runner, plugins, etc.) -** Manual pages for the usplash, splashy, and askpass-fifo plugins. - All plugins now have man pages. -** More secure compilation and linking flags. - All programs are now compiled with "-fstack-protector-all -fPIE - -pie", and linked using "-z relro -pie" for additional security. - -* There is now a "NEWS" file (this one), giving a history of - noteworthy changes. === removed file 'README' --- README 2009-01-12 22:02:33 +0000 +++ README 1970-01-01 00:00:00 +0000 @@ -1,178 +0,0 @@ --*- org -*- - -* Mandos - - Have your cake and eat it too! - - You know how it is. You’ve heard of it happening. The Man comes - and takes away your servers, your friends’ servers, the servers of - everybody in the same hosting facility. The servers of their - neighbors, and their neighbors’ friends. The servers of people who - owe them money. And like *that*, they’re gone. And you doubt - you’ll ever see them again. - - That is why your servers have encrypted root file systems. However, - there’s a downside. There’s no going around it: rebooting is a - pain. Dragging out that rarely-used keyboard and screen and - unraveling cables behind your servers to plug them in to type in - that password is messy, especially if you have many servers. There - are some people who do clever things like using serial line consoles - and daisy-chain it to the next server, and keep all the servers - connected in a ring with serial cables, which will work, if your - servers are physically close enough. There are also other - out-of-band management solutions, but with *all* these, you still - have to be on hand and manually type in the password at boot time. - Otherwise the server just sits there, waiting for a password. - - Wouldn’t it be great if you could have the security of encrypted - root file systems and still have servers that could boot up - automatically if there was a short power outage while you were - asleep? That you could reboot at will, without having someone run - over to the server to type in the password? - - Well, with Mandos, you (almost) can! The gain in convenience will - only be offset by a small loss in security. The setup is as - follows: - - The server will still have its encrypted root file system. The - password to this file system will be stored on another computer - (henceforth known as the Mandos server) on the same local network. - The password will *not* be stored in plaintext, but encrypted with - OpenPGP. To decrypt this password, a key is needed. This key (the - Mandos client key) will not be stored there, but back on the - original server (henceforth known as the Mandos client) in the - initial RAM disk image. Oh, and all network Mandos client/server - communications will be encrypted, using TLS (SSL). - - So, at boot time, the Mandos client will ask for its encrypted data - over the network, decrypt it to get the password, use it to decrypt - the root file, and continue booting. - - Now, of course the initial RAM disk image is not on the encrypted - root file system, so anyone who had physical access could take the - Mandos client computer offline and read the disk with their own - tools to get the authentication keys used by a client. *But*, by - then the Mandos server should notice that the original server has - been offline for too long, and will no longer give out the encrypted - key. The timing here is the only real weak point, and the method, - frequency and timeout of the server’s checking can be adjusted to - any desired level of paranoia - - (The encrypted keys on the Mandos server is on its normal file - system, so those are safe, provided the root file system of *that* - server is encrypted.) - -* FAQ - couldn’t the security be defeated by... - -** Grabbing the Mandos client key from the initrd *really quickly*? - This, as mentioned above, is the only real weak point. But if you - set the timing values tight enough, this will be really difficult - to do. An attacker would have to physically disassemble the client - computer, extract the key from the initial RAM disk image, and then - connect to a *still online* Mandos server to get the encrypted key, - and do all this *before* the Mandos server timeout kicks in and the - Mandos server refuses to give out the key to anyone. - - Now, as the typical procedure seems to be to barge in and turn off - and grab *all* computers, to maybe look at them months later, this - is not likely. If someone does that, the whole system *will* lock - itself up completely, since Mandos servers are no longer running. - - For sophisticated attackers who *could* do the clever thing, *and* - had physical access to the server for enough time, it would be - simpler to get a key for an encrypted file system by using hardware - memory scanners and reading it right off the memory bus. - -** Replay attacks? - Nope, the network stuff is all done over TLS, which provides - protection against that. - -** Man-in-the-middle? - No. The server only gives out the passwords to clients which have - *in the TLS handshake* proven that they do indeed hold the OpenPGP - private key corresponding to that client. - -** Physically grabbing the Mandos server computer? - You could protect *that* computer the old-fashioned way, with a - must-type-in-the-password-at-boot method. Or you could have two - computers be the Mandos server for each other. - - Multiple Mandos servers can coexist on a network without any - trouble. They do not clash, and clients will try all available - servers. This means that if just one reboots then the other can - bring it back up, but if both reboots at the same time they will - stay down until someone types in the password on one of them. - -** Faking ping replies? - The default for the server is to use "fping", the replies to which - could be faked to eliminate the timeout. But this could easily be - changed to any shell command, with any security measures you like. - It could, for instance, be changed to an SSH command with strict - keychecking, which could not be faked. Or IPsec could be used for - the ping packets, making them secure. - -* Security Summary - So, in summary: The only weakness in the Mandos system is from - people who have: - 1. The power to come in and physically take your servers, *and* - 2. The cunning and patience to do it carefully, one at a time, and - *quickly*, faking Mandos client/server responses for each one - before the timeout. - - While there are some who may be threatened by people who have *both* - these attributes, they do not, probably, constitute the majority. - - If you *do* face such opponents, you must figure that they could - just as well open your servers and read the file system keys right - off the memory by running wires to the memory bus. - - What Mandos is designed to protect against is *not* such determined, - focused, and competent attacks, but against the early morning knock - on your door and the sudden absence of all the servers in your - server room. Which it does nicely. - -* The Plugin System - 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. 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. - - Three additional plugins are provided: - * usplash(8mandos) - This prompts for a password when using usplash(8). - * splashy(8mandos) - This prompts for a password when using splashy(8). - * askpass-fifo(8mandos) - To provide compatibility with the "askpass" program from - cryptsetup, this plugin listens to the same FIFO as askpass would - do. - - More plugins 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,2009 Teddy Hogeborn - Copyright © 2008,2009 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 - . === modified file 'TODO' --- TODO 2009-01-12 22:02:33 +0000 +++ TODO 2008-08-29 05:53:59 +0000 @@ -1,68 +1,131 @@ -*- org -*- -* mandos-client -** TODO [#B] Temporarily lower kernel log level +* [#A] README file + +* plugin-runner +** [#B] Add more comments to code +** [#B] Add more if(debug) calls +** [#B] Seperate more code to function for more readability +** [#A] Man page: man8/plugin-runner.8mandos +*** EXIT STATUS +*** ENVIRONMENT + Environment is modified according to options and passed to plugins +*** EXAMPLE + Examples of normal usage, debug usage, debugging single or all + plugins, etc. +*** FILES +*** SECURITY + Note the danger of using this program, since you might lock + yourself out of your system without any means of entering the root + file system password. This is, however, very unlikely considering + the fallback to getpass(3). +*** BUGS +*** SEE ALSO + Explaining text on what you can read + +* password-request +** [#A] Man page: man8/password-request.8mandos +*** SYNOPSIS + Document short options +*** DESCRIPTION + State that this command is not meant to be invoked directly, but + is run as a plugin from mandos-client(8) and only run in the + initrd environment, not the real system. +*** PURPOSE + As in mandos.xml +*** OVERVIEW + As in mandos.xml +*** EXIT STATUS +*** ENVIRONMENT + Note that it does *not* currently use cryptsource or crypttarget. +*** FILES + Describe the key files and the key ring files. Also note that + they should normally have been automatically created. +*** BUGS +*** EXAMPLE + Examples of normal usage, debug usage, debugging by connecting + directly, etc. +*** SECURITY +*** SEE ALSO + Update from mandos.xml +** [#B] Temporarily lower kernel log level for less printouts during sucessfull boot. - klogctl(6, NULL, 0); klogctl(7, NULL, 0); -** TODO [#C] IPv4 support +** IPv4 support +** use strsep instead of strtok? +** Do not depend on GnuPG key rings on disk + This would mean creating new GnuPG key rings with GPGME by + importing the key files from scratch on every program start. +** Keydir move: /etc/mandos -> /etc/keys/mandos + Must create in preinst if not pre-depending on cryptsetup -* plugin-runner -** TODO [#B] use scandir(3) instead of readdir(3) +* password-prompt +** [#C] Use getpass(3)? + Man page says "obsolete", but [[info:libc:getpass][GNU LibC Manual: Reading Passwords]] + does not. See also [[http://sources.redhat.com/ml/libc-alpha/2003-05/msg00251.html][Marcus Brinkmann: Re: getpass obsolete?]] and + [[http://article.gmane.org/gmane.comp.lib.glibc.alpha/4906][Petter Reinholdtsen: Re: getpass obsolete?]], and especially also + [[http://www.steve.org.uk/Reference/Unix/faq_4.html#SEC48][Unix Programming FAQ 3.1 How can I make my program not echo input?]] * mandos (server) -** TODO [#B] Log level :bugs: -** TODO /etc/mandos/clients.d/*.conf +** [#A] /etc/init.d/mandos-server :teddy: +** [#B] Log level :bugs: +** /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: +** config for TXT record +** [#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: +** Implement --foreground :bugs: + [[info:standards:Option%20Table][Table of Long Options]] +** Implement --socket + [[info:standards:Option%20Table][Table of Long Options]] +** Date+time on console log messages :bugs: Is this the default? -** TODO delete hook when clients fall out by timeout - This will not be strictly necessary when the D-Bus interface is - implemented. - -* mandos.xml -** [[file:mandos.xml::XXX][Document D-Bus interface]] - -* Provide and install /etc/dbus-1/system.d/mandos.conf - -* mandos-list -*** Handle "no D-Bus server" and/or "no Mandos server found" better -*** [#B] --dump option -** TODO Disable client -** TODO Enable client -** TODO Reset timer - -* Curses interface - -* mandos-keygen -** TODO "--secfile" option - Using the "secfile" option instead of "secret" -** TODO [#B] "--test" option - For testing decryption before rebooting. + +* Mandos-tools/utilities + All of this probably using D-Bus +** List clients +** Disable client +** Enable client + +* Man pages tags + Go through all man pages to conform to the style of tags chosen in + [[http://svn.debian.org/wsvn/debian-xml-sgml/packages/docbook-xsl/trunk/debian/examples/foo.1.example_manpage.xml?op=file&rev=0&sc=0][foo.1.example_manpage.xml]]. + +* Installer +** Client-side +*** Update initrd.img after installation + This seems to use some kind of "trigger" system + [[file:/usr/share/doc/dpkg/triggers.txt.gz]] + dpkg-trigger(1), deb-triggers(5) +*** Keydir move: /etc/mandos -> /etc/keys/mandos + Must create in preinst if not pre-depending on cryptsetup +*** mandos-keygen +**** "--passfile" option + Using the "secfile" option instead of "secret" +**** [#A] "--test" option + For testing decryption before rebooting. +** Server-side +*** [#A] Create mandos user and group for server +*** [#A] Create /var/run/mandos directory with perm and ownership * [#A] Package ** /usr/share/initramfs-tools/hooks/mandos -*** TODO [#C] Do not install in initrd.img if configured not to. +*** Do not install in initrd.img if configured not to. Use "/etc/initramfs-tools/conf.d/mandos"? Definitely a debconf question. -** TODO [#C] /etc/bash_completion.d/mandos +** /etc/bash_completion.d/mandos From XML sources directly? -** TODO initramfs-tools-script :test: - Do not insert plugin-runner as keyscript if a kernel parameter - "mandos=off" is passed. +** unperish +** bzr-builddeb + +* INSTALL file + +* Web site + +* Mailing list + +* Announce project on news + [[news:comp.os.linux.announce]] #+STARTUP: showall === modified file 'clients.conf' --- clients.conf 2009-01-08 03:54:06 +0000 +++ clients.conf 2008-08-27 01:18:25 +0000 @@ -14,7 +14,7 @@ ;interval = 5m # What command to run as "the checker". -;checker = fping -q -- %%(host)s +;checker = fping -q -- %(host)s ;#### @@ -55,7 +55,7 @@ ;fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 ; ;# If "secret" is not specified, a file can be read for the data. -;secfile = /etc/mandos/bar-secret.bin +;;secfile = /etc/mandos/bar-secret.txt.asc ; ;# An IP address for host is also fine, if the checker accepts it. ;host = 192.0.2.3 === removed file 'common.ent' --- common.ent 2008-09-30 07:23:39 +0000 +++ common.ent 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ - - - === removed directory 'debian' === removed file 'debian/changelog' --- debian/changelog 2009-01-06 22:43:19 +0000 +++ debian/changelog 1970-01-01 00:00:00 +0000 @@ -1,52 +0,0 @@ -mandos (1.0.3-2) unstable; urgency=low - - * Removed some now-unused debconf files. - * Changed postinst scripts to not source debconf/confmodule. - * Removed po-debconf from build-depends. - - -- Teddy Hogeborn Tue, 06 Jan 2009 21:28:20 +0100 - -mandos (1.0.3-1) unstable; urgency=low - - * New upstream release. - * Add -Xlinker to linker flags to fix FTBFS for some architectures. - Thanks to Thiemo Seufer for the report and - fix. (Closes: #509398) - * Remove debconf use altogether, thereby stopping debconf abuse. Thanks - to Christian Perrier . (Closes: #509653) - * Add NEWS file to /usr/share/doc directories. - * Use and create "_mandos" user+group. Rename old user+group created by - older versions of this package. - * Fix manual pages by adding build-depend on "docbook-xml". - - -- Teddy Hogeborn Tue, 06 Jan 2009 01:21:20 +0100 - -mandos (1.0.2-1) unstable; urgency=low - - * New upstream release. - * debian/copyright: Rewritten to conform to - . - - -- Teddy Hogeborn Fri, 17 Oct 2008 20:42:12 +0200 - -mandos (1.0.1-1) unstable; urgency=low - - * New upstream release. - * Separate /usr/share/doc/mandos-client/README.Debian into sections with - headlines. Add instructions on how to test the server and verify the - password. - - -- Teddy Hogeborn Tue, 07 Oct 2008 23:07:23 +0200 - -mandos (1.0-2) unstable; urgency=low - - * Added comments in debian/*.lintian-overrides files. Added Debian - revison number to version number. - - -- Teddy Hogeborn Wed, 01 Oct 2008 17:23:35 +0200 - -mandos (1.0-1) unstable; urgency=low - - * Initial Release. (Closes: #500727). - - -- Teddy Hogeborn Tue, 30 Sep 2008 21:58:43 +0200 === removed file 'debian/compat' --- debian/compat 2008-09-17 00:34:09 +0000 +++ debian/compat 1970-01-01 00:00:00 +0000 @@ -1,1 +0,0 @@ -7 === removed file 'debian/control' --- debian/control 2009-01-06 20:39:35 +0000 +++ debian/control 1970-01-01 00:00:00 +0000 @@ -1,52 +0,0 @@ -Source: mandos -Section: admin -Priority: extra -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, - pkg-config -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 - -Package: mandos -Architecture: all -Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus, - 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 - computers to have encrypted root file systems and at the - same time be capable of remote and/or unattended reboots. - . - The computers run a small client program in the initial RAM - disk environment which will communicate with a server over a - network. All network communication is encrypted using TLS. - The clients are identified by the server using an OpenPGP - key; each client has one unique to it. The server sends the - clients an encrypted password. The encrypted password is - decrypted by the clients using the same OpenPGP key, and the - password is then used to unlock the root file system, - whereupon the computers can continue booting normally. - -Package: mandos-client -Architecture: any -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 - computers to have encrypted root file systems and at the - same time be capable of remote and/or unattended reboots. - . - The computers run a small client program in the initial RAM - disk environment which will communicate with a server over a - network. All network communication is encrypted using TLS. - The clients are identified by the server using an OpenPGP - key; each client has one unique to it. The server sends the - clients an encrypted password. The encrypted password is - decrypted by the clients using the same OpenPGP key, and the - password is then used to unlock the root file system, - whereupon the computers can continue booting normally. === removed file 'debian/copyright' --- debian/copyright 2009-01-04 21:54:55 +0000 +++ debian/copyright 1970-01-01 00:00:00 +0000 @@ -1,26 +0,0 @@ -Format-Specification: - http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=233 -Upstream-Name: Mandos -Upstream-Maintainer: Mandos Maintainers -Upstream-Source: - -Files: * -Copyright: Copyright © 2008,2009 Teddy Hogeborn -Copyright: Copyright © 2008,2009 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 - 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 - . - . - On Debian systems, the complete text of the GNU General Public - License can be found in "/usr/share/common-licenses/GPL". === removed file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2009-01-15 02:52:02 +0000 +++ debian/mandos-client.README.Debian 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ -* 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 \ - --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. 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 - 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. - - -- Teddy Hogeborn , Mon, 12 Jan 2009 02:29:10 +0100 === removed file 'debian/mandos-client.dirs' --- debian/mandos-client.dirs 2008-09-17 00:34:09 +0000 +++ debian/mandos-client.dirs 1970-01-01 00:00:00 +0000 @@ -1,5 +0,0 @@ -usr/share/man/man8 -usr/sbin -usr/share/initramfs-tools/hooks -usr/share/initramfs-tools/conf-hooks.d -usr/share/initramfs-tools/scripts/local-top === removed file 'debian/mandos-client.docs' --- debian/mandos-client.docs 2008-10-18 11:17:22 +0000 +++ debian/mandos-client.docs 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ -NEWS -README -TODO === removed file 'debian/mandos-client.links' --- debian/mandos-client.links 2008-09-19 13:50:22 +0000 +++ debian/mandos-client.links 1970-01-01 00:00:00 +0000 @@ -1,1 +0,0 @@ -usr/share/man/man8/plugin-runner.8mandos.gz usr/share/man/man5/plugin-runner.conf.5mandos.gz === removed file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2008-10-01 15:29:01 +0000 +++ debian/mandos-client.lintian-overrides 1970-01-01 00:00:00 +0000 @@ -1,32 +0,0 @@ -# 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 - -# The directory /usr/lib/mandos/plugins.d contains setuid binaries -# which are not meant to be run outside an initial RAM disk -# environment (except for test purposes). It would be insecure to -# allow anyone to run them. -# -mandos-client binary: non-standard-dir-perm usr/lib/mandos/plugins.d/ 0700 != 0755 - -# These binaries must be setuid root, since they need root powers, but -# are started by plugin-runner(8mandos), which runs all plugins as -# user/group "mandos". These binaries are not run in a running -# system, but in an initial RAM disk environment. Here they are -# protected from non-root access by the directory permissions, above. -# -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/askpass-fifo 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/splashy 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/usplash 4755 root/root - -# The directory /etc/mandos/plugins.d can be used by local system -# administrators to place plugins in, overriding and complementing -# /usr/lib/mandos/plugins.d, and must be likewise protected. -# -mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755 === removed file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2009-01-06 05:08:37 +0000 +++ debian/mandos-client.postinst 1970-01-01 00:00:00 +0000 @@ -1,72 +0,0 @@ -#!/bin/bash -e -# This script can be called in the following ways: -# -# After the package was installed: -# configure -# -# -# If prerm fails during upgrade or fails on failed upgrade: -# abort-upgrade -# -# If prerm fails during deconfiguration of a package: -# abort-deconfigure in-favour -# removing -# -# If prerm fails during replacement due to conflict: -# abort-remove in-favour - -# Update the initial RAM file system image -update_initramfs() -{ - if [ -x /usr/sbin/update-initramfs ]; then - update-initramfs -u -k all - fi -} - -# Add user and group -add_mandos_user(){ - # Rename old "mandos" user and group - 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 \ - --no-create-home --group --disabled-password \ - --gecos "Mandos password system" _mandos - fi -} - -# Create client key pair -create_key(){ - if [ -r /etc/keys/mandos/pubkey.txt \ - -a -r /etc/keys/mandos/seckey.txt ]; then - return 0 - fi - if [ -x /usr/sbin/mandos-keygen ]; then - mandos-keygen - fi -} - -case "$1" in - configure) - add_mandos_user - create_key - update_initramfs - ;; - abort-upgrade|abort-deconfigure|abort-remove) - ;; - - *) - echo "$0 called with unknown argument \`$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos-client.postrm' --- debian/mandos-client.postrm 2008-09-19 20:54:58 +0000 +++ debian/mandos-client.postrm 1970-01-01 00:00:00 +0000 @@ -1,60 +0,0 @@ -#!/bin/sh -e -# This script can be called in the following ways: -# -# After the package was removed: -# remove -# -# After the package was purged: -# purge -# -# After the package was upgraded: -# upgrade -# if that fails: -# failed-upgrade -# -# -# After all of the packages files have been replaced: -# disappear -# -# -# If preinst fails during install: -# abort-install -# -# If preinst fails during upgrade of removed package: -# abort-install -# -# If preinst fails during upgrade: -# abort-upgrade - - -# Update the initial RAM file system image -update_initramfs() -{ - if [ -x /usr/sbin/update-initramfs ]; then - update-initramfs -u -k all - fi -} - -case "$1" in - remove) - update_initramfs - ;; - - purge) - shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || : - rm --force /etc/mandos/plugin-runner.conf \ - /etc/keys/mandos/pubkey.txt \ - /etc/keys/mandos/seckey.txt 2>/dev/null - ;; - upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) - ;; - - *) - echo "$0 called with unknown argument \`$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos.README.Debian' --- debian/mandos.README.Debian 2009-01-04 22:15:01 +0000 +++ debian/mandos.README.Debian 1970-01-01 00:00:00 +0000 @@ -1,7 +0,0 @@ -The Mandos server is useless without at least one configured client in -/etc/mandos/clients.conf. To create one, install the "mandos-client" -package on a client computer, and run "mandos-keygen --password" there -to get a config file stanza. Append that to /etc/mandos/clients.conf -on the Mandos server. - - -- Teddy Hogeborn , Sun, 4 Jan 2009 22:59:22 +0100 === removed file 'debian/mandos.dirs' --- debian/mandos.dirs 2008-09-17 00:34:09 +0000 +++ debian/mandos.dirs 1970-01-01 00:00:00 +0000 @@ -1,5 +0,0 @@ -usr/share/man/man5 -usr/share/man/man8 -etc/init.d -etc/default -usr/sbin === removed file 'debian/mandos.docs' --- debian/mandos.docs 2008-10-18 11:17:22 +0000 +++ debian/mandos.docs 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ -NEWS -README -TODO === removed file 'debian/mandos.lintian-overrides' --- debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000 +++ debian/mandos.lintian-overrides 1970-01-01 00:00:00 +0000 @@ -1,4 +0,0 @@ -# This config file will normally have encrypted secret client keys in -# it, so it must be kept unreadable for non-root users. -# -mandos binary: non-standard-file-perm etc/mandos/clients.conf 0600 != 0644 === removed file 'debian/mandos.postinst' --- debian/mandos.postinst 2009-01-06 05:08:37 +0000 +++ debian/mandos.postinst 1970-01-01 00:00:00 +0000 @@ -1,47 +0,0 @@ -#!/bin/bash -e -# This script can be called in the following ways: -# -# After the package was installed: -# configure -# -# -# If prerm fails during upgrade or fails on failed upgrade: -# abort-upgrade -# -# If prerm fails during deconfiguration of a package: -# abort-deconfigure in-favour -# removing -# -# If prerm fails during replacement due to conflict: -# abort-remove in-favour - -case "$1" in - configure) - # Rename old "mandos" user and group - 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 \ - --home /nonexistent --no-create-home --group \ - --disabled-password --gecos "Mandos password system" \ - _mandos - fi - ;; - - abort-upgrade|abort-deconfigure|abort-remove) - ;; - - *) - echo "$0 called with unknown argument \`$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos.prerm' --- debian/mandos.prerm 2008-09-21 13:42:34 +0000 +++ debian/mandos.prerm 1970-01-01 00:00:00 +0000 @@ -1,38 +0,0 @@ -#! /bin/sh -# prerm script for mandos -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see /usr/share/doc/packaging-manual/ - -case "$1" in - remove|deconfigure) - if [ -x /etc/init.d/mandos ]; then - if [ -x /usr/sbin/invoke-rc.d ]; then - invoke-rc.d mandos stop - else - /etc/init.d/mandos stop - fi - fi - ;; - upgrade|failed-upgrade) - ;; - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 0 - ;; -esac - -#DEBHELPER# - -exit 0 === removed directory 'debian/po' === removed file 'debian/rules' --- debian/rules 2009-01-04 22:26:07 +0000 +++ debian/rules 1970-01-01 00:00:00 +0000 @@ -1,92 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# This has to be exported to make some magic below work. -export DH_OPTIONS - -configure: configure-stamp -configure-stamp: - dh_testdir - touch configure-stamp - -build: build-arch build-indep - -build-arch: build-arch-stamp -build-arch-stamp: configure-stamp - dh_auto_build -- all doc - touch $@ - -build-indep: build-indep-stamp -build-indep-stamp: configure-stamp - dh_auto_build -- doc - touch $@ - -clean: - dh_testdir - dh_testroot - rm -f build-arch-stamp build-indep-stamp configure-stamp - dh_auto_clean - dh_clean - -install: install-indep install-arch -install-indep: - dh_testdir - dh_testroot - dh_prep - dh_installdirs --indep - $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server - dh_lintian - dh_installinit --onlyscripts --no-start \ - --update-rcd-params="defaults 25 15" - dh_install --indep - -install-arch: - dh_testdir - dh_testroot - dh_prep - dh_installdirs --same-arch - $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client install-client-nokey - dh_lintian - dh_install --same-arch - -binary-common: - dh_testdir - dh_testroot - dh_installchangelogs - dh_installdocs - dh_link - dh_strip - dh_compress - dh_fixperms --exclude etc/keys/mandos \ - --exclude etc/mandos/clients.conf \ - --exclude etc/mandos/plugins.d \ - --exclude usr/lib/mandos/plugins.d - dh_installdeb - dh_shlibdeps - dh_gencontrol - dh_md5sums - dh_builddeb - -# Build architecture independant packages using the common target. -binary-indep: build-indep install-indep - $(MAKE) -f debian/rules DH_OPTIONS=--indep binary-common - -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - $(MAKE) -f debian/rules DH_OPTIONS=--same-arch binary-common - -binary: binary-arch binary-indep - -.PHONY: build clean binary-indep binary-arch binary install \ - install-indep install-arch configure === removed file 'debian/watch' --- debian/watch 2009-01-15 02:52:02 +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) === removed file 'default-mandos' --- default-mandos 2008-09-17 00:34:09 +0000 +++ default-mandos 1970-01-01 00:00:00 +0000 @@ -1,7 +0,0 @@ -# Directory where configuration files are located. Default is -# "/etc/mandos". -# -#CONFIGDIR=/etc/mandos - -# Additional options that are passed to the Daemon. -DAEMON_ARGS="" === removed file 'init.d-mandos' --- init.d-mandos 2008-09-21 12:04:02 +0000 +++ init.d-mandos 1970-01-01 00:00:00 +0000 @@ -1,159 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: mandos -# 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 -# Description: Gives encrypted passwords to Mandos clients -### END INIT INFO - -# Author: Teddy Hogeborn -# Author: Björn Påhlsson -# -# Please remove the "Author" lines above and replace them -# with your own name if you copy and modify this script. - -# Do NOT "set -e" - -# PATH should only include /usr/* if it runs after the mountnfs.sh script -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="Mandos root file system password server" -NAME=mandos -DAEMON=/usr/sbin/$NAME -DAEMON_ARGS="" -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -if [ -n "$CONFIGDIR" ]; then - DAEMON_ARGS="$DAEMON_ARGS --configdir $CONFIGDIR" -fi - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ - || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ - $DAEMON_ARGS \ - || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Wait for children to finish too if this is a daemon that forks - # and if the daemon is only ever run from this initscript. - # If the above conditions are not satisfied then add some other code - # that waits for the process to drop all resources that could be - # needed by services started subsequently. A last resort is to - # sleep for some time. - start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON - [ "$?" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} - -# -# Function that sends a SIGHUP to the daemon/service -# -do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # - start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME - return 0 -} - -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2008-12-10 01:26:02 +0000 +++ initramfs-tools-hook 2008-08-24 23:18:18 +0000 @@ -29,39 +29,15 @@ . /usr/share/initramfs-tools/hook-functions -for d in /usr /usr/local; do - if [ -d "$d"/lib/mandos ]; then - prefix="$d" - break - fi -done -if [ -z "$prefix" ]; then +if [ -d /usr/lib/mandos ]; then + prefix=/usr +elif [ -d /usr/local/lib/mandos ]; then + prefix=/usr/local +else # Mandos not found exit 1 fi -for d in /etc/keys/mandos /etc/mandos/keys; do - if [ -d "$d" ]; then - keydir="$d" - break - fi -done -if [ -z "$keydir" ]; then - # Mandos key directory not found - exit 1 -fi - -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 # The Mandos network client uses IPv6 @@ -73,13 +49,11 @@ PLUGINDIR="${MANDOSDIR}/plugins.d" # Make directories -install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \ - "${DESTDIR}${MANDOSDIR}" -install --owner=${mandos_user} --group=${mandos_group} --directory \ - --mode=u=rwx "${DESTDIR}${PLUGINDIR}" +mkdir --parents "${DESTDIR}${CONFDIR}" +mkdir --parents "${DESTDIR}${PLUGINDIR}" # Copy the Mandos plugin runner -copy_exec "$prefix"/lib/mandos/plugin-runner "${MANDOSDIR}" +copy_exec "$prefix"/lib/mandos/plugin-runner "${DESTDIR}${MANDOSDIR}" # Copy the plugins @@ -91,8 +65,7 @@ continue fi case "$base" in - *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;; - "*") :;; + *~|.*|\#*\#|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;; *) copy_exec "$file" "${PLUGINDIR}";; esac done @@ -101,46 +74,31 @@ for file in /etc/mandos/plugins.d/*; do base="`basename \"$file\"`" case "$base" in - *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert) : ;; - "*") :;; + *~|.*|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;; *) copy_exec "$file" "${PLUGINDIR}";; esac done # GPGME needs /usr/bin/gpg -if [ ! -e "${DESTDIR}/usr/bin/gpg" \ - -a -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* \ - 2>/dev/null`" ]; then +if ! [ -e "${DESTDIR}/usr/bin/gpg" ] \ + && [ -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* 2>/dev/null`" ]; then copy_exec /usr/bin/gpg fi -# Config files +# Key files for file in /etc/mandos/*; do if [ -d "$file" ]; then continue fi cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" done - -if [ ${mandos_user} != 65534 ]; then - PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf" - echo "--userid=${mandos_user}" >> "$PLUGINRUNNERCONF" -fi - -if [ ${mandos_group} != 65534 ]; then - PLUGINRUNNERCONF="${DESTDIR}${CONFDIR}/plugin-runner.conf" - echo "--groupid=${mandos_group}" >> "$PLUGINRUNNERCONF" -fi - -# Key files -for file in "$keydir"/*; do - if [ -d "$file" ]; then - continue - fi - cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" - chown ${mandos_user}:${mandos_group} \ - "${DESTDIR}${CONFDIR}/`basename \"$file\"`" -done +# Create key ring files +gpg --no-random-seed-file --quiet --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --homedir "${DESTDIR}${CONFDIR}" --no-permission-warning \ + --trust-model always --import-options import-minimal \ + --import "${DESTDIR}${CONFDIR}/seckey.txt" +chown nobody "${DESTDIR}${CONFDIR}/secring.gpg" # /lib/mandos/plugin-runner will drop priviliges, but needs access to # its plugin directory and its config file. However, since almost all @@ -154,7 +112,7 @@ # condition. This umask is set by "initramfs-tools-hook-conf", # installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".) # -for full in "${MANDOSDIR}" "${CONFDIR}"; do +for full in "${PLUGINDIR}" "${CONFDIR}"; do while [ "$full" != "/" ]; do chmod a+rX "${DESTDIR}$full" full="`dirname \"$full\"`" @@ -164,11 +122,8 @@ # Reset some other things to sane permissions which we have # inadvertently affected with our umask setting. for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do - if [ -d "${DESTDIR}$dir" ]; then - chmod a+rX "${DESTDIR}$dir" - fi + chmod a+rX "${DESTDIR}$dir" done for dir in /lib /usr/lib; do - find "${DESTDIR}$dir" \! -perm -u+rw,g+r -prune -or -print0 \ - | xargs --null --no-run-if-empty chmod a+rX + chmod --recursive a+rX "${DESTDIR}$dir" done === modified file 'initramfs-tools-script' --- initramfs-tools-script 2009-01-15 02:52:02 +0000 +++ initramfs-tools-script 2008-08-14 02:24:59 +0000 @@ -24,14 +24,6 @@ ;; esac -for param in `cat /proc/cmdline`; do - case "$param" in - mandos=off) exit 0;; - esac -done - -chmod a=rwxt /tmp - test -w /conf/conf.d/cryptroot # Do not replace cryptroot file unless we need to. === removed file 'legalnotice.xml' --- legalnotice.xml 2008-09-06 17:24:58 +0000 +++ legalnotice.xml 1970-01-01 00:00:00 +0000 @@ -1,27 +0,0 @@ - - - - - This manual page is free software: you can redistribute it and/or - modify it under the terms of the GNU General - Public License as published by the Free Software Foundation, - either version 3 of the License, or (at your option) any later - version. - - - - This manual page is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied - warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more - details. - - - - You should have received a copy of the GNU - General Public License along with this program. If not, see - http://www.gnu.org/licenses/. - - === modified file 'mandos' --- mandos 2009-01-08 03:54:06 +0000 +++ mandos 2008-08-27 01:18:25 +0000 @@ -11,8 +11,7 @@ # and some lines in "main". # # Everything else is -# Copyright © 2008,2009 Teddy Hogeborn -# Copyright © 2008,2009 Björn Påhlsson +# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -31,10 +30,11 @@ # Contact the authors at . # -from __future__ import division, with_statement, absolute_import +from __future__ import division import SocketServer import socket +import select from optparse import OptionParser import datetime import errno @@ -55,25 +55,21 @@ import stat import logging import logging.handlers -import pwd -from contextlib import closing import dbus -import dbus.service import gobject import avahi from dbus.mainloop.glib import DBusGMainLoop import ctypes -import ctypes.util -version = "1.0.3" +version = "1.0" logger = logging.Logger('mandos') -syslogger = (logging.handlers.SysLogHandler - (facility = logging.handlers.SysLogHandler.LOG_DAEMON, - address = "/dev/log")) -syslogger.setFormatter(logging.Formatter - ('Mandos: %(levelname)s: %(message)s')) +syslogger = logging.handlers.SysLogHandler\ + (facility = logging.handlers.SysLogHandler.LOG_DAEMON, + address = "/dev/log") +syslogger.setFormatter(logging.Formatter\ + ('Mandos: %(levelname)s: %(message)s')) logger.addHandler(syslogger) console = logging.StreamHandler() @@ -82,11 +78,10 @@ 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)) + def __str__(self): + return repr(self.value) class AvahiServiceError(AvahiError): pass @@ -112,13 +107,16 @@ a sensible number of times """ def __init__(self, interface = avahi.IF_UNSPEC, name = None, - servicetype = None, port = None, TXT = None, - domain = "", host = "", max_renames = 32768): + type = None, port = None, TXT = None, domain = "", + host = "", max_renames = 32768): self.interface = interface self.name = name - self.type = servicetype + self.type = type self.port = port - self.TXT = TXT if TXT is not None else [] + if TXT is None: + self.TXT = [] + else: + self.TXT = TXT self.domain = domain self.host = host self.rename_count = 0 @@ -128,14 +126,14 @@ 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") + rename_count) + raise AvahiServiceError("Too many renames") self.name = server.GetAlternativeServiceName(self.name) logger.info(u"Changing Zeroconf service name to %r ...", str(self.name)) - syslogger.setFormatter(logging.Formatter + syslogger.setFormatter(logging.Formatter\ ('Mandos (%s): %%(levelname)s:' - ' %%(message)s' % self.name)) + ' %%(message)s' % self.name)) self.remove() self.add() self.rename_count += 1 @@ -147,10 +145,10 @@ """Derived from the Avahi example code""" global group if group is None: - group = dbus.Interface(bus.get_object - (avahi.DBUS_NAME, + group = dbus.Interface\ + (bus.get_object(avahi.DBUS_NAME, server.EntryGroupNew()), - avahi.DBUS_INTERFACE_ENTRY_GROUP) + avahi.DBUS_INTERFACE_ENTRY_GROUP) group.connect_to_signal('StateChanged', entry_group_state_changed) logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...", @@ -170,199 +168,148 @@ # End of Avahi example code -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) - - -class Client(dbus.service.Object): +class Client(object): """A representation of a client host served by this server. Attributes: - name: string; from the config file, used in log messages + 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_enabled: datetime.datetime(); (UTC) - enabled: 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 - disable_hook: If set, called by disable() as disable_hook(self) - checker: subprocess.Popen(); a running checker process used - to see if the client lives. - 'None' if no process is running. + secret: bytestring; sent verbatim (over TLS) to client + host: string; available for use by the checker command + created: datetime.datetime(); object creation, not client host + last_checked_ok: datetime.datetime() or None if not yet checked OK + timeout: datetime.timedelta(); How long from last_checked_ok + until this client is invalid + interval: datetime.timedelta(); How often to start a new checker + stop_hook: If set, called by stop() as stop_hook(self) + checker: subprocess.Popen(); a running checker process used + to see if the client lives. + 'None' if no process is running. checker_initiator_tag: a gobject event source tag, or None - disable_initiator_tag: - '' - + 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. - use_dbus: bool(); Whether to provide D-Bus interface and signals - dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus + Private attibutes: + _timeout: Real variable for 'timeout' + _interval: Real variable for 'interval' + _timeout_milliseconds: Used when calling gobject.timeout_add() + _interval_milliseconds: - '' - """ - def timeout_milliseconds(self): - "Return the 'timeout' attribute in milliseconds" - return ((self.timeout.days * 24 * 60 * 60 * 1000) - + (self.timeout.seconds * 1000) - + (self.timeout.microseconds // 1000)) - - def interval_milliseconds(self): - "Return the 'interval' attribute in milliseconds" - return ((self.interval.days * 24 * 60 * 60 * 1000) - + (self.interval.seconds * 1000) - + (self.interval.microseconds // 1000)) - - def __init__(self, name = None, disable_hook=None, config=None, - use_dbus=True): + def _set_timeout(self, timeout): + "Setter function for 'timeout' attribute" + self._timeout = timeout + self._timeout_milliseconds = ((self.timeout.days + * 24 * 60 * 60 * 1000) + + (self.timeout.seconds * 1000) + + (self.timeout.microseconds + // 1000)) + timeout = property(lambda self: self._timeout, + _set_timeout) + del _set_timeout + def _set_interval(self, interval): + "Setter function for 'interval' attribute" + self._interval = interval + self._interval_milliseconds = ((self.interval.days + * 24 * 60 * 60 * 1000) + + (self.interval.seconds + * 1000) + + (self.interval.microseconds + // 1000)) + interval = property(lambda self: self._interval, + _set_interval) + del _set_interval + def __init__(self, name = None, stop_hook=None, config={}): """Note: the 'checker' key in 'config' sets the 'checker_command' attribute and *not* the 'checker' attribute.""" self.name = name - if config is None: - config = {} logger.debug(u"Creating client %r", self.name) - self.use_dbus = use_dbus - if self.use_dbus: - self.dbus_object_path = (dbus.ObjectPath - ("/Mandos/clients/" - + self.name.replace(".", "_"))) - dbus.service.Object.__init__(self, bus, - self.dbus_object_path) # Uppercase and remove spaces from fingerprint for later # comparison purposes with return value from the fingerprint() # function - self.fingerprint = (config["fingerprint"].upper() - .replace(u" ", u"")) + self.fingerprint = config["fingerprint"].upper()\ + .replace(u" ", u"") logger.debug(u" Fingerprint: %s", self.fingerprint) if "secret" in config: self.secret = config["secret"].decode(u"base64") elif "secfile" in config: - with closing(open(os.path.expanduser - (os.path.expandvars - (config["secfile"])))) as secfile: - self.secret = secfile.read() + sf = open(config["secfile"]) + self.secret = sf.read() + sf.close() else: raise TypeError(u"No secret or secfile for client %s" % self.name) self.host = config.get("host", "") - self.created = datetime.datetime.utcnow() - self.enabled = False - self.last_enabled = None + self.created = datetime.datetime.now() self.last_checked_ok = None self.timeout = string_to_delta(config["timeout"]) self.interval = string_to_delta(config["interval"]) - self.disable_hook = disable_hook + 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["checker"] - - def enable(self): + self.check_command = config["checker"] + def start(self): """Start this client's checker and timeout hooks""" - self.last_enabled = 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.start_checker)) + self.checker_initiator_tag = gobject.timeout_add\ + (self._interval_milliseconds, + self.start_checker) # Also start a new checker *right now*. 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 - if self.use_dbus: - # Emit D-Bus signals - self.PropertyChanged(dbus.String(u"enabled"), - dbus.Boolean(True, variant_level=1)) - self.PropertyChanged(dbus.String(u"last_enabled"), - (_datetime_to_dbus(self.last_enabled, - variant_level=1))) - - def disable(self): - """Disable this client.""" - if not getattr(self, "enabled", False): + # Schedule a stop() when 'timeout' has passed + self.stop_initiator_tag = gobject.timeout_add\ + (self._timeout_milliseconds, + self.stop) + def stop(self): + """Stop this client. + The possibility that a client might be restarted is left open, + but not currently used.""" + # If this client doesn't have a secret, it is already stopped. + if hasattr(self, "secret") and self.secret: + logger.info(u"Stopping client %s", self.name) + self.secret = None + else: return False - logger.info(u"Disabling client %s", self.name) - if getattr(self, "disable_initiator_tag", False): - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = None + 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.use_dbus: - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"enabled"), - dbus.Boolean(False, variant_level=1)) + if self.stop_hook: + self.stop_hook(self) # Do not run this again if called by a gobject.timeout_add return False - def __del__(self): - self.disable_hook = None - self.disable() - - def checker_callback(self, pid, condition, command): + self.stop_hook = None + self.stop() + def checker_callback(self, pid, condition): """The checker has completed, so take appropriate actions.""" + now = datetime.datetime.now() self.checker_callback_tag = None self.checker = None - if self.use_dbus: - # 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)): + if os.WIFEXITED(condition) \ + and (os.WEXITSTATUS(condition) == 0): logger.info(u"Checker for %(name)s succeeded", vars(self)) - if self.use_dbus: - # Emit D-Bus signal - self.CheckerCompleted(dbus.Boolean(True), - dbus.UInt16(condition), - dbus.String(command)) - self.bump_timeout() + self.last_checked_ok = now + gobject.source_remove(self.stop_initiator_tag) + self.stop_initiator_tag = gobject.timeout_add\ + (self._timeout_milliseconds, + self.stop) elif not os.WIFEXITED(condition): logger.warning(u"Checker for %(name)s crashed?", vars(self)) - if self.use_dbus: - # 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)) - if self.use_dbus: - # Emit D-Bus signal - self.CheckerCompleted(dbus.Boolean(False), - dbus.UInt16(condition), - dbus.String(command)) - - 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)) - if self.use_dbus: - # Emit D-Bus signal - self.PropertyChanged( - dbus.String(u"last_checked_ok"), - (_datetime_to_dbus(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 @@ -377,18 +324,18 @@ # 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 + # In case check_command has exactly one % operator + command = self.check_command % self.host except TypeError: # Escape attributes for the shell escaped_attrs = dict((key, re.escape(str(val))) for key, val in vars(self).iteritems()) try: - command = self.checker_command % escaped_attrs + command = self.check_command % escaped_attrs except TypeError, error: logger.error(u'Could not format string "%s":' - u' %s', self.checker_command, error) + u' %s', self.check_command, error) return True # Try again later try: logger.info(u"Starting checker %r for %s", @@ -400,22 +347,14 @@ self.checker = subprocess.Popen(command, close_fds=True, shell=True, cwd="/") - if self.use_dbus: - # 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)) + self.checker_callback_tag = gobject.child_watch_add\ + (self.checker.pid, + self.checker_callback) except OSError, error: logger.error(u"Failed to start subprocess: %s", error) # Re-run this periodically if run by gobject.timeout_add return True - def stop_checker(self): """Force the checker process, if any, to stop.""" if self.checker_callback_tag: @@ -433,168 +372,26 @@ if error.errno != errno.ESRCH: # No such process raise self.checker = None - if self.use_dbus: - self.PropertyChanged(dbus.String(u"checker_running"), - dbus.Boolean(False, variant_level=1)) - def still_valid(self): """Has the timeout not yet passed for this client?""" - if not getattr(self, "enabled", False): - return False - now = datetime.datetime.utcnow() + now = datetime.datetime.now() if self.last_checked_ok is None: return now < (self.created + self.timeout) else: return now < (self.last_checked_ok + self.timeout) - - ## 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="bqs") - def CheckerCompleted(self, success, condition, command): - "D-Bus signal" - pass - - # CheckerStarted - signal - @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(self.created, variant_level=1), - dbus.String("last_enabled"): - (_datetime_to_dbus(self.last_enabled, - variant_level=1) - if self.last_enabled is not None - else dbus.Boolean(False, variant_level=1)), - dbus.String("enabled"): - dbus.Boolean(self.enabled, variant_level=1), - dbus.String("last_checked_ok"): - (_datetime_to_dbus(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="sv") - def PropertyChanged(self, property, value): - "D-Bus signal" - pass - - # SetChecker - method - @dbus.service.method(_interface, in_signature="s") - def SetChecker(self, checker): - "D-Bus setter method" - self.checker_command = checker - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"checker"), - dbus.String(self.checker_command, - variant_level=1)) - - # SetHost - method - @dbus.service.method(_interface, in_signature="s") - def SetHost(self, host): - "D-Bus setter method" - self.host = host - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"host"), - dbus.String(self.host, variant_level=1)) - - # SetInterval - method - @dbus.service.method(_interface, in_signature="t") - def SetInterval(self, milliseconds): - self.interval = datetime.timedelta(0, 0, 0, milliseconds) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"interval"), - (dbus.UInt64(self.interval_milliseconds(), - variant_level=1))) - - # 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) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"timeout"), - (dbus.UInt64(self.timeout_milliseconds(), - variant_level=1))) - - # Enable - method - Enable = dbus.service.method(_interface)(enable) - Enable.__name__ = "Enable" - - # StartChecker - method - @dbus.service.method(_interface) - def StartChecker(self): - "D-Bus method" - self.start_checker() - - # Disable - method - @dbus.service.method(_interface) - def Disable(self): - "D-Bus method" - self.disable() - - # StopChecker - method - StopChecker = dbus.service.method(_interface)(stop_checker) - StopChecker.__name__ = "StopChecker" - - del _interface 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): + 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))) + 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] @@ -604,55 +401,50 @@ 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)))) + 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))) + 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)) + 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))) + 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() + buffer = ctypes.create_string_buffer(20) + buffer_length = ctypes.c_size_t() # Get the fingerprint from the certificate into the buffer - (gnutls.library.functions - .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), - ctypes.byref(buf_len))) + gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\ + (crt, ctypes.byref(buffer), ctypes.byref(buffer_length)) # Deinit the certificate gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) # Convert the buffer to a Python bytestring - fpr = ctypes.string_at(buf, buf_len.value) + fpr = ctypes.string_at(buffer, buffer_length.value) # Convert the bytestring to hexadecimal notation hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr) return hex_fpr -class TCP_handler(SocketServer.BaseRequestHandler, object): +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): logger.info(u"TCP connection from: %s", - unicode(self.client_address)) - session = (gnutls.connection - .ClientSession(self.request, - gnutls.connection - .X509Credentials())) + 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) @@ -671,11 +463,12 @@ #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)) + priority = "NORMAL" # Fallback default, since this + # MUST be set. + if self.server.settings["priority"]: + priority = self.server.settings["priority"] + gnutls.library.functions.gnutls_priority_set_direct\ + (session._c_object, priority, None); try: session.handshake() @@ -691,11 +484,12 @@ session.bye() return logger.debug(u"Fingerprint: %s", fpr) + client = None for c in self.server.clients: if c.fingerprint == fpr: client = c break - else: + if not client: logger.warning(u"Client not found for fingerprint: %s", fpr) session.bye() @@ -708,8 +502,6 @@ 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:]) @@ -720,13 +512,11 @@ session.bye() -class IPv6_TCPServer(SocketServer.ForkingMixIn, - SocketServer.TCPServer, object): +class IPv6_TCPServer(SocketServer.ForkingTCPServer, 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 """ address_family = socket.AF_INET6 def __init__(self, *args, **kwargs): @@ -736,8 +526,7 @@ if "clients" in kwargs: self.clients = kwargs["clients"] del kwargs["clients"] - self.enabled = False - super(IPv6_TCPServer, self).__init__(*args, **kwargs) + return super(type(self), self).__init__(*args, **kwargs) def server_bind(self): """This overrides the normal server_bind() function to bind to an interface if one was specified, and also NOT to @@ -772,12 +561,7 @@ # if_nametoindex # (self.settings # ["interface"])) - return super(IPv6_TCPServer, self).server_bind() - def server_activate(self): - if self.enabled: - return super(IPv6_TCPServer, self).server_activate() - def enable(self): - self.enabled = True + return super(type(self), self).server_bind() def string_to_delta(interval): @@ -799,8 +583,8 @@ timevalue = datetime.timedelta(0) for s in interval.split(): try: - suffix = unicode(s[-1]) - value = int(s[:-1]) + suffix=unicode(s[-1]) + value=int(s[:-1]) if suffix == u"d": delta = datetime.timedelta(value) elif suffix == u"s": @@ -840,15 +624,16 @@ 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)) + raise AvahiGroupError("State changed: %s", str(error)) def if_nametoindex(interface): """Call the C function if_nametoindex(), or equivalent""" global if_nametoindex try: - if_nametoindex = (ctypes.cdll.LoadLibrary - (ctypes.util.find_library("c")) - .if_nametoindex) + if "ctypes.util" not in sys.modules: + import ctypes.util + if_nametoindex = ctypes.cdll.LoadLibrary\ + (ctypes.util.find_library("c")).if_nametoindex except (OSError, AttributeError): if "struct" not in sys.modules: import struct @@ -857,9 +642,10 @@ def if_nametoindex(interface): "Get an interface index the hard way, i.e. using fcntl()" SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h - with closing(socket.socket()) as s: - ifreq = fcntl.ioctl(s, SIOCGIFINDEX, - struct.pack("16s16x", interface)) + s = socket.socket() + ifreq = fcntl.ioctl(s, SIOCGIFINDEX, + struct.pack("16s16x", interface)) + s.close() interface_index = struct.unpack("I", ifreq[16:20])[0] return interface_index return if_nametoindex(interface) @@ -889,6 +675,9 @@ def main(): + global main_loop_started + main_loop_started = False + parser = OptionParser(version = "%%prog %s" % version) parser.add_option("-i", "--interface", type="string", metavar="IF", help="Bind to interface IF") @@ -896,7 +685,7 @@ 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", + 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" @@ -909,11 +698,7 @@ default="/etc/mandos", metavar="DIR", help="Directory to search for configuration" " files") - parser.add_option("--no-dbus", action="store_false", - dest="use_dbus", - help="Do not provide D-Bus system bus" - " interface") - options = parser.parse_args()[0] + (options, args) = parser.parse_args() if options.check: import doctest @@ -928,7 +713,6 @@ "priority": "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", "servicename": "Mandos", - "use_dbus": "True", } # Parse config file for server-global settings @@ -937,34 +721,29 @@ server_config.read(os.path.join(options.configdir, "mandos.conf")) # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() - # Use getboolean on the boolean config options - server_settings["debug"] = (server_config.getboolean - ("DEFAULT", "debug")) - server_settings["use_dbus"] = (server_config.getboolean - ("DEFAULT", "use_dbus")) + # Use getboolean on the boolean config option + server_settings["debug"] = server_config.getboolean\ + ("DEFAULT", "debug") del server_config # Override the settings from the config file with command line # options, if set. for option in ("interface", "address", "port", "debug", - "priority", "servicename", "configdir", - "use_dbus"): + "priority", "servicename", "configdir"): value = getattr(options, option) if value is not None: server_settings[option] = value del options # Now we have our good server settings in "server_settings" - # For convenience debug = server_settings["debug"] - use_dbus = server_settings["use_dbus"] if not debug: syslogger.setLevel(logging.WARNING) console.setLevel(logging.WARNING) if server_settings["servicename"] != "Mandos": - syslogger.setFormatter(logging.Formatter + syslogger.setFormatter(logging.Formatter\ ('Mandos (%s): %%(levelname)s:' ' %%(message)s' % server_settings["servicename"])) @@ -972,52 +751,19 @@ # Parse config file with clients client_defaults = { "timeout": "1h", "interval": "5m", - "checker": "fping -q -- %%(host)s", + "checker": "fping -q -- %(host)s", "host": "", } 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 - gid = pwd.getpwnam("_mandos").pw_gid - except KeyError: - try: - uid = pwd.getpwnam("mandos").pw_uid - gid = pwd.getpwnam("mandos").pw_gid - except KeyError: - try: - uid = pwd.getpwnam("nobody").pw_uid - gid = pwd.getpwnam("nogroup").pw_gid - except KeyError: - uid = 65534 - gid = 65534 - try: - os.setuid(uid) - os.setgid(gid) - except OSError, error: - if error[0] != errno.EPERM: - raise error - global service service = AvahiService(name = server_settings["servicename"], - servicetype = "_mandos._tcp", ) + type = "_mandos._tcp", ); if server_settings["interface"]: - service.interface = (if_nametoindex - (server_settings["interface"])) + service.interface = if_nametoindex\ + (server_settings["interface"]) global main_loop global bus @@ -1030,17 +776,22 @@ avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) # End of Avahi example code - if use_dbus: - bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", - bus) + + clients = Set() + def remove_from_clients(client): + clients.remove(client) + if not clients: + logger.critical(u"No clients left, exiting") + sys.exit() clients.update(Set(Client(name = section, + stop_hook = remove_from_clients, config - = dict(client_config.items(section)), - use_dbus = use_dbus) + = dict(client_config.items(section))) for section in client_config.sections())) if not clients: - logger.warning(u"No clients defined") + logger.critical(u"No clients defined") + sys.exit(1) if debug: # Redirect stdin so all checkers get /dev/null @@ -1054,18 +805,16 @@ # Close all input and output, do double fork, etc. daemon() + pidfilename = "/var/run/mandos/mandos.pid" + pid = os.getpid() try: - pid = os.getpid() + pidfile = open(pidfilename, "w") 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 + except IOError, err: + logger.error(u"Could not write %s file with PID %d", + pidfilename, os.getpid()) def cleanup(): "Cleanup function; run on exit" @@ -1078,8 +827,8 @@ while clients: client = clients.pop() - client.disable_hook = None - client.disable() + client.stop_hook = None + client.stop() atexit.register(cleanup) @@ -1088,65 +837,14 @@ signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) - if use_dbus: - 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: - clients.remove(c) - # Don't signal anything except ClientRemoved - c.use_dbus = False - c.disable() - # Emit D-Bus signal - self.ClientRemoved(object_path) - return - raise KeyError - @dbus.service.method(_interface) - def Quit(self): - main_loop.quit() - - del _interface - - mandos_server = MandosServer() - for client in clients: - if use_dbus: - # Emit D-Bus signal - mandos_server.ClientAdded(client.dbus_object_path, - client.GetAllProperties()) - client.enable() - - tcp_server.enable() - tcp_server.server_activate() - + client.start() + + tcp_server = IPv6_TCPServer((server_settings["address"], + server_settings["port"]), + tcp_handler, + settings=server_settings, + clients=clients) # Find out what port we got service.port = tcp_server.socket.getsockname()[1] logger.info(u"Now listening on address %r, port %d, flowinfo %d," @@ -1166,13 +864,14 @@ gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN, lambda *args, **kwargs: - (tcp_server.handle_request - (*args[2:], **kwargs) or True)) + tcp_server.handle_request\ + (*args[2:], **kwargs) or True) logger.debug(u"Starting main loop") + main_loop_started = True main_loop.run() except AvahiError, error: - logger.critical(u"AvahiError: %s", error) + logger.critical(u"AvahiError: %s" + unicode(error)) sys.exit(1) except KeyboardInterrupt: if debug: === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2009-01-08 03:54:06 +0000 +++ mandos-clients.conf.xml 2008-08-25 07:52:35 +0000 @@ -1,20 +1,17 @@ - + /etc/mandos/clients.conf"> - - -%common; ]> - + - Mandos Manual + &CONFNAME; - Mandos - &version; - &TIMESTAMP; + &CONFNAME; + &VERSION; Björn @@ -33,13 +30,34 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &CONFNAME; 5 @@ -51,11 +69,13 @@ Configuration file for the Mandos server - + - &CONFPATH; + + &CONFPATH; + - + DESCRIPTION @@ -95,18 +115,17 @@ start time expansion, see . - Unknown options are ignored. The used options are as follows: + Uknown options are ignored. The used options are as follows: - + - + - + timeout - - This option is optional. - + timeout = TIME + The timeout is how long the server will wait for a successful checker run until a client is considered @@ -128,14 +147,13 @@ - + - + interval - - This option is optional. - + interval = TIME + How often to run the checker to confirm that a client is still up. Note: a new checker will @@ -150,15 +168,14 @@ as for timeout above. - - + + - + checker - - This option is optional. - + checker = COMMAND + This option allows you to override the default shell command that the server will use to check if the client is @@ -170,7 +187,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 @@ -181,12 +198,11 @@ - + fingerprint - - This option is required. - + fingerprint = HEXSTRING + This option sets the OpenPGP fingerprint that identifies the public key that clients authenticate themselves with @@ -197,14 +213,11 @@ - + secret - - If this option is not specified, the option is required - to be present. - + secret = BASE64_ENCODED_DATA + If present, this option must be set to a string of base64-encoded binary data. It will be decoded and sent @@ -223,42 +236,39 @@ lines is that a line beginning with white space adds to the value of the previous line, RFC 822-style. - - - - - - - - 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. - + + If this option is not specified, the option is used instead, but one of them + must be present. + + + + + + secfile + + secfile = FILENAME + + + The same as , but the secret data + is in an external file. The contents of the file should + not be base64-encoded, but will be + sent to clients verbatim. + + + This option is only used, and must be + present, if is not specified. + + + + + + host + + host = STRING + Host name for this client. This is not used by the server directly, but can be, and is by default, used by the @@ -268,7 +278,7 @@ - + EXPANSION @@ -317,11 +327,11 @@ percent characters in a row (%%%%) must be entered. Also, a bad format here will lead to an immediate but silent run-time fatal exit; debug - mode is needed to expose an error of this kind. + mode is needed to track down an error of this kind. - - + + FILES @@ -351,7 +361,7 @@ [DEFAULT] timeout = 1h interval = 5m -checker = fping -q -- %%(host)s +checker = fping -q -- %(host)s # Client "foo" [foo] @@ -380,24 +390,21 @@ fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 secfile = /etc/mandos/bar-secret timeout = 15m + - + SEE ALSO - mandos-keygen - 8, - mandos.conf - 5, - mandos - 8 + + mandos + 8, + mandos-keygen + 8, + mandos.conf + 5 - - - - - === modified file 'mandos-keygen' --- mandos-keygen 2009-01-06 02:42:53 +0000 +++ mandos-keygen 2008-08-25 03:53:42 +0000 @@ -2,8 +2,7 @@ # # Mandos key generator - create a new OpenPGP key for a Mandos client # -# Copyright © 2008,2009 Teddy Hogeborn -# Copyright © 2008,2009 Björn Påhlsson +# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,14 +20,14 @@ # Contact the authors at . # -VERSION="1.0.3" +VERSION="1.0" -KEYDIR="/etc/keys/mandos" +KEYDIR="/etc/mandos" KEYTYPE=DSA KEYLENGTH=2048 SUBKEYTYPE=ELG-E SUBKEYLENGTH=2048 -KEYNAME="`hostname --fqdn 2>/dev/null || hostname`" +KEYNAME="`hostname --fqdn`" KEYEMAIL="" KEYCOMMENT="Mandos client key" KEYEXPIRE=0 @@ -36,13 +35,9 @@ KEYCOMMENT_ORIG="$KEYCOMMENT" mode=keygen -if [ ! -d "$KEYDIR" ]; then - KEYDIR="/etc/mandos/keys" -fi - # Parse options -TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \ - --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \ +TEMP=`getopt --options vhd:t:l:n:e:c:x:f \ + --longoptions version,help,password,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \ --name "$0" -- "$@"` help(){ @@ -54,7 +49,6 @@ $basename [ OPTIONS ] Encrypted password creation: $basename { -p | --password } [ --name NAME ] [ --dir DIR] - $basename { -F | --passfile } FILE [ --name NAME ] [ --dir DIR] Key creation options: -v, --version Show program's version number and exit @@ -70,22 +64,18 @@ -n NAME, --name NAME Name of key. Default is the FQDN. -e ADDRESS, --email ADDRESS Email address of key. Default is empty. - -c TEXT, --comment TEXT + -c COMMENT, --comment COMMENT Comment field for key. The default value is "Mandos client key". -x TIME, --expire TIME Key expire time. Default is no expiration. See gpg(1) for syntax. - -f, --force Force overwriting old key files. + -f, --force Force overwriting old keys. Password creation options: - -p, --password Create an encrypted password using the key in - the key directory. All options other than - --dir and --name are ignored. - -F FILE, --passfile FILE - Encrypt a password from FILE using the key in - the key directory. All options other than - --dir and --name are ignored. + -p, --password Create an encrypted password using the keys in + the key directory. All options other than + --keydir and --name are ignored. EOF } @@ -93,7 +83,6 @@ while :; do case "$1" in -p|--password) mode=password; shift;; - -F|--passfile) mode=password; PASSFILE="$2"; shift 2;; -d|--dir) KEYDIR="$2"; shift 2;; -t|--type) KEYTYPE="$2"; shift 2;; -s|--subtype) SUBKEYTYPE="$2"; shift 2;; @@ -119,20 +108,21 @@ PUBKEYFILE="$KEYDIR/pubkey.txt" # Check for some invalid values -if [ ! -d "$KEYDIR" ]; then +if [ -d "$KEYDIR" ]; then :; else echo "$KEYDIR not a directory" >&2 exit 1 fi -if [ ! -r "$KEYDIR" ]; then - echo "Directory $KEYDIR not readable" >&2 +if [ -w "$KEYDIR" ]; then :; else + echo "Directory $KEYDIR not writeable" >&2 + exit 1 +fi + +if [ "$mode" = password -a -e "$KEYDIR/trustdb.gpg.lock" ]; then + echo "Key directory has locked trustdb; aborting." >&2 exit 1 fi if [ "$mode" = keygen ]; then - if [ ! -w "$KEYDIR" ]; then - echo "Directory $KEYDIR not writeable" >&2 - exit 1 - fi if [ -z "$KEYTYPE" ]; then echo "Empty key type" >&2 exit 1 @@ -159,8 +149,8 @@ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;; esac - if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \ - -a "$FORCE" -eq 0 ]; then + if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ]; } \ + && [ "$FORCE" -eq 0 ]; then echo "Refusing to overwrite old key files; use --force" >&2 exit 1 fi @@ -174,28 +164,35 @@ fi # Create temporary gpg batch file - BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`" + BATCHFILE="`mktemp -t mandos-gpg-batch.XXXXXXXXXX`" fi if [ "$mode" = password ]; then # Create temporary encrypted password file - SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`" -fi - -# Create temporary key ring directory -RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`" + SECFILE="`mktemp -t mandos-gpg-secfile.XXXXXXXXXX`" +fi + +# Create temporary key rings +SECRING="`mktemp -t mandos-gpg-secring.XXXXXXXXXX`" +PUBRING="`mktemp -t mandos-gpg-pubring.XXXXXXXXXX`" + +if [ "$mode" = password ]; then + # If a trustdb.gpg file does not already exist, schedule it for + # deletion when we are done. + if ! [ -e "$KEYDIR/trustdb.gpg" ]; then + TRUSTDB="$KEYDIR/trustdb.gpg" + fi +fi # Remove temporary files on exit trap " set +e; \ -test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \ -shred --remove \"$RINGDIR\"/sec*; -test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \ -rm --recursive --force \"$RINGDIR\"; +rm --force $PUBRING ${PUBRING}~ $BATCHFILE $TRUSTDB; \ +shred --remove $SECRING $SECFILE; \ stty echo; \ " EXIT -umask 077 +umask 027 if [ "$mode" = keygen ]; then # Create batch file for GnuPG @@ -212,17 +209,18 @@ Expire-Date: $KEYEXPIRE #Preferences: #Handle: - #%pubring pubring.gpg - #%secring secring.gpg + %pubring $PUBRING + %secring $SECRING %commit EOF # Generate a new key in the key rings - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --trust-model always \ + gpg --no-random-seed-file --quiet --batch --no-tty \ + --no-default-keyring --no-options --enable-dsa2 \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ --gen-key "$BATCHFILE" rm --force "$BATCHFILE" - + # Backup any old key files if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \ 2>/dev/null; then @@ -242,68 +240,64 @@ FILECOMMENT="$FILECOMMENT <$KEYEMAIL>" fi - # Export key from key rings to key files - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --armor --export-options export-minimal \ - --comment "$FILECOMMENT" --output "$SECKEYFILE" \ - --export-secret-keys - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --armor --export-options export-minimal \ - --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export + # Export keys from key rings to key files + gpg --no-random-seed-file --quiet --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ + --export-options export-minimal --comment "$FILECOMMENT" \ + --output "$SECKEYFILE" --export-secret-keys + gpg --no-random-seed-file --quiet --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ + --export-options export-minimal --comment "$FILECOMMENT" \ + --output "$PUBKEYFILE" --export fi if [ "$mode" = password ]; then - # Import key into temporary key rings - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --trust-model always --armor \ - --import "$SECKEYFILE" - gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --trust-model always --armor \ - --import "$PUBKEYFILE" - + # Import keys into temporary key rings + gpg --no-random-seed-file --quiet --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --homedir "$KEYDIR" --no-permission-warning \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ + --trust-model always --import "$SECKEYFILE" + gpg --no-random-seed-file --quiet --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --homedir "$KEYDIR" --no-permission-warning \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ + --trust-model always --import "$PUBKEYFILE" + # Get fingerprint of key - FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \ - --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \ - --fingerprint --with-colons \ - | sed --quiet \ - --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`" + FINGERPRINT="`gpg --no-random-seed-file --quiet --batch --no-tty \ + --armor --no-default-keyring --no-options --enable-dsa2 \ + --homedir \"$KEYDIR\" --no-permission-warning \ + --secret-keyring \"$SECRING\" --keyring \"$PUBRING\" \ + --trust-model always --fingerprint --with-colons \ + | sed -n -e '/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`" test -n "$FINGERPRINT" FILECOMMENT="Encrypted password for a Mandos client" - if [ -n "$PASSFILE" ]; then - cat "$PASSFILE" - else - stty -echo - echo -n "Enter passphrase: " >&2 - first="$(head --lines=1 | tr --delete '\n')" - echo -n -e "\nRepeat passphrase: " >&2 - second="$(head --lines=1 | tr --delete '\n')" - echo >&2 - stty echo - if [ "$first" != "$second" ]; then - echo -e "Passphrase mismatch" >&2 - false - else - echo -n "$first" - fi - fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --trust-model always --armor --encrypt \ - --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \ + stty -echo + echo -n "Enter passphrase: " >&2 + sed -e '1q' \ + | gpg --no-random-seed-file --batch --no-tty --armor \ + --no-default-keyring --no-options --enable-dsa2 \ + --homedir "$KEYDIR" --no-permission-warning \ + --secret-keyring "$SECRING" --keyring "$PUBRING" \ + --trust-model always --encrypt --recipient "$FINGERPRINT" \ + --comment "$FILECOMMENT" \ > "$SECFILE" - status="${PIPESTATUS[0]}" - if [ "$status" -ne 0 ]; then - exit "$status" - fi + echo >&2 + stty echo cat <<-EOF [$KEYNAME] host = $KEYNAME fingerprint = $FINGERPRINT secret = - EOF - sed --quiet --expression=' +EOF + sed -n -e ' /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{ /^$/,${ # Remove 24-bit Radix-64 checksum @@ -322,5 +316,9 @@ shred --remove "$SECFILE" fi # Remove the key rings -shred --remove "$RINGDIR"/sec* -rm --recursive --force "$RINGDIR" +shred --remove "$SECRING" +rm --force "$PUBRING" "${PUBRING}~" +# Remove the trustdb, if one did not exist when we started +if [ -n "$TRUSTDB" ]; then + rm --force "$TRUSTDB" +fi === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2009-01-04 21:54:55 +0000 +++ mandos-keygen.xml 2008-08-25 03:53:42 +0000 @@ -1,19 +1,16 @@ - - -%common; ]> - Mandos Manual + &COMMANDNAME; - Mandos - &version; - &TIMESTAMP; + &COMMANDNAME; + &VERSION; Björn @@ -32,13 +29,34 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &COMMANDNAME; 8 @@ -47,169 +65,174 @@ &COMMANDNAME; - Generate key and password for Mandos client and server. + Generate keys for password-request + 8mandos - + &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + directory + + + + type + + + + bits + + + + type + + + + bits + + + + NAME + + + + EMAIL + + + + COMMENT + + + + TIME + + + + + + + &COMMANDNAME; + + + directory + + + + type + + + + bits + + + + type + + + + bits + + + + NAME + + + + EMAIL + + + + COMMENT + + + + TIME + + + + &COMMANDNAME; + - - - - FILE - - - - - - - - - - + + + + directory + + + + NAME &COMMANDNAME; + - &COMMANDNAME; + - - + DESCRIPTION &COMMANDNAME; is a program to generate the - OpenPGP key used by - mandos-client - 8mandos. The key is + OpenPGP keys used by + password-request + 8mandos. The keys are normally written to /etc/mandos for later installation into the - initrd image, but this, and most other things, can be changed - with command line options. + initrd image, but this, like most things, can be changed with + command line options. - This program can also be used with the - or - options to generate a ready-made section for - clients.conf (see + It can also be used to generate ready-made sections for mandos-clients.conf - 5). + 5 using the + option. PURPOSE + The purpose of this is to enable remote and unattended rebooting of client host computer with an encrypted root file system. See for details. + OPTIONS - + - - + -h, --help Show a help message and exit - + - - + -d, --dir + directory Target directory for key files. Default is @@ -217,36 +240,30 @@ - + - - + -t, --type + type Key type. Default is DSA. - + - - + -l, --length + bits Key length in bits. Default is 2048. - + - - + -s, --subtype + type Subkey type. Default is ELG-E (Elgamal @@ -254,36 +271,30 @@ - + - - + -L, --sublength + bits Subkey length in bits. Default is 2048. - + - - + -e, --email + address Email address of key. Default is empty. - + - - + -c, --comment + comment Comment field for key. The default value is @@ -291,12 +302,10 @@ - + - - + -x, --expire + time Key expire time. Default is no expiration. See @@ -305,19 +314,18 @@ - + - - + -f, --force - Force overwriting old key. + Force overwriting old keys. - - + -p, --password Prompt for a password and encrypt it with the key already @@ -329,41 +337,27 @@ >8. The host name or the name specified with the option is used for the section header. All other options are ignored, - and no key is created. - - - - - - - - - The same as , but read from - FILE, not the terminal. + and no keys are created. - + 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. + new Mandos clients. - + EXIT STATUS - The exit status will be 0 if a new key (or password, if the - option was used) was successfully - created, otherwise not. + The exit status will be 0 if new keys were successfully created, + otherwise not. @@ -371,7 +365,7 @@ ENVIRONMENT - TMPDIR + TMPDIR If set, temporary files will be created here. See @@ -383,7 +377,7 @@ - + FILES Use the option to change where @@ -420,13 +414,14 @@ - - - - - - - + + + BUGS + + None are known at this time. + + + EXAMPLE @@ -434,78 +429,48 @@ Normal invocation needs no options: - &COMMANDNAME; + mandos-keygen - Create key in another directory and of another type. Force + Create keys in another directory and of another type. Force overwriting old key files: -&COMMANDNAME; --dir ~/keydir --type RSA --force - - - - - - Prompt for a password, encrypt it with the key in - /etc/mandos and output a section suitable - for clients.conf. - - - &COMMANDNAME; --password - - - - - Prompt for a password, encrypt it with the key in the - client-key directory and output a section - suitable for clients.conf. - - - - -&COMMANDNAME; --password --dir client-key +mandos-keygen --dir ~/keydir --type RSA --force - + SECURITY The , , , and - options can be used to create keys of low security. If in - doubt, leave them to the default values. + options can be used to create keys of insufficient security. If + in doubt, leave them to the default values. - The key expire time is not guaranteed to be - honored by mandos + The key expire time is not guaranteed to be honored by + mandos 8. - + SEE ALSO + password-request + 8mandos, + mandos + 8, gpg - 1, - mandos-clients.conf - 5, - mandos - 8, - mandos-client - 8mandos + 1 - - - - - === removed file 'mandos-list' --- mandos-list 2008-12-21 19:19:25 +0000 +++ mandos-list 1970-01-01 00:00:00 +0000 @@ -1,65 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; coding: utf-8 -*- - -import dbus -from optparse import OptionParser -import locale - -locale.setlocale(locale.LC_ALL, u'') - -tablewords = { - 'name': u'Name', - 'enabled': u'Enabled', - 'timeout': u'Timeout', - 'last_checked_ok': u'Last Successful Check', - 'created': u'Created', - 'interval': u'Interval', - 'host': u'Host', - 'fingerprint': u'Fingerprint', - 'checker_running': u'Check Is Running', - 'last_enabled': u'Last Enabled', - 'checker': u'Checker', - } -busname = 'org.mandos-system.Mandos' -object_path = '/Mandos' -interface = 'org.mandos_system.Mandos' -version = "1.0.2" -defaultkeywords = ('name', 'enabled', 'timeout', 'last_checked_ok', - 'checker') - -parser = OptionParser(version = "%%prog %s" % version) -parser.add_option("-a", "--all", action="store_true", default=False, - help="Print all fields") -options = parser.parse_args()[0] -if options.all: - keywords = ('name', 'enabled', 'timeout', 'last_checked_ok', - 'created', 'interval', 'host', 'fingerprint', - 'checker_running', 'last_enabled', 'checker') -else: - keywords = defaultkeywords - - -bus = dbus.SystemBus() -mandos_dbus_objc = bus.get_object(busname, object_path) -mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface = interface) -mandos_clients = mandos_serv.GetAllClientsWithProperties() - -def valuetostring(x): - if type(x) is dbus.Boolean: - return u"Yes" if x else u"No" - else: - return unicode(x) - -format_string = u' '.join(u'%%-%ds' - % max(len(tablewords[key]), - max(len(valuetostring(client[key])) - for client - in mandos_clients.itervalues())) - for key in keywords) -print format_string % tuple(tablewords[key] for key in keywords) -for client in mandos_clients.itervalues(): - print format_string % tuple(valuetostring(client[key]) - for key in keywords) - - === modified file 'mandos-options.xml' --- mandos-options.xml 2008-12-29 02:44:54 +0000 +++ mandos-options.xml 2008-08-25 10:41:16 +0000 @@ -5,8 +5,6 @@
@@ -17,8 +15,8 @@ and listen to requests on the specified network interface. Default is to use all available interfaces. Note: a failure to bind to the specified - interface is not considered critical, and the server will not - exit, but instead continue normally. + interface is not considered critical, and the server does not + exit, but will instead continue normally. @@ -45,31 +43,24 @@ - GnuTLS priority string for the TLS handshake. - The default is SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP. See - gnutls_priority_init - 3 for the syntax. - Warning: changing this may make the - TLS handshake fail, making server-client - communication impossible. + GnuTLS priority string for the TLS handshake with the clients. + The default is + SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP. + See gnutls_priority_init + 3 for the + syntax. Warning: changing this may make the + TLS handshake fail, making communication with clients impossible. Zeroconf service name. The default is Mandos. This only needs to be - changed if for some reason is would be necessary to run more than - one server on the same host. This would not + changed this if it, for some reason, is necessary to run more than + one server on the same host, which would not normally be useful. If there are name collisions on the same network, the newer server will automatically rename itself to Mandos #2, and so on; therefore, this option is not needed in that case. - - - This option controls whether the server will provide a D-Bus - system bus interface. The default is to provide such an - interface. -
=== modified file 'mandos.conf' --- mandos.conf 2008-12-29 02:44:54 +0000 +++ mandos.conf 2008-08-18 23:55:28 +0000 @@ -36,6 +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 === modified file 'mandos.conf.xml' --- mandos.conf.xml 2009-01-04 21:54:55 +0000 +++ mandos.conf.xml 2008-08-25 10:41:16 +0000 @@ -1,20 +1,17 @@ - + /etc/mandos/mandos.conf"> - - -%common; ]> - Mandos Manual + &CONFNAME; - Mandos - &version; - &TIMESTAMP; + &CONFNAME; + &VERSION; Björn @@ -33,13 +30,34 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &CONFNAME; 5 @@ -51,11 +69,13 @@ Configuration file for the Mandos server - + - &CONFPATH; + + &CONFPATH; + - + DESCRIPTION @@ -73,75 +93,76 @@ # or ; are ignored and may be used to provide comments. - + OPTIONS - + interface + interface = NAME + - + - + address + address = ADDRESS + - + - + port + port = NUMBER + - + - + + debug = { 1 | yes | true | on | 0 | no | false | off } - + >false | off } + - + - + priority + priority = STRING + - + - + servicename + servicename = NAME + - - - - - - - @@ -157,7 +178,7 @@ The [DEFAULT] is necessary because the Python built-in module ConfigParser - requires it. + requres it. @@ -184,7 +205,6 @@ debug = true priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP servicename = Daena -use_dbus = False @@ -192,14 +212,15 @@ SEE ALSO - gnutls_priority_init3, - mandos - 8, - mandos-clients.conf - 5 + + mandos + 8, + mandos-clients.conf + 5, + gnutls_priority_init + 3 - + @@ -247,8 +268,3 @@ - - - - - === removed file 'mandos.lsm' --- mandos.lsm 2009-01-06 02:42:53 +0000 +++ mandos.lsm 1970-01-01 00:00:00 +0000 @@ -1,22 +0,0 @@ -Begin4 -Title: Mandos -Version: 1.0.3 -Entered-date: 2009-01-06 -Description: The Mandos system allows computers to have encrypted -root file systems and at the same time be capable of remote and/or -unattended reboots. -Keywords: boot, encryption, luks, cryptsetup, network, openpgp, -tls, dm-crypt -Author: teddy@fukt.bsnet.se (Teddy Hogeborn), - belorn@fukt.bsnet.se (Björn Påhlsson) -Maintained-by: teddy@fukt.bsnet.se (Teddy Hogeborn), - belorn@fukt.bsnet.se (Björn Påhlsson) -Primary-site: http://www.fukt.bsnet.se/mandos - 92K mandos_1.0.3.orig.tar.gz -Alternate-site: ftp://ftp.fukt.bsnet.se/pub/mandos - 92K mandos_1.0.3.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 -End === modified file 'mandos.xml' --- mandos.xml 2009-01-04 21:54:55 +0000 +++ mandos.xml 2008-08-29 05:53:59 +0000 @@ -1,19 +1,16 @@ - - -%common; ]> - - Mandos Manual + + &COMMANDNAME; - Mandos - &version; - &TIMESTAMP; + &COMMANDNAME; + &VERSION; Björn @@ -32,13 +29,34 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &COMMANDNAME; 8 @@ -47,64 +65,48 @@ &COMMANDNAME; - Gives encrypted passwords to authenticated Mandos clients + Sends encrypted passwords to authenticated Mandos clients - + &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - + --interfaceNAME + --addressADDRESS + --portPORT + --priorityPRIORITY + --servicenameNAME + --configdirDIRECTORY + --debug + + + &COMMANDNAME; + -iNAME + -aADDRESS + -pPORT + --priorityPRIORITY + --servicenameNAME + --configdirDIRECTORY + --debug &COMMANDNAME; - - + -h + --help &COMMANDNAME; - + --version &COMMANDNAME; - + --check - + DESCRIPTION @@ -119,63 +121,60 @@ Any authenticated client is then given the stored pre-encrypted password for that specific client. + PURPOSE + The purpose of this is to enable remote and unattended rebooting of client host computer with an encrypted root file system. See for details. + OPTIONS + - - + -h, --help Show a help message and exit - + - - NAME - - NAME + -i, --interface NAME - + - - + -a, --address + ADDRESS - + - - + -p, --port + PORT - + - + --check Run the server’s self-tests. This includes any unit @@ -183,34 +182,34 @@ - + - + --debug - + - + --priority + PRIORITY - + - + --servicename NAME + - + - + --configdir DIR + Directory to search for configuration files. Default is @@ -222,38 +221,28 @@ - + - + --version Prints the program version and exit. - - - - - - - See also . - - - - + OVERVIEW This program is the server part. It is a normal server program and will run in a normal system environment, not in an initial - RAM disk environment. + RAM disk environment. - + NETWORK PROTOCOL @@ -311,7 +300,7 @@ - + CHECKING @@ -325,7 +314,7 @@ 5. - + LOGGING @@ -335,16 +324,6 @@ and also show them on the console. - - - 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. - - - EXIT STATUS @@ -353,12 +332,12 @@ critical error is encountered. - + ENVIRONMENT - PATH + PATH To start the configured checker (see - - + + FILES Use the option to change where @@ -403,7 +382,7 @@ - /var/run/mandos.pid + /var/run/mandos/mandos.pid The file containing the process id of @@ -444,7 +423,7 @@ 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 @@ -458,11 +437,7 @@ Debug mode is conflated with running in the foreground. - The console log messages does not show a time stamp. - - - This server does not check the expire time of clients’ OpenPGP - keys. + The console log messages does not show a timestamp. @@ -503,19 +478,19 @@ - + SECURITY - + SERVER Running this &COMMANDNAME; server program should not in itself present any security risk to the host - computer running it. The program switches to a non-root user - soon after startup. + computer running it. The program does not need any special + privileges to run, and is designed to run as a non-root user. - + CLIENTS The server only gives out its stored data to clients which @@ -528,7 +503,7 @@ mandos-clients.conf 5) must be made non-readable by anyone - except the user starting the server (usually root). + except the user running the server. As detailed in , the status of all @@ -545,29 +520,29 @@ 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 + initial RAM image of the client host. What should be done in + that case (if restarting the server program really is + necessary) is to stop the server program, edit the configuration file to omit any suspect clients, and restart the server program. For more details on client-side security, see - mandos-client + password-request 8mandos. - + SEE ALSO + mandos.conf + 5, mandos-clients.conf 5, - mandos.conf - 5, - mandos-client + password-request 8mandos, sh1 @@ -676,8 +651,3 @@ - - - - - === modified file 'overview.xml' --- overview.xml 2008-09-13 15:36:18 +0000 +++ overview.xml 2008-08-23 07:17:28 +0000 @@ -1,17 +1,15 @@ - This is part of the Mandos system for allowing computers to have - encrypted root file systems and at the same time be capable of - remote and/or unattended reboots. The computers run a small client - program in the initial RAM disk environment which - will communicate with a server over a network. All network - communication is encrypted using TLS. The - clients are identified by the server using an OpenPGP key; each - client has one unique to it. The server sends the clients an - encrypted password. The encrypted password is decrypted by the - clients using the same OpenPGP key, and the password is then used to - unlock the root file system, whereupon the computers can continue - booting normally. + encrypted root file systems and also be capable of remote and + unattended reboots. The computers run a small client program in the + initial RAM disk environment which will communicate with a server + over a network. The clients are identified by the server using a + OpenPGP key; each client has one unique to it. The server sends the + clients an encrypted password. The encrypted password is decrypted + by the clients using the same OpenPGP key, and the password is then + used to unlock the root file system, whereupon the computers can + continue booting normally. === modified file 'plugin-runner.c' --- plugin-runner.c 2009-01-14 14:20:17 +0000 +++ plugin-runner.c 2008-08-25 07:53:43 +0000 @@ -2,8 +2,7 @@ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 Björn Påhlsson + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,8 +27,8 @@ #include /* malloc(), exit(), EXIT_FAILURE, EXIT_SUCCESS, realloc() */ #include /* bool, true, false */ -#include /* perror, fileno(), fprintf(), - stderr, STDOUT_FILENO */ +#include /* perror, popen(), fileno(), + fprintf(), stderr, STDOUT_FILENO */ #include /* DIR, opendir(), stat(), struct stat, waitpid(), WIFEXITED(), WEXITSTATUS(), wait(), pid_t, @@ -47,7 +46,7 @@ fcntl(), setuid(), setgid(), F_GETFD, F_SETFD, FD_CLOEXEC, access(), pipe(), fork(), close() - dup2(), STDOUT_FILENO, _exit(), + dup2, STDOUT_FILENO, _exit(), execv(), write(), read(), close() */ #include /* fcntl(), F_GETFD, F_SETFD, @@ -64,25 +63,18 @@ sigprocmask(), SIG_BLOCK, SIGCHLD, SIG_UNBLOCK, kill() */ #include /* errno, EBADF */ -#include /* intmax_t, SCNdMAX, PRIdMAX, */ #define BUFFER_SIZE 256 #define PDIR "/lib/mandos/plugins.d" #define AFILE "/conf/conf.d/mandos/plugin-runner.conf" -const char *argp_program_version = "plugin-runner " VERSION; +const char *argp_program_version = "plugin-runner 1.0"; const char *argp_program_bug_address = ""; -typedef struct plugin{ - char *name; /* can be NULL or any plugin name */ - char **argv; - int argc; - char **environ; - int envc; - bool disabled; +struct process; - /* Variables used for running processes*/ +typedef struct process{ pid_t pid; int fd; char *buffer; @@ -91,24 +83,29 @@ bool eof; volatile bool completed; volatile int status; + struct process *next; +} process; + +typedef struct plugin{ + char *name; /* can be NULL or any plugin name */ + char **argv; + int argc; + char **environ; + int envc; + bool disabled; struct plugin *next; } plugin; -static plugin *plugin_list = NULL; - -/* Gets an existing plugin based on name, - or if none is found, creates a new one */ -static plugin *getplugin(char *name){ - /* 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))){ +static plugin *getplugin(char *name, plugin **plugin_list){ + for (plugin *p = *plugin_list; p != NULL; p = p->next){ + if ((p->name == name) + or (p->name and name and (strcmp(p->name, name) == 0))){ return p; } } /* Create a new plugin */ plugin *new_plugin = malloc(sizeof(plugin)); - if(new_plugin == NULL){ + if (new_plugin == NULL){ return NULL; } char *copy_name = NULL; @@ -121,18 +118,19 @@ *new_plugin = (plugin) { .name = copy_name, .argc = 1, + .envc = 0, .disabled = false, - .next = plugin_list }; + .next = *plugin_list }; new_plugin->argv = malloc(sizeof(char *) * 2); - if(new_plugin->argv == NULL){ + if (new_plugin->argv == NULL){ free(copy_name); free(new_plugin); return NULL; } new_plugin->argv[0] = copy_name; new_plugin->argv[1] = NULL; - + new_plugin->environ = malloc(sizeof(char *)); if(new_plugin->environ == NULL){ free(copy_name); @@ -141,9 +139,8 @@ return NULL; } new_plugin->environ[0] = NULL; - /* Append the new plugin to the list */ - plugin_list = new_plugin; + *plugin_list = new_plugin; return new_plugin; } @@ -179,36 +176,21 @@ } /* Add to a plugin's environment */ -static bool add_environment(plugin *p, const char *def, bool replace){ +static bool add_environment(plugin *p, const char *def){ if(p == NULL){ return false; } - /* namelen = length of name of environment variable */ - size_t namelen = (size_t)(strchrnul(def, '=') - def); - /* Search for this environment variable */ - for(char **e = p->environ; *e != NULL; e++){ - if(strncmp(*e, def, namelen + 1) == 0){ - /* It already exists */ - if(replace){ - char *new = realloc(*e, strlen(def) + 1); - if(new == NULL){ - return false; - } - *e = new; - strcpy(*e, def); - } - return true; - } - } return add_to_char_array(def, &(p->environ), &(p->envc)); } + /* * Based on the example in the GNU LibC manual chapter 13.13 "File * Descriptor Flags". * *Note File Descriptor Flags:(libc)Descriptor Flags. */ -static int set_cloexec_flag(int fd){ +static int set_cloexec_flag(int fd) +{ int ret = fcntl(fd, F_GETFD, 0); /* If reading the flags failed, return error indication now. */ if(ret < 0){ @@ -218,12 +200,13 @@ return fcntl(fd, F_SETFD, ret | FD_CLOEXEC); } +process *process_list = NULL; /* Mark processes as completed when they exit, and save their exit status. */ -static void handle_sigchld(__attribute__((unused)) int sig){ +void handle_sigchld(__attribute__((unused)) int sig){ while(true){ - plugin *proc = plugin_list; + process *proc = process_list; int status; pid_t pid = waitpid(-1, &status, WNOHANG); if(pid == 0){ @@ -231,13 +214,13 @@ break; } if(pid == -1){ - if(errno != ECHILD){ + if (errno != ECHILD){ perror("waitpid"); } /* No child processes */ break; } - + /* A child exited, find it in process_list */ while(proc != NULL and proc->pid != pid){ proc = proc->next; @@ -251,9 +234,11 @@ } } -/* Prints out a password to stdout */ -static bool print_out_password(const char *buffer, size_t length){ +bool print_out_password(const char *buffer, size_t length){ ssize_t ret; + if(length>0 and buffer[length-1] == '\n'){ + length--; + } for(size_t written = 0; written < length; written += (size_t)ret){ ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written, length - written)); @@ -264,39 +249,18 @@ return true; } -/* Removes and free a plugin from the plugin list */ -static void free_plugin(plugin *plugin_node){ - - for(char **arg = plugin_node->argv; *arg != NULL; arg++){ - free(*arg); - } - free(plugin_node->argv); - for(char **env = plugin_node->environ; *env != NULL; env++){ - free(*env); - } - free(plugin_node->environ); - free(plugin_node->buffer); - - /* Removes the plugin from the singly-linked list */ - if(plugin_node == plugin_list){ - /* First one - simple */ - plugin_list = plugin_list->next; - } else { - /* Second one or later */ - for(plugin *p = plugin_list; p != NULL; p = p->next){ - if(p->next == plugin_node){ - p->next = plugin_node->next; - break; - } - } - } - - free(plugin_node); -} - -static void free_plugin_list(void){ - while(plugin_list != NULL){ - free_plugin(plugin_list); +static void free_plugin_list(plugin *plugin_list){ + for(plugin *next; plugin_list != NULL; plugin_list = next){ + next = plugin_list->next; + for(char **arg = plugin_list->argv; *arg != NULL; arg++){ + free(*arg); + } + free(plugin_list->argv); + for(char **env = plugin_list->environ; *env != NULL; env++){ + free(*env); + } + free(plugin_list->environ); + free(plugin_list); } } @@ -309,9 +273,7 @@ struct dirent *dirst; struct stat st; fd_set rfds_all; - int ret, numchars, maxfd = 0; - ssize_t sret; - intmax_t tmpmax; + int ret, maxfd = 0; uid_t uid = 65534; gid_t gid = 65534; bool debug = false; @@ -342,21 +304,18 @@ { .name = "global-options", .key = 'g', .arg = "OPTION[,OPTION[,...]]", .doc = "Options passed to all plugins" }, - { .name = "global-env", .key = 'G', + { .name = "global-envs", .key = 'e', .arg = "VAR=value", .doc = "Environment variable passed to all plugins" }, { .name = "options-for", .key = 'o', .arg = "PLUGIN:OPTION[,OPTION[,...]]", .doc = "Options passed only to specified plugin" }, - { .name = "env-for", .key = 'E', + { .name = "envs-for", .key = 'f', .arg = "PLUGIN:ENV=value", .doc = "Environment variable passed to specified plugin" }, { .name = "disable", .key = 'd', .arg = "PLUGIN", .doc = "Disable a specific plugin", .group = 1 }, - { .name = "enable", .key = 'e', - .arg = "PLUGIN", - .doc = "Enable a specific plugin", .group = 1 }, { .name = "plugin-dir", .key = 128, .arg = "DIRECTORY", .doc = "Specify a different plugin directory", .group = 2 }, @@ -374,54 +333,64 @@ { .name = NULL } }; - error_t parse_opt(int key, char *arg, __attribute__((unused)) - struct argp_state *state) { - switch(key) { - case 'g': /* --global-options */ - if(arg != 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. */ + plugin **plugins = state->input; + switch (key) { + case 'g': + if (arg != NULL){ char *p; while((p = strsep(&arg, ",")) != NULL){ if(p[0] == '\0'){ continue; } - if(not add_argument(getplugin(NULL), p)){ + if(not add_argument(getplugin(NULL, plugins), p)){ perror("add_argument"); return ARGP_ERR_UNKNOWN; } } } break; - case 'G': /* --global-env */ + case 'e': if(arg == NULL){ break; } - if(not add_environment(getplugin(NULL), arg, true)){ - perror("add_environment"); + { + char *envdef = strdup(arg); + if(envdef == NULL){ + break; + } + if(not add_environment(getplugin(NULL, plugins), envdef)){ + perror("add_environment"); + } } break; - case 'o': /* --options-for */ - if(arg != NULL){ + case 'o': + if (arg != NULL){ char *p_name = strsep(&arg, ":"); - if(p_name[0] == '\0' or arg == NULL){ + if(p_name[0] == '\0'){ break; } char *opt = strsep(&arg, ":"); - if(opt[0] == '\0' or opt == NULL){ + if(opt[0] == '\0'){ 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; + if(opt != NULL){ + char *p; + while((p = strsep(&opt, ",")) != NULL){ + if(p[0] == '\0'){ + continue; + } + if(not add_argument(getplugin(p_name, plugins), p)){ + perror("add_argument"); + return ARGP_ERR_UNKNOWN; + } } } } break; - case 'E': /* --env-for */ + case 'f': if(arg == NULL){ break; } @@ -430,108 +399,49 @@ if(envdef == NULL){ break; } - *envdef = '\0'; - if(not add_environment(getplugin(arg), envdef+1, true)){ + char *p_name = strndup(arg, (size_t) (envdef-arg)); + if(p_name == NULL){ + break; + } + envdef++; + if(not add_environment(getplugin(p_name, plugins), envdef)){ perror("add_environment"); } } break; - case 'd': /* --disable */ - if(arg != NULL){ - plugin *p = getplugin(arg); + case 'd': + if (arg != NULL){ + plugin *p = getplugin(arg, plugins); if(p == NULL){ return ARGP_ERR_UNKNOWN; } p->disabled = true; } break; - case 'e': /* --enable */ - if(arg != NULL){ - plugin *p = getplugin(arg); - if(p == NULL){ - return ARGP_ERR_UNKNOWN; - } - p->disabled = false; - } - break; - case 128: /* --plugin-dir */ - free(plugindir); + case 128: 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 */ - ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (uid_t)tmpmax - or arg[numchars] != '\0'){ - fprintf(stderr, "Bad user ID number: \"%s\", using %" - PRIdMAX "\n", arg, (intmax_t)uid); - } else { - uid = (uid_t)tmpmax; - } - break; - case 131: /* --groupid */ - ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (gid_t)tmpmax - or arg[numchars] != '\0'){ - fprintf(stderr, "Bad group ID number: \"%s\", using %" - PRIdMAX "\n", arg, (intmax_t)gid); - } else { - gid = (gid_t)tmpmax; - } - break; - case 132: /* --debug */ - debug = true; - 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'){ - fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); - } - break; - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; - } - 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) { - switch(key) { - case 'g': /* --global-options */ - case 'G': /* --global-env */ - case 'o': /* --options-for */ - case 'E': /* --env-for */ - case 'd': /* --disable */ - case 'e': /* --enable */ - case 128: /* --plugin-dir */ - break; - case 129: /* --config-file */ - free(argfile); + case 129: argfile = strdup(arg); if(argfile == NULL){ perror("strdup"); } break; - case 130: /* --userid */ - case 131: /* --groupid */ - case 132: /* --debug */ + case 130: + uid = (uid_t)strtol(arg, NULL, 10); + break; + case 131: + gid = (gid_t)strtol(arg, NULL, 10); + break; + case 132: + debug = true; + break; case ARGP_KEY_ARG: + fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); + break; case ARGP_KEY_END: break; default: @@ -540,33 +450,30 @@ return 0; } - struct argp argp = { .options = options, - .parser = parse_opt_config_file, - .args_doc = "", + plugin *plugin_list = NULL; + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "[+PLUS_SEPARATED_OPTIONS]", .doc = "Mandos plugin runner -- Run plugins" }; - /* Parse using parse_opt_config_file() in order to get the custom - config file location, if any. */ - ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL); - if(ret == ARGP_ERR_UNKNOWN){ + ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list); + if (ret == ARGP_ERR_UNKNOWN){ fprintf(stderr, "Unknown error while parsing arguments\n"); exitstatus = EXIT_FAILURE; goto fallback; } - - /* Reset to the normal argument parser */ - argp.parser = parse_opt; - - /* Open the configfile if available */ - if(argfile == NULL){ + + if (argfile == NULL){ conffp = fopen(AFILE, "r"); } else { conffp = fopen(argfile, "r"); - } + } + if(conffp != NULL){ char *org_line = NULL; char *p, *arg, *new_arg, *line; size_t size = 0; + ssize_t sret; const char whitespace_delims[] = " \r\t\f\v\n"; const char comment_delim[] = "#"; @@ -579,9 +486,7 @@ } custom_argv[0] = argv[0]; custom_argv[1] = NULL; - - /* for each line in the config file, strip whitespace and ignore - commented text */ + while(true){ sret = getline(&org_line, &size, conffp); if(sret == -1){ @@ -616,36 +521,25 @@ } } free(org_line); - } else { + } else{ /* Check for harmful errors and go to fallback. Other errors might not affect opening plugins */ - if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){ + if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){ perror("fopen"); exitstatus = EXIT_FAILURE; goto fallback; } } - /* 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, - 0, NULL); - if(ret == ARGP_ERR_UNKNOWN){ + ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list); + if (ret == ARGP_ERR_UNKNOWN){ fprintf(stderr, "Unknown error while parsing arguments\n"); exitstatus = EXIT_FAILURE; goto fallback; } } - /* Parse actual command line arguments, to let them override the - config file */ - 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; - } - if(debug){ for(plugin *p = plugin_list; p != NULL; p=p->next){ fprintf(stderr, "Plugin: %s has %d arguments\n", @@ -653,24 +547,24 @@ 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); } } } - /* Strip permissions down to nobody */ ret = setuid(uid); - if(ret == -1){ + if (ret == -1){ perror("setuid"); - } + } + setgid(gid); - if(ret == -1){ + if (ret == -1){ perror("setgid"); } - - if(plugindir == NULL){ + + if (plugindir == NULL){ dir = opendir(PDIR); } else { dir = opendir(plugindir); @@ -697,13 +591,12 @@ FD_ZERO(&rfds_all); - /* Read and execute any executable in the plugin directory*/ while(true){ dirst = readdir(dir); - /* All directory entries have been processed */ + // All directory entries have been processed if(dirst == NULL){ - if(errno == EBADF){ + if (errno == EBADF){ perror("readdir"); exitstatus = EXIT_FAILURE; goto fallback; @@ -713,7 +606,7 @@ d_name_len = strlen(dirst->d_name); - /* Ignore dotfiles, backup files and other junk */ + // Ignore dotfiles, backup files and other junk { bool bad_name = false; @@ -721,7 +614,6 @@ const char const *bad_suffixes[] = { "~", "#", ".dpkg-new", ".dpkg-old", - ".dpkg-bak", ".dpkg-divert", NULL }; for(const char **pre = bad_prefixes; *pre != NULL; pre++){ size_t pre_len = strlen(*pre); @@ -735,9 +627,11 @@ break; } } + if(bad_name){ continue; } + for(const char **suf = bad_suffixes; *suf != NULL; suf++){ size_t suf_len = strlen(*suf); if((d_name_len >= suf_len) @@ -758,25 +652,20 @@ } char *filename; - if(plugindir == NULL){ - ret = asprintf(&filename, PDIR "/%s", dirst->d_name); - } else { - ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name); - } + ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name); if(ret < 0){ perror("asprintf"); continue; } ret = stat(filename, &st); - if(ret == -1){ + if (ret == -1){ perror("stat"); free(filename); continue; } - - /* Ignore non-executable files */ - if(not S_ISREG(st.st_mode) or (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); @@ -784,8 +673,7 @@ free(filename); continue; } - - plugin *p = getplugin(dirst->d_name); + plugin *p = getplugin(dirst->d_name, &plugin_list); if(p == NULL){ perror("getplugin"); free(filename); @@ -801,7 +689,7 @@ } { /* Add global arguments to argument list for this plugin */ - plugin *g = getplugin(NULL); + plugin *g = getplugin(NULL, &plugin_list); if(g != NULL){ for(char **a = g->argv + 1; *a != NULL; a++){ if(not add_argument(p, *a)){ @@ -810,7 +698,7 @@ } /* Add global environment variables */ for(char **e = g->environ; *e != NULL; e++){ - if(not add_environment(p, *e, false)){ + if(not add_environment(p, *e)){ perror("add_environment"); } } @@ -821,7 +709,12 @@ process, too. */ if(p->environ[0] != NULL){ for(char **e = environ; *e != NULL; e++){ - if(not add_environment(p, *e, false)){ + char *copy = strdup(*e); + if(copy == NULL){ + perror("strdup"); + continue; + } + if(not add_environment(p, copy)){ perror("add_environment"); } } @@ -829,12 +722,11 @@ int pipefd[2]; ret = pipe(pipefd); - if(ret == -1){ + 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){ perror("set_cloexec_flag"); @@ -848,13 +740,13 @@ goto fallback; } /* Block SIGCHLD until process is safely in process list */ - ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL); + ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ perror("sigprocmask"); exitstatus = EXIT_FAILURE; goto fallback; } - /* Starting a new process to be watched */ + // Starting a new process to be watched pid_t pid = fork(); if(pid == -1){ perror("fork"); @@ -868,12 +760,12 @@ perror("sigaction"); _exit(EXIT_FAILURE); } - ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ perror("sigprocmask"); _exit(EXIT_FAILURE); } - + ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ perror("dup2"); @@ -898,90 +790,86 @@ } /* no return */ } - /* Parent process */ - close(pipefd[1]); /* Close unused write end of pipe */ + /* parent process */ free(filename); - plugin *new_plugin = getplugin(dirst->d_name); - if(new_plugin == NULL){ - perror("getplugin"); - ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + close(pipefd[1]); /* close unused write end of pipe */ + process *new_process = malloc(sizeof(process)); + if (new_process == NULL){ + perror("malloc"); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - perror("sigprocmask"); + perror("sigprocmask"); } exitstatus = EXIT_FAILURE; goto fallback; } - new_plugin->pid = pid; - new_plugin->fd = pipefd[0]; - + *new_process = (struct process){ .pid = pid, + .fd = pipefd[0], + .next = process_list }; + // List handling + process_list = new_process; /* Unblock SIGCHLD so signal handler can be run if this process has already completed */ - ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ perror("sigprocmask"); exitstatus = EXIT_FAILURE; goto fallback; } - FD_SET(new_plugin->fd, &rfds_all); + FD_SET(new_process->fd, &rfds_all); - if(maxfd < new_plugin->fd){ - maxfd = new_plugin->fd; + if (maxfd < new_process->fd){ + maxfd = new_process->fd; } + } + + free_plugin_list(plugin_list); + plugin_list = NULL; closedir(dir); dir = NULL; - - for(plugin *p = plugin_list; p != NULL; p = p->next){ - if(p->pid != 0){ - break; - } - if(p->next == NULL){ - fprintf(stderr, "No plugin processes started. Incorrect plugin" - " directory?\n"); - free_plugin_list(); - } + + if (process_list == NULL){ + fprintf(stderr, "No plugin processes started. Incorrect plugin" + " directory?\n"); + process_list = NULL; } - - /* Main loop while running plugins exist */ - while(plugin_list){ + while(process_list){ fd_set rfds = rfds_all; int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL); - if(select_ret == -1){ + if (select_ret == -1){ perror("select"); exitstatus = EXIT_FAILURE; goto fallback; } /* OK, now either a process completed, or something can be read from one of them */ - for(plugin *proc = plugin_list; proc != NULL;){ + for(process *proc = process_list; proc ; proc = proc->next){ /* Is this process completely done? */ if(proc->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 %" PRIdMAX " exited with status" - " %d\n", (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 %" PRIdMAX " killed by signal" - " %d\n", (intmax_t) (proc->pid), + fprintf(stderr, "Plugin %u killed by signal %d\n", + (unsigned int) (proc->pid), WTERMSIG(proc->status)); } else if(WCOREDUMP(proc->status)){ - fprintf(stderr, "Plugin %" PRIdMAX " dumped core\n", - (intmax_t) (proc->pid)); + fprintf(stderr, "Plugin %d dumped core\n", + (unsigned int) (proc->pid)); } } - /* Remove the plugin */ FD_CLR(proc->fd, &rfds_all); - /* Block signal while modifying process_list */ ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ @@ -989,29 +877,34 @@ exitstatus = EXIT_FAILURE; goto fallback; } - - plugin *next_plugin = proc->next; - free_plugin(proc); - proc = next_plugin; - + /* Delete this process entry from the list */ + if(process_list == proc){ + /* First one - simple */ + process_list = proc->next; + } else { + /* Second one or later */ + for(process *p = process_list; p != NULL; p = p->next){ + if(p->next == proc){ + p->next = proc->next; + break; + } + } + } /* We are done modifying process list, so unblock signal */ - ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, - NULL); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, + NULL); if(ret < 0){ perror("sigprocmask"); - exitstatus = EXIT_FAILURE; - goto fallback; - } - - if(plugin_list == NULL){ - break; - } - - continue; + } + free(proc->buffer); + free(proc); + /* We deleted this process from the list, so we can't go + proc->next. Therefore, start over from the beginning of + the process list */ + break; } - /* This process exited nicely, so print its buffer */ - + bool bret = print_out_password(proc->buffer, proc->buffer_length); if(not bret){ @@ -1020,18 +913,16 @@ } goto fallback; } - /* This process has not completed. Does it have any output? */ if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* This process had nothing to say at this time */ - proc = proc->next; continue; } /* Before reading, make the process' data buffer large enough */ if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){ proc->buffer = realloc(proc->buffer, proc->buffer_size + (size_t) BUFFER_SIZE); - if(proc->buffer == NULL){ + if (proc->buffer == NULL){ perror("malloc"); exitstatus = EXIT_FAILURE; goto fallback; @@ -1039,18 +930,17 @@ proc->buffer_size += BUFFER_SIZE; } /* Read from the process */ - sret = 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; } } } @@ -1058,19 +948,13 @@ fallback: - if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){ + if(process_list == NULL or exitstatus != EXIT_SUCCESS){ /* Fallback if all plugins failed, none are found or an error occured */ bool bret; fprintf(stderr, "Going to fallback mode using getpass(3)\n"); char *passwordbuffer = getpass("Password: "); - size_t len = strlen(passwordbuffer); - /* Strip trailing newline */ - if(len > 0 and passwordbuffer[len-1] == '\n'){ - passwordbuffer[len-1] = '\0'; /* not strictly necessary */ - len--; - } - bret = print_out_password(passwordbuffer, len); + bret = print_out_password(passwordbuffer, strlen(passwordbuffer)); if(not bret){ perror("print_out_password"); exitstatus = EXIT_FAILURE; @@ -1083,28 +967,30 @@ perror("sigaction"); exitstatus = EXIT_FAILURE; } - + if(custom_argv != NULL){ for(char **arg = custom_argv+1; *arg != NULL; arg++){ free(*arg); } free(custom_argv); } + free_plugin_list(plugin_list); if(dir != NULL){ closedir(dir); } - /* Kill the processes */ - for(plugin *p = plugin_list; p != NULL; p = p->next){ - if(p->pid != 0){ - close(p->fd); - ret = kill(p->pid, SIGTERM); - if(ret == -1 and errno != ESRCH){ - /* Set-uid proccesses might not get closed */ - perror("kill"); - } + /* Free the process list and kill the processes */ + for(process *next; process_list != NULL; process_list = next){ + next = process_list->next; + close(process_list->fd); + ret = kill(process_list->pid, SIGTERM); + if(ret == -1 and errno != ESRCH){ + /* set-uid proccesses migth not get closed */ + perror("kill"); } + free(process_list->buffer); + free(process_list); } /* Wait for any remaining child processes to terminate */ @@ -1114,9 +1000,7 @@ if(errno != ECHILD){ perror("wait"); } - - free_plugin_list(); - + free(plugindir); free(argfile); === removed file 'plugin-runner.conf' --- plugin-runner.conf 2008-10-05 17:38:31 +0000 +++ plugin-runner.conf 1970-01-01 00:00:00 +0000 @@ -1,11 +0,0 @@ -## 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! -## -## 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-04 21:54:55 +0000 +++ plugin-runner.xml 2008-08-16 20:31:21 +0000 @@ -1,19 +1,18 @@ - + + - - -%common; ]> - + - Mandos Manual - - Mandos - &version; - &TIMESTAMP; + &COMMANDNAME; + + &COMMANDNAME; + &VERSION; Björn @@ -32,13 +31,33 @@ 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson + Teddy Hogeborn & Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &COMMANDNAME; 8mandos @@ -47,594 +66,209 @@ &COMMANDNAME; - Run Mandos plugins, pass data from first to succeed. + get password for encrypted rootdisk - + &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &COMMANDNAME; - - - - - - - &COMMANDNAME; - - - - &COMMANDNAME; - - - - - + --global-optionsOPTIONS + --options-forPLUGIN:OPTIONS + --disablePLUGIN + --groupidID + --useridID + --plugin-dirDIRECTORY + --debug + + + &COMMANDNAME; + --help + + + &COMMANDNAME; + --usage + + + &COMMANDNAME; + --version + - + DESCRIPTION - &COMMANDNAME; is a program which is meant to - be specified as a keyscript for the root disk in + &COMMANDNAME; is a plugin runner that waits + for any of its plugins to return sucessfull with a password, and + passes it to cryptsetup as stdout message. This command is not + meant to be invoked directly, but is instead meant to be run by + cryptsetup by being specified in /etc/crypttab as a keyscript + and subsequlently started in the initrd environment. See crypttab - 5. The aim of this - program is therefore to output a password, which then - cryptsetup - 8 will use to unlock the - root disk. - - - This program is not meant to be invoked directly, but can be in - order to test it. Note that any password obtained will simply - be output on standard output. - - - - - PURPOSE - - The purpose of this is to enable remote and unattended - rebooting of client host computer with an - encrypted root file system. See for details. - - - + 5 for more information on + keyscripts. + + + + plugins is looked for in the plugins directory which by default will be + /conf/conf.d/mandos/plugins.d if not changed by option --plugin-dir. + + OPTIONS - - - - - This option will add an environment variable setting to - all plugins. This will override any inherited environment - variable. - - - - - - - - - - This option will add an environment variable setting to - the PLUGIN plugin. This will - override any inherited environment variables or - environment variables specified using - . - - - - - - - - - - Pass some options to all plugins. - OPTIONS is a comma separated - list of options. This is not a very useful option, except - for specifying the - option to all plugins. - - - - - - - - - - Pass some options to a specific plugin. PLUGIN is the name (file basename) of a - plugin, and OPTIONS is a comma - separated list of options. - - - Note that since options are not split on whitespace, the - way to pass, to the plugin - foo, the option - with the option argument - baz is either - --options-for=foo:--bar=baz or - --options-for=foo:--bar,baz. Using - --options-for="foo:--bar baz". will - not work. - - - - - - - - - - Disable the plugin named - PLUGIN. The plugin will not be - started. - - - - - - - - - - Re-enable the plugin named - PLUGIN. This is only useful to - undo a previous option, maybe - from the configuration file. - - - - - - - - - Change to group ID ID on - startup. The default is 65534. All plugins will be - started using this group ID. Note: - This must be a number, not a name. - - - - - - - - - Change to user ID ID on - startup. The default is 65534. All plugins will be - started using this user ID. Note: - This must be a number, not a name. - - - - - - - - - Specify a different plugin directory. The default is - /lib/mandos/plugins.d, which will - exist in the initial RAM disk - environment. - - - - - - - - - Specify a different file to read additional options from. - See . Other command line options - will override options specified in the file. - - - - - - - - - Enable debug mode. This will enable a lot of output to - standard error about what the program is doing. The - program will still perform all other functions normally. - The default is to not run in debug - mode. - - - The plugins will not be affected by - this option. Use - - if complete debugging eruption is desired. - - - - - - - - - - Gives a help message about options and their meanings. - - - - - - - - - Gives a short usage message. - - - - - - - - - - Prints the program version. - - - + -g,--global-options + OPTIONS + + + Global options given to all plugins as additional start + arguments. Options are specified with a -o flag followed + by a comma separated string of options. + + + + + + -o, --options-for + PLUGIN:OPTION + + + + Plugin specific options given to the plugin as additional + start arguments. Options are specified with a -o flag + followed by a comma separated string of options. + + + + + + -d, --disable + PLUGIN + + + + Disable a specific plugin + + + + + + --groupid ID + + + + Group ID the plugins will run as + + + + + + --userid ID + + + + User ID the plugins will run as + + + + + + --plugin-dir DIRECTORY + + + + Specify a different plugin directory + + + + + + --debug + + + Debug mode + + + + + + -?, --help + + + Gives a help message + + + + + + --usage + + + Gives a short usage message + + + + + + -V, --version + + + Prints the program version + + + - - - OVERVIEW - - - This program will run on the client side in the initial - RAM disk environment, and is responsible for - getting a password. It does this by running plugins, one of - which will normally be the actual client program communicating - with the server. - - - - PLUGINS - - This program will get a password by running a number of - plugins, which are simply executable - programs in a directory in the initial RAM - disk environment. The default directory is - /lib/mandos/plugins.d, but this can be - changed with the option. The - plugins are started in parallel, and the first plugin to output - a password and exit with a successful exit - code will make this plugin-runner output the password from that - plugin, stop any other plugins, and exit. - - - - WRITING PLUGINS - - A plugin is simply a program which prints a password to its - standard output and then exits with a successful (zero) exit - status. If the exit status is not zero, any output on - standard output will be ignored by the plugin runner. Any - output on its standard error channel will simply be passed to - the standard error of the plugin runner, usually the system - console. - - - If the password is a single-line, manually entered passprase, - a final trailing newline character should - not be printed. - - - The plugin will run in the initial RAM disk environment, so - care must be taken not to depend on any files or running - services not available there. - - - The plugin must exit cleanly and free all allocated resources - upon getting the TERM signal, since this is what the plugin - runner uses to stop all other plugins when one plugin has - output a password and exited cleanly. - - - The plugin must not use resources, like for instance reading - from the standard input, without knowing that no other plugin - is also using it. - - - It is useful, but not required, for the plugin to take the - option. - - - - - - FALLBACK - - If no plugins succeed, this program will, as a fallback, ask for - a password on the console using getpass3, - and output it. This is not meant to be the normal mode of - operation, as there is a separate plugin for getting a password - from the console. - - - + EXIT STATUS - Exit status of this program is zero if no errors were - encountered, and otherwise not. The fallback (see ) may or may not have succeeded in either - case. - - - - - ENVIRONMENT - - This program does not use any environment variables itself, it - only passes on its environment to all the plugins. The - environment passed to plugins can be modified using the - and - options. - - - - + + + + FILES - - - /conf/conf.d/mandos/plugin-runner.conf - - - Since this program will be run as a keyscript, there is - little to no opportunity to pass command line arguments - to it. Therefore, it will also - read this file and use its contents as - whitespace-separated command line options. Also, - everything from a # character to the end - of a line is ignored. - - - This program is meant to run in the initial RAM disk - environment, so that is where this file is assumed to - exist. The file does not need to exist in the normal - file system. - - - This file will be processed before - the normal command line options, so the latter can - override the former, if need be. - - - This file name is the default; the file to read for - arguments can be changed using the - option. - - - - + + + + + NOTES + BUGS - The option is ignored when - specified from within a configuration file. - - + + - EXAMPLE - - - Normal invocation needs no options: - - - &COMMANDNAME; - - - - - Run the program, but not the plugins, in debug mode: - - - - - &COMMANDNAME; --debug - - - - - - Run all plugins, but run the foo plugin in - debug mode: - - - - - &COMMANDNAME; --options-for=foo:--debug - - - - - - Run all plugins, but not the program, in debug mode: - - - - - &COMMANDNAME; --global-options=--debug - - - - - - Run plugins from a different directory, read a different - configuration file, and add two options to the - mandos-client - 8mandos plugin: - - - - -&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 - - - + EXAMPLES + + + SECURITY - This program will, when starting, try to switch to another user. - If it is started as root, it will succeed, and will by default - switch to user and group 65534, which are assumed to be - non-privileged. This user and group is then what all plugins - will be started as. Therefore, the only way to run a plugin as - a privileged user is to have the set-user-ID or set-group-ID bit - set on the plugin executable file (see - execve2 - ). - - - If this program is used as a keyscript in crypttab5 - , there is a slight risk that if this program - fails to work, there might be no way to boot the system except - for booting from another media and editing the initial RAM disk - image to not run this program. This is, however, unlikely, - since the password-prompt8mandos - plugin will read a password from the console in - case of failure of the other plugins, and this plugin runner - will also, in case of catastrophic failure, itself fall back to - asking and outputting a password on the console (see ). - + SEE ALSO - cryptsetup - 8, - crypttab - 5, - execve - 2, mandos - 8, - password-prompt - 8mandos, - mandos-client - 8mandos + 8, + password-request + 8mandos, + password-prompt + 8mandos, and + cryptsetup + 8 - + - - - - - === removed file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2009-01-10 06:00:50 +0000 +++ plugins.d/askpass-fifo.c 1970-01-01 00:00:00 +0000 @@ -1,105 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Askpass-FIFO - Read a password from a FIFO and output it - * - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 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 and - * . - */ - -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ -#include /* ssize_t */ -#include /* mkfifo(), S_IRUSR, S_IWUSR */ -#include /* and */ -#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 */ - - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - ssize_t sret; - - /* Create FIFO */ - const char passfifo[] = "/lib/cryptsetup/passfifo"; - ret = (int)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 = (int)TEMP_FAILURE_RETRY(open(passfifo, O_RDONLY)); - if(fifo_fd == -1){ - perror("open"); - return EXIT_FAILURE; - } - - /* Read from FIFO */ - char *buf = NULL; - size_t buf_len = 0; - { - size_t buf_allocated = 0; - const size_t blocksize = 1024; - do{ - if(buf_len + blocksize > buf_allocated){ - char *tmp = realloc(buf, buf_allocated + blocksize); - if(tmp == NULL){ - perror("realloc"); - free(buf); - return EXIT_FAILURE; - } - buf = tmp; - buf_allocated += blocksize; - } - sret = TEMP_FAILURE_RETRY(read(fifo_fd, buf + buf_len, - buf_allocated - buf_len)); - if(sret == -1){ - perror("read"); - free(buf); - return EXIT_FAILURE; - } - buf_len += (size_t)sret; - }while(sret != 0); - } - - /* Close FIFO */ - TEMP_FAILURE_RETRY(close(fifo_fd)); - - /* Print password to stdout */ - size_t written = 0; - while(written < buf_len){ - sret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf + written, - buf_len - written)); - if(sret == -1){ - perror("write"); - free(buf); - return EXIT_FAILURE; - } - written += (size_t)sret; - } - free(buf); - - return EXIT_SUCCESS; -} === removed file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/askpass-fifo.xml 1970-01-01 00:00:00 +0000 @@ -1,162 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to get a password from a - FIFO. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program reads a password from a FIFO and - outputs it to standard output. - - - This program is not very useful on its own. This program is - really meant to run as a plugin in the Mandos client-side system, where it is used as a - fallback and alternative to retrieving passwords from a - Mandos server. - - - This program is meant to be imitate a feature of the - askpass program, so that programs written to - interface with it can keep working under the - Mandos system. - - - - - OPTIONS - - This program takes no options. - - - - - EXIT STATUS - - If exit status is 0, the output from the program is the password - as it was read. Otherwise, if exit status is other than 0, the - program was interrupted or encountered an error, and any output - so far could be corrupt and/or truncated, and should therefore - be ignored. - - - - - FILES - - - /lib/cryptsetup/passfifo - - - This is the FIFO where this program - will read the password. If it does not exist, it will be - created. - - - - - - - - EXAMPLE - - Note that normally, this program will not be invoked directly, - but instead started by the Mandos plugin-runner8mandos - . - - - - This program takes no options. - - - &COMMANDNAME; - - - - - - SECURITY - - The only thing that could be considered worthy of note is - this: This program is meant to be run by - plugin-runner8mandos, and will, when run - standalone, outside, in a normal environment, immediately output - on its standard output any presumably secret password it just - received. Therefore, when running this program standalone - (which should never normally be done), take care not to type in - any real secret password by force of habit, since it would then - immediately be shown as output. - - - - - SEE ALSO - - fifo - 7, - plugin-runner - 8mandos - - -
- - - - - === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2009-01-13 04:35:19 +0000 +++ plugins.d/password-prompt.c 2008-08-29 05:53:59 +0000 @@ -1,9 +1,8 @@ /* -*- coding: utf-8 -*- */ /* - * Password-prompt - Read a password from the terminal and print it - * - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 Björn Påhlsson + * Passprompt - Read a password from the terminal and print it + * + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -53,7 +52,7 @@ volatile bool quit_now = false; bool debug = false; -const char *argp_program_version = "password-prompt " VERSION; +const char *argp_program_version = "password-prompt 1.0"; const char *argp_program_bug_address = ""; static void termination_handler(__attribute__((unused))int signum){ @@ -79,8 +78,10 @@ .doc = "Debug mode", .group = 3 }, { .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 'p': prefix = arg; @@ -89,7 +90,7 @@ debug = true; break; case ARGP_KEY_ARG: - argp_usage(state); + argp_usage (state); break; case ARGP_KEY_END: break; @@ -98,26 +99,26 @@ } 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, 0, 0, NULL); - if(ret == ARGP_ERR_UNKNOWN){ + 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){ + if (tcgetattr(STDIN_FILENO, &t_old) != 0){ perror("tcgetattr"); return EXIT_FAILURE; } @@ -131,7 +132,7 @@ 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){ perror("sigaction"); @@ -143,7 +144,7 @@ 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){ perror("sigaction"); @@ -155,7 +156,7 @@ 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){ perror("sigaction"); @@ -164,22 +165,22 @@ } - if(debug){ + if (debug){ fprintf(stderr, "Removing echo flag from terminal attributes\n"); } t_new = t_old; t_new.c_lflag &= ~ECHO; - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ + 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"); } @@ -211,15 +212,10 @@ } } ret = getline(&buffer, &n, stdin); - if(ret > 0){ + if (ret > 0){ status = EXIT_SUCCESS; /* Make n = data size instead of allocated buffer size */ n = (size_t)ret; - /* Strip final newline */ - if(n>0 and buffer[n-1] == '\n'){ - buffer[n-1] = '\0'; /* not strictly necessary */ - n--; - } size_t written = 0; while(written < n){ ret = write(STDOUT_FILENO, buffer + written, n - written); @@ -232,8 +228,8 @@ } break; } - if(ret < 0){ - if(errno != EINTR and not feof(stdin)){ + if (ret < 0){ + if (errno != EINTR and not feof(stdin)){ perror("getline"); status = EXIT_FAILURE; break; @@ -249,22 +245,17 @@ } } - free(buffer); - - if(debug){ + if (debug){ fprintf(stderr, "Restoring terminal attributes\n"); } - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ perror("tcsetattr+echo"); } - if(debug){ + if (debug){ fprintf(stderr, "%s is exiting with status %d\n", argv[0], status); } - if(status == EXIT_SUCCESS){ - fputc('\n', stderr); - } return status; } === modified file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/password-prompt.xml 2008-08-29 05:53:59 +0000 @@ -1,18 +1,17 @@ - - -%common; + ]> - + Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,11 +31,32 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + @@ -53,19 +73,18 @@ &COMMANDNAME; - - PREFIX - &COMMANDNAME; + - @@ -75,24 +94,22 @@ &COMMANDNAME; + - - + DESCRIPTION All &COMMANDNAME; does is prompt for a - password and output any given password to standard output. - - - This program is not very useful on its own. This program is - really meant to run as a plugin in the Mandos client-side system, where it is used as a - fallback and alternative to retrieving passwords from a - Mandos server. + password and output any given password to standard output. This + 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 retriving passwords from a Mandos server. This program is little more than a - - + PREFIX + PREFIX Prefix string shown before the password prompt. @@ -139,8 +156,8 @@ + - Gives a help message about options and their meanings. @@ -158,14 +175,14 @@ + - Prints the program version. - + @@ -198,8 +215,8 @@ 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 + 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 @@ -222,7 +239,7 @@ None are known at this time. - + EXAMPLE @@ -244,7 +261,7 @@ Show a prefix before the prompt; in this case, a host name. It might be useful to be reminded of which host needs a - password, in case of KVM switches, etc. + password, in case of KVM switches, etc. @@ -274,7 +291,7 @@ >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, + any presumably secret password it just recieved. 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 @@ -294,7 +311,7 @@ crypttab 5 - mandos-client + password-request 8mandos plugin-runner 8mandos, === renamed file 'plugins.d/mandos-client.c' => 'plugins.d/password-request.c' --- plugins.d/mandos-client.c 2009-01-14 14:20:17 +0000 +++ plugins.d/password-request.c 2008-08-24 10:49:09 +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,7 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 Björn Påhlsson + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -36,7 +35,7 @@ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ #include /* fprintf(), stderr, fwrite(), - stdout, ferror(), sscanf */ + stdout, ferror() */ #include /* uint16_t, uint32_t */ #include /* NULL, size_t, ssize_t */ #include /* free(), EXIT_SUCCESS, EXIT_FAILURE, @@ -48,28 +47,23 @@ #include /* socket(), inet_pton(), sockaddr, sockaddr_in6, PF_INET6, SOCK_STREAM, INET6_ADDRSTRLEN, - uid_t, gid_t, open(), opendir(), - DIR */ -#include /* open() */ + uid_t, gid_t */ +#include /* PRIu16 */ #include /* socket(), struct sockaddr_in6, struct in6_addr, inet_pton(), connect() */ -#include /* open() */ -#include /* opendir(), struct dirent, readdir() - */ -#include /* PRIu16, intmax_t, SCNdMAX */ #include /* assert() */ #include /* perror(), errno */ #include /* time() */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS, if_indextoname(), if_nametoindex(), IF_NAMESIZE */ -#include #include /* close(), SEEK_SET, off_t, write(), getuid(), getgid(), setuid(), setgid() */ +#include #include /* inet_pton(), htons */ -#include /* not, and, or */ +#include /* not, and */ #include /* struct argp_option, error_t, struct argp_state, struct argp, argp_parse(), ARGP_KEY_ARG, @@ -92,8 +86,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 */ @@ -105,13 +98,10 @@ #define BUFFER_SIZE 256 -#define PATHDIR "/conf/conf.d/mandos" -#define SECKEY "seckey.txt" -#define PUBKEY "pubkey.txt" - bool debug = false; +static const char *keydir = "/conf/conf.d/mandos"; static const char mandos_protocol_version[] = "1"; -const char *argp_program_version = "mandos-client " VERSION; +const char *argp_program_version = "password-request 1.0"; const char *argp_program_bug_address = ""; /* Used for passing in values through the Avahi callback functions */ @@ -122,7 +112,6 @@ unsigned int dh_bits; gnutls_dh_params_t dh_params; const char *priority; - gpgme_ctx_t ctx; } mandos_context; /* @@ -132,9 +121,9 @@ */ 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; @@ -143,120 +132,58 @@ } /* - * Initialize GPGME. + * Decrypt OpenPGP data using keyrings in HOMEDIR. + * Returns -1 on error */ -static bool init_gpgme(mandos_context *mc, const char *seckey, - const char *pubkey, const char *tempdir){ - int ret; +static ssize_t pgp_packet_decrypt (const char *cryptotext, + size_t crypto_size, + char **plaintext, + const char *homedir){ + gpgme_data_t dh_crypto, dh_plain; + gpgme_ctx_t ctx; gpgme_error_t rc; + ssize_t ret; + size_t plaintext_capacity = 0; + ssize_t plaintext_length = 0; gpgme_engine_info_t engine_info; - - /* - * Helper function to insert pub and seckey to the enigne keyring. - */ - bool import_key(const char *filename){ - int fd; - gpgme_data_t pgp_data; - - fd = (int)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){ - 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){ - fprintf(stderr, "bad gpgme_op_import: %s: %s\n", - gpgme_strsource(rc), gpgme_strerror(rc)); - return false; - } - - ret = (int)TEMP_FAILURE_RETRY(close(fd)); - if(ret == -1){ - perror("close"); - } - gpgme_data_release(pgp_data); - return true; - } - - if(debug){ - fprintf(stderr, "Initialize gpgme\n"); + if (debug){ + fprintf(stderr, "Trying to decrypt OpenPGP data\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; + return -1; } - /* Set GPGME home directory for the OpenPGP engine only */ - rc = gpgme_get_engine_info(&engine_info); - if(rc != GPG_ERR_NO_ERROR){ + /* Set GPGME home directory for the OpenPGP engine only */ + 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; + return -1; } while(engine_info != NULL){ if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){ gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, - engine_info->file_name, tempdir); + engine_info->file_name, homedir); break; } engine_info = engine_info->next; } if(engine_info == NULL){ - fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir); - return false; - } - - /* Create new GPGME "context" */ - 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)){ - return false; - } - - return true; -} - -/* - * Decrypt OpenPGP data. - * Returns -1 on error - */ -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){ - fprintf(stderr, "Trying to decrypt OpenPGP data\n"); + fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir); + return -1; } /* 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; @@ -264,24 +191,33 @@ /* 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); return -1; } + /* Create new GPGME "context" */ + rc = gpgme_new(&ctx); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_new: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + plaintext_length = -1; + goto decrypt_end; + } + /* 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(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(ctx); + if (result == NULL){ fprintf(stderr, "gpgme_op_decrypt_result failed\n"); } else { fprintf(stderr, "Unsupported algorithm: %s\n", @@ -314,8 +250,8 @@ } /* Seek back to the beginning of the GPGME plaintext data buffer */ - if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){ - perror("gpgme_data_seek"); + if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){ + perror("pgpme_data_seek"); plaintext_length = -1; goto decrypt_end; } @@ -325,7 +261,7 @@ plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length, plaintext_capacity); - if(plaintext_capacity == 0){ + if (plaintext_capacity == 0){ perror("adjustbuffer"); plaintext_length = -1; goto decrypt_end; @@ -334,7 +270,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; } @@ -345,7 +281,7 @@ } plaintext_length += ret; } - + if(debug){ fprintf(stderr, "Decrypted password is: "); for(ssize_t i = 0; i < plaintext_length; i++){ @@ -364,10 +300,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; } @@ -388,13 +323,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 */ @@ -404,58 +339,56 @@ /* 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(); + if (ret != GNUTLS_E_SUCCESS){ + fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious + warning */ + safer_gnutls_strerror(ret)); + gnutls_global_deinit (); return -1; } if(debug){ - fprintf(stderr, "Attempting to use OpenPGP public key %s and" - " secret key %s as GnuTLS credentials\n", pubkeyfilename, + fprintf(stderr, "Attempting to use OpenPGP certificate %s" + " and keyfile %s as GnuTLS credentials\n", pubkeyfilename, seckeyfilename); } ret = gnutls_certificate_set_openpgp_key_file (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); - fprintf(stderr, "The GnuTLS error is: %s\n", + fprintf(stdout, "The GnuTLS error is: %s\n", safer_gnutls_strerror(ret)); goto globalfail; } /* 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)); + 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)); + 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); - + return 0; - + globalfail: - + gnutls_certificate_free_credentials(mc->cred); gnutls_global_deinit(); - gnutls_dh_params_deinit(mc->dh_params); return -1; + } static int init_gnutls_session(mandos_context *mc, @@ -463,7 +396,7 @@ int ret; /* GnuTLS session creation */ ret = gnutls_init(session, GNUTLS_SERVER); - if(ret != GNUTLS_E_SUCCESS){ + if (ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error in GnuTLS session initialization: %s\n", safer_gnutls_strerror(ret)); } @@ -471,29 +404,29 @@ { const char *err; ret = gnutls_priority_set_direct(*session, mc->priority, &err); - if(ret != GNUTLS_E_SUCCESS) { + 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; } } ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, mc->cred); - if(ret != GNUTLS_E_SUCCESS) { + 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; } @@ -507,7 +440,6 @@ AvahiIfIndex if_index, mandos_context *mc){ int ret, tcp_sd; - ssize_t sret; union { struct sockaddr in; struct sockaddr_in6 in6; } to; char *buffer = NULL; char *decrypted_buffer; @@ -519,8 +451,8 @@ char interface[IF_NAMESIZE]; gnutls_session_t session; - ret = init_gnutls_session(mc, &session); - if(ret != 0){ + ret = init_gnutls_session (mc, &session); + if (ret != 0){ return -1; } @@ -534,7 +466,7 @@ perror("socket"); return -1; } - + if(debug){ if(if_indextoname((unsigned int)if_index, interface) == NULL){ perror("if_indextoname"); @@ -548,7 +480,7 @@ /* 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 ){ + if (ret < 0 ){ perror("inet_pton"); return -1; } @@ -556,9 +488,7 @@ fprintf(stderr, "Bad address: %s\n", ip); return -1; } - to.in6.sin6_port = htons(port); /* Spurious warnings from - -Wconversion and - -Wunreachable-code */ + to.in6.sin6_port = htons(port); /* Spurious warning */ to.in6.sin6_scope_id = (uint32_t)if_index; @@ -577,18 +507,18 @@ } ret = connect(tcp_sd, &to.in, sizeof(to)); - if(ret < 0){ + 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){ + if (ret == -1){ perror("write"); retval = -1; goto mandos_end; @@ -597,7 +527,7 @@ if(written < out_size){ continue; } else { - if(out == mandos_protocol_version){ + if (out == mandos_protocol_version){ written = 0; out = "\r\n"; } else { @@ -605,21 +535,21 @@ } } } - + if(debug){ fprintf(stderr, "Establishing TLS session with %s\n", ip); } - gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd); - + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd); + do{ - ret = gnutls_handshake(session); + 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); } retval = -1; goto mandos_end; @@ -631,33 +561,33 @@ fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip); } - + while(true){ buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity); - if(buffer_capacity == 0){ + if (buffer_capacity == 0){ perror("adjustbuffer"); retval = -1; goto mandos_end; } - sret = gnutls_record_recv(session, buffer+buffer_length, - BUFFER_SIZE); - if(sret == 0){ + 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); + 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); + gnutls_perror (ret); retval = -1; goto mandos_end; } @@ -666,11 +596,11 @@ fprintf(stderr, "Unknown error while reading data from" " encrypted session with Mandos server\n"); retval = -1; - gnutls_bye(session, GNUTLS_SHUT_RDWR); + gnutls_bye (session, GNUTLS_SHUT_RDWR); goto mandos_end; } } else { - buffer_length += (size_t) sret; + buffer_length += (size_t) ret; } } @@ -678,18 +608,19 @@ fprintf(stderr, "Closing TLS session\n"); } - gnutls_bye(session, GNUTLS_SHUT_RDWR); + gnutls_bye (session, GNUTLS_SHUT_RDWR); - if(buffer_length > 0){ - decrypted_buffer_size = pgp_packet_decrypt(mc, buffer, + if (buffer_length > 0){ + decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length, - &decrypted_buffer); - if(decrypted_buffer_size >= 0){ + &decrypted_buffer, + keydir); + if (decrypted_buffer_size >= 0){ written = 0; while(written < (size_t) decrypted_buffer_size){ - 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)){ if(debug){ fprintf(stderr, "Error writing encrypted data: %s\n", @@ -712,11 +643,8 @@ mandos_end: free(buffer); - ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd)); - if(ret == -1){ - perror("close"); - } - gnutls_deinit(session); + close(tcp_sd); + gnutls_deinit (session); return retval; } @@ -740,7 +668,7 @@ /* Called whenever a service has been resolved successfully or timed out */ - switch(event) { + switch (event) { default: case AVAHI_RESOLVER_FAILURE: fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'" @@ -754,11 +682,11 @@ 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, mc); - if(ret == 0){ + if (ret == 0){ avahi_simple_poll_quit(mc->simple_poll); } } @@ -782,7 +710,7 @@ /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - switch(event) { + switch (event) { default: case AVAHI_BROWSER_FAILURE: @@ -797,7 +725,7 @@ the callback function is called the Avahi server will free the resolver for us. */ - if(!(avahi_s_service_resolver_new(mc->server, interface, + if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, mc))) @@ -817,12 +745,22 @@ } } +/* Combines file name and path and returns the malloced new + string. some sane checks could/should be added */ +static char *combinepath(const char *first, const char *second){ + char *tmp; + int ret = asprintf(&tmp, "%s/%s", first, second); + if(ret < 0){ + return NULL; + } + return tmp; +} + + int main(int argc, char *argv[]){ AvahiSServiceBrowser *sb = NULL; int error; int ret; - intmax_t tmpmax; - int numchars; int exitcode = EXIT_SUCCESS; const char *interface = "eth0"; struct ifreq network; @@ -830,82 +768,86 @@ 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; - + char *pubkeyfilename = NULL; + char *seckeyfilename = NULL; + const char *pubkeyname = "pubkey.txt"; + const char *seckeyname = "seckey.txt"; mandos_context mc = { .simple_poll = NULL, .server = NULL, - .dh_bits = 1024, .priority = "SECURE256" - ":!CTYPE-X.509:+CTYPE-OPENPGP" }; + .dh_bits = 1024, .priority = "SECURE256"}; 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", + .arg = "IP", + .doc = "Connect directly to a sepcified mandos server", .group = 1 }, { .name = "interface", .key = 'i', - .arg = "NAME", - .doc = "Interface that will be used to search for Mandos" - " servers", + .arg = "INTERFACE", + .doc = "Interface that Avahi will conntect through", + .group = 1 }, + { .name = "keydir", .key = 'd', + .arg = "KEYDIR", + .doc = "Directory where the openpgp keyring is", .group = 1 }, { .name = "seckey", .key = 's', - .arg = "FILE", - .doc = "OpenPGP secret key file base name", + .arg = "SECKEY", + .doc = "Secret openpgp key for gnutls authentication", .group = 1 }, { .name = "pubkey", .key = 'p', - .arg = "FILE", - .doc = "OpenPGP public key file base name", + .arg = "PUBKEY", + .doc = "Public openpgp key for gnutls authentication", .group = 2 }, { .name = "dh-bits", .key = 129, .arg = "BITS", - .doc = "Bit length of the prime number used in the" - " Diffie-Hellman key exchange", + .doc = "dh-bits to use in gnutls communication", .group = 2 }, { .name = "priority", .key = 130, - .arg = "STRING", - .doc = "GnuTLS priority string for the TLS handshake", - .group = 1 }, + .arg = "PRIORITY", + .doc = "GNUTLS priority", .group = 1 }, { .name = NULL } }; + - error_t parse_opt(int key, char *arg, - struct argp_state *state) { - switch(key) { - case 128: /* --debug */ + 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 = true; break; - case 'c': /* --connect */ + case 'c': connect_to = arg; break; - case 'i': /* --interface */ + case 'i': interface = arg; break; - case 's': /* --seckey */ - seckey = arg; - break; - case 'p': /* --pubkey */ - pubkey = arg; - break; - case 129: /* --dh-bits */ - ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax - or arg[numchars] != '\0'){ - fprintf(stderr, "Bad number of DH bits\n"); + case 'd': + keydir = arg; + break; + case 's': + seckeyname = arg; + break; + case 'p': + pubkeyname = arg; + break; + case 129: + errno = 0; + mc.dh_bits = (unsigned int) strtol(arg, NULL, 10); + if (errno){ + perror("strtol"); exit(EXIT_FAILURE); } - mc.dh_bits = (typeof(mc.dh_bits))tmpmax; break; - case 130: /* --priority */ + case 130: mc.priority = arg; break; case ARGP_KEY_ARG: - argp_usage(state); + argp_usage (state); case ARGP_KEY_END: break; default: @@ -913,18 +855,41 @@ } 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){ + " passwords from 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; } } + + pubkeyfilename = combinepath(keydir, pubkeyname); + if (pubkeyfilename == NULL){ + perror("combinepath"); + exitcode = EXIT_FAILURE; + goto end; + } + + seckeyfilename = combinepath(keydir, seckeyname); + if (seckeyfilename == NULL){ + perror("combinepath"); + exitcode = EXIT_FAILURE; + goto end; + } + + ret = init_gnutls_global(&mc, pubkeyfilename, seckeyfilename); + if (ret == -1){ + fprintf(stderr, "init_gnutls_global failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gnutls_initalized = true; + } /* If the interface is down, bring it up */ { @@ -950,48 +915,22 @@ goto end; } } - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } + close(sd); } uid = getuid(); gid = getgid(); ret = setuid(uid); - if(ret == -1){ + if (ret == -1){ perror("setuid"); } setgid(gid); - if(ret == -1){ + 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); @@ -1007,15 +946,13 @@ exitcode = EXIT_FAILURE; goto end; } - uint16_t port; - ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (uint16_t)tmpmax - or address[numchars+1] != '\0'){ - fprintf(stderr, "Bad port number\n"); + errno = 0; + uint16_t port = (uint16_t) strtol(address+1, NULL, 10); + if(errno){ + perror("Bad port number"); exitcode = EXIT_FAILURE; goto end; } - port = (uint16_t)tmpmax; *address = '\0'; address = connect_to; ret = start_mandos_communication(address, port, if_index, &mc); @@ -1027,7 +964,7 @@ goto end; } - if(not debug){ + if (not debug){ avahi_set_log_function(empty_log); } @@ -1036,13 +973,13 @@ /* Allocate main Avahi loop object */ mc.simple_poll = avahi_simple_poll_new(); - if(mc.simple_poll == NULL) { + 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 */ @@ -1051,18 +988,18 @@ 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) { + if (mc.server == NULL) { fprintf(stderr, "Failed to create Avahi server: %s\n", avahi_strerror(error)); exitcode = EXIT_FAILURE; @@ -1074,7 +1011,7 @@ AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, &mc); - if(sb == NULL) { + if (sb == NULL) { fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(mc.server))); exitcode = EXIT_FAILURE; @@ -1082,77 +1019,35 @@ } /* Run the main loop */ - - if(debug){ + + if (debug){ fprintf(stderr, "Starting Avahi loop search\n"); } avahi_simple_poll_loop(mc.simple_poll); end: - - if(debug){ + + if (debug){ fprintf(stderr, "%s exiting\n", argv[0]); } /* Cleanup things */ - if(sb != NULL) + if (sb != NULL) avahi_s_service_browser_free(sb); - if(mc.server != NULL) + if (mc.server != NULL) avahi_server_free(mc.server); - - if(mc.simple_poll != NULL) + + if (mc.simple_poll != NULL) avahi_simple_poll_free(mc.simple_poll); - - if(gnutls_initalized){ + free(pubkeyfilename); + free(seckeyfilename); + + 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){ - if(errno != ENOENT){ - perror("opendir"); - } - } 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 and errno != ENOENT){ - perror("rmdir"); - } - } - + gnutls_global_deinit (); + } + return exitcode; } === renamed file 'plugins.d/mandos-client.xml' => 'plugins.d/password-request.xml' --- plugins.d/mandos-client.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/password-request.xml 2008-08-18 05:24:20 +0000 @@ -1,19 +1,18 @@ - + + - - -%common; + + ]> - + - Mandos Manual - - Mandos - &version; - &TIMESTAMP; + &COMMANDNAME; + + &COMMANDNAME; + &VERSION; Björn @@ -32,13 +31,33 @@ 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson + Teddy Hogeborn & Björn Påhlsson - + + + This manual page is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any + later version. + + + + This manual page is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; If not, see + . + + - + &COMMANDNAME; 8mandos @@ -47,566 +66,252 @@ &COMMANDNAME; - Client for Mandos + Client for mandos - + &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &COMMANDNAME; - - - - - - - &COMMANDNAME; - - - - &COMMANDNAME; - - - - - + --connectIP + --keydirKEYDIR + --interfaceINTERFACE + --pubkeyPUBKEY + --seckeySECKEY + --priorityPRIORITY + --dh-bitsBITS + --debug + + + &COMMANDNAME; + --help + + + &COMMANDNAME; + --usage + + + &COMMANDNAME; + --version + - + DESCRIPTION - &COMMANDNAME; is a client program that - communicates with mandos8 - 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 - to run as a plugin of the Mandos - plugin-runner - 8mandos, which runs in the - initial RAM disk environment because it is - specified as a keyscript in the - crypttab5 - file. - - - - - 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. - - + &COMMANDNAME; is a mandos plugin that works + like a client program that through avahi detects mandos servers, + sets up a gnutls connect and request a encrypted password. Any + passwords given is automaticly decrypted and passed to + cryptsetup. + + OPTIONS - This program is commonly not invoked from the command line; it - is normally started by the Mandos - plugin runner, see plugin-runner8mandos - . Any command line options this program accepts - are therefore normally provided by the plugin runner, and not - directly. + Commonly not invoked as command lines but from configuration + file of plugin runner. - + - - - - - Do not use Zeroconf to locate servers. Connect directly - to only one specified Mandos - server. Note that an IPv6 address has colon characters in - it, so the last colon character is - assumed to separate the address from the port number. - - - This option is normally only useful for testing and - debugging. - - - - - - - - - - Network interface that will be brought up and scanned for - 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. - - - - - - - - - - OpenPGP public key file name. The default name is - /conf/conf.d/mandos/pubkey.txt. - - - - - - - - - - OpenPGP secret key file name. The default name is - /conf/conf.d/mandos/seckey.txt. - - - - - - - - - - - - - - - - Sets the number of bits to use for the prime number in the - TLS Diffie-Hellman key exchange. Default is 1024. - - - - - - - - - Enable debug mode. This will enable a lot of output to - standard error about what the program is doing. The - program will still perform all other functions normally. - - - It will also enable debug mode in the Avahi and GnuTLS - libraries, making them print large amounts of debugging - output. - - - - - - - - - - Gives a help message about options and their meanings. - - - - - - - - - Gives a short usage message. - - - - - - - - - - Prints the program version. - - - + -c, --connect= + IP + + + Connect directly to a specified mandos server + + + + + + -d, --keydir= + KEYDIR + + + Directory where the openpgp keyring is + + + + + + -i, --interface= + INTERFACE + + + Interface that Avahi will conntect through + + + + + + -p, --pubkey= + PUBKEY + + + Public openpgp key for gnutls authentication + + + + + + -s, --seckey= + SECKEY + + + Secret openpgp key for gnutls authentication + + + + + + --priority=PRIORITY + + + + GNUTLS priority + + + + + + --dh-bits=BITS + + + + dh-bits to use in gnutls communication + + + + + + --debug + + + Debug mode + + + + + + -?, --help + + + Gives a help message + + + + + + --usage + + + Gives a short usage message + + + + + + -V, --version + + + Prints the program version + + + - - - OVERVIEW - - - This program is the client part. It is a plugin started by - plugin-runner - 8mandos which will run in - an initial RAM disk environment. - - - This program could, theoretically, be used as a keyscript in - /etc/crypttab, but it would then be - impossible to enter a password for the encrypted root disk at - the console, since this program does not read from the console - at all. This is why a separate plugin runner ( - plugin-runner - 8mandos) is used to run - both this program and others in in parallel, - one of which will prompt for passwords on - the system console. - - - + EXIT STATUS - This program will exit with a successful (zero) exit status if a - server could be found and the password received from it could be - successfully decrypted and output on standard output. The - program will exit with a non-zero exit status only if a critical - error occurs. Otherwise, it will forever connect to new - Mandos servers as they appear, trying - to get a decryptable password and print it. - + ENVIRONMENT - This program does not use any environment variables, not even - the ones provided by cryptsetup8 - . - - - + + + FILES - - - /conf/conf.d/mandos/pubkey.txt - /conf/conf.d/mandos/seckey.txt - - - OpenPGP public and private key files, in ASCII - Armor format. These are the default file names, - they can be changed with the and - options. - - - - - - - - - - - - + + + + + + BUGS + + + + EXAMPLE - Note that normally, command line options will not be given - directly, but via options for the Mandos plugin-runner - 8mandos. - - - Normal invocation needs no options, if the network interface - is eth0: - - - &COMMANDNAME; - - - - - Search for Mandos servers (and connect to them) using another - interface: - - - - &COMMANDNAME; --interface eth1 - - - - - Run in debug mode, and use a custom key: - - - - -&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt - - - - - - Run in debug mode, with a custom key, and do not use Zeroconf - 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 2001:db8:f983:bd0b:30de:ae4a:71f2:f672:4711 --interface eth2 - - - - + SECURITY - This program is set-uid to root, but will switch back to the - original (and presumably non-privileged) user and group after - bringing up the network interface. - - - To use this program for its intended purpose (see ), the password for the root file system will - have to be given out to be stored in a server computer, after - having been encrypted using an OpenPGP key. This encrypted data - which will be stored in a server can only be decrypted by the - OpenPGP key, and the data will only be given out to those - clients who can prove they actually have that key. This key, - however, is stored unencrypted on the client side in its initial - RAM disk image file system. This is normally - readable by all, but this is normally fixed during installation - of this program; file permissions are set so that no-one is able - to read that file. - - - The only remaining weak point is that someone with physical - access to the client hard drive might turn off the client - computer, read the OpenPGP keys directly from the hard drive, - and communicate with the server. To safeguard against this, the - server is supposed to notice the client disappearing and stop - giving out the encrypted data. Therefore, it is important to - set the timeout and checker interval values tightly on the - server. See mandos8. - - - It will also help if the checker program on the server is - configured to request something from the client which can not be - spoofed by someone else on the network, unlike unencrypted - ICMP echo (ping) replies. - - - Note: This makes it completely insecure to - have Mandos clients which dual-boot - to another operating system which is not - trusted to keep the initial RAM disk image - confidential. - + SEE ALSO - - cryptsetup - 8, - crypttab - 5, - mandos - 8, - password-prompt - 8mandos, - plugin-runner - 8mandos - - - - - Zeroconf - - - - Zeroconf is the network protocol standard used for finding - Mandos servers on the local network. - - - - - - Avahi - - - - Avahi is the library this program calls to find Zeroconf - services. - - - - - - GnuTLS - - - - GnuTLS is the library this client uses to implement TLS for - communicating securely with the server, and at the same time - send the public OpenPGP key to the server. - - - - - - GPGME - - - - GPGME is the library used to decrypt the OpenPGP data sent - by the server. - - - - - - RFC 4291: IP Version 6 Addressing - Architecture - - - - - Section 2.2: Text Representation of - Addresses - - - - Section 2.5.5.2: IPv4-Mapped IPv6 - Address - - - - Section 2.5.6, Link-Local IPv6 Unicast - Addresses - - - This client uses IPv6 link-local addresses, which are - immediately usable since a link-local addresses is - automatically assigned to a network interfaces when it - is brought up. - - - - - - - - - RFC 4346: The Transport Layer Security (TLS) - Protocol Version 1.1 - - - - TLS 1.1 is the protocol implemented by GnuTLS. - - - - - - RFC 4880: OpenPGP Message Format - - - - The data received from the server is binary encrypted - OpenPGP data. - - - - - - RFC 5081: Using OpenPGP Keys for Transport Layer - Security - - - - This is implemented by GnuTLS and used by this program so - that OpenPGP keys can be used. - - - - + + + mandos + 8 + + + + plugin-runner + 8mandos + + + + password-prompt + 8mandos + + + + Zeroconf + + + + Avahi + + + + GnuTLS + + + + + GPGME + + + + RFC 4880: OpenPGP Message + Format + + + + RFC 5081: Using OpenPGP Keys for + Transport Layer Security + + + + RFC 4291: IP Version 6 Addressing + Architecture, section 2.5.6, Link-Local IPv6 + Unicast Addresses + + + - - - - - - === removed file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2009-01-14 14:20:17 +0000 +++ plugins.d/splashy.c 1970-01-01 00:00:00 +0000 @@ -1,304 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Splashy - Read a password from splashy and output it - * - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 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 and - * . - */ - -#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(), perror(), sscanf() */ -#include /* EXIT_FAILURE, free(), - EXIT_SUCCESS */ -#include /* pid_t, DIR, struct dirent, - ssize_t */ -#include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, SCNdMAX */ -#include /* struct stat, lstat(), S_ISLNK */ -#include /* not, or, and */ -#include /* readlink(), fork(), execl(), - sleep(), dup2() STDERR_FILENO, - STDOUT_FILENO, _exit() */ -#include /* memcmp() */ -#include /* errno */ -#include /* waitpid(), WIFEXITED(), - WEXITSTATUS() */ - -sig_atomic_t interrupted_by_signal = 0; - -static void termination_handler(__attribute__((unused))int signum){ - interrupted_by_signal = 1; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - - /* Create prompt string */ - char *prompt = NULL; - { - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char *const prompt_start = "getpass " - "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 splashy process */ - pid_t splashy_pid = 0; - { - const char splashy_name[] = "/sbin/splashy"; - 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; - { - intmax_t tmpmax; - int numchars; - ret = sscanf(proc_ent->d_name, "%" SCNdMAX "%n", &tmpmax, - &numchars); - if(ret < 1 or tmpmax != (pid_t)tmpmax - or proc_ent->d_name[numchars] != '\0'){ - /* Not a process */ - continue; - } - pid = (pid_t)tmpmax; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - char exe_target[sizeof(splashy_name)]; - ssize_t sret; - { - 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){ - if(errno == ENOENT){ - free(exe_link); - continue; - } - 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 == ((ssize_t)sizeof(exe_target)-1)) - and (memcmp(splashy_name, exe_target, - sizeof(exe_target)-1) == 0)){ - splashy_pid = pid; - break; - } - } - closedir(proc_dir); - } - if(splashy_pid == 0){ - free(prompt); - return EXIT_FAILURE; - } - - /* Set up the signal handler */ - { - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - 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){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - ret = sigaction(SIGHUP, NULL, &old_action); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - ret = sigaction(SIGTERM, NULL, &old_action); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - } - - /* Fork off the splashy command to prompt for password */ - 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"; - ret = execl(splashy_command, splashy_command, prompt, - (char *)NULL); - if(not interrupted_by_signal){ - perror("execl"); - } - free(prompt); - _exit(EXIT_FAILURE); - } - } - - /* Parent */ - free(prompt); - - /* Wait for command to complete */ - if(not interrupted_by_signal and splashy_command_pid != 0){ - int status; - ret = waitpid(splashy_command_pid, &status, 0); - if(ret == -1){ - if(errno != EINTR){ - perror("waitpid"); - } - if(errno == ECHILD){ - splashy_command_pid = 0; - } - } else { - /* The child process has exited */ - splashy_command_pid = 0; - if(not interrupted_by_signal and WIFEXITED(status) - and WEXITSTATUS(status)==0){ - return EXIT_SUCCESS; - } - } - } - 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; -} === removed file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/splashy.xml 1970-01-01 00:00:00 +0000 @@ -1,283 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use splashy to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - splashy_update - 8 and outputs any given - password to standard output. If no splashy8 - 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 - splashy8 - to abort requesting a password, because - splashy - 8 does not support this. - Therefore, this program will then kill the - running splashy - 8 process and start a - new one, using boot as the only argument. - - - - - 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 - - - /sbin/splashy_update - - - This is the command run to retrieve a password from - splashy - 8. See - splashy_update8 - . - - - - - /proc - - - To find the running splashy8 - , this directory will be searched for - numeric entries which will be assumed to be directories. - In all those directories, the exe - entry will be used to determine the name of the running - binary and the effective user and group - ID of the process. See - proc5. - - - - - /sbin/splashy - - - This is the name of the binary which will be searched for - in the process list. See splashy8 - . - - - - - - - - BUGS - - Killing splashy - 8 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 splashy8 - 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 splashy - 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, - splashy - 8, - splashy_update - 8 - - -
- - - - - === added file 'plugins.d/usplash' --- plugins.d/usplash 1970-01-01 00:00:00 +0000 +++ plugins.d/usplash 2008-08-14 02:24:59 +0000 @@ -0,0 +1,42 @@ +#!/bin/sh -e + +# If not on a tty, then get rid of possibly disrupting stderr output +if ! tty -s; then + exec 2>/dev/null +fi + +test -x /sbin/usplash + +usplash="`pidof usplash -o $$`" +test -n "$usplash" + +# We get some variables from cryptsetup: +# $cryptsource the device node, like "/dev/sda3" +# $crypttarget the device mapper name, like "sda3_crypt". + +prompt="Enter passphrase to unlock" +if [ -n "$crypttarget" ]; then + prompt="$prompt the disk $crypttarget" +fi +if [ -n "$cryptsource" ]; then + prompt="$prompt ($cryptsource)" +fi + +splash_input_password(){ + test -p /dev/.initramfs/usplash_outfifo || return 1 + /sbin/usplash_write "INPUTQUIET $1" || return 1 + cat /dev/.initramfs/usplash_outfifo 2> /dev/null || return 1 +} + +# Usplash keeps waiting for input even if some other plugin provided +# the password, so we must kill it +trap "kill -TERM $usplash; sleep 2; kill -KILL $usplash; + kill -TERM $$" TERM HUP + +password="`splash_input_password \"$prompt: \" password`" + +trap - TERM + +/sbin/usplash_write "TIMEOUT 15" + +echo -n "$password" === removed file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2009-01-14 14:20:17 +0000 +++ plugins.d/usplash.c 1970-01-01 00:00:00 +0000 @@ -1,531 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Usplash - Read a password from usplash and output it - * - * Copyright © 2008,2009 Teddy Hogeborn - * Copyright © 2008,2009 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 and - * . - */ - -#define _GNU_SOURCE /* asprintf() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - SIG_IGN, kill(), SIGKILL */ -#include /* bool, false, true */ -#include /* open(), O_WRONLY, O_RDONLY */ -#include /* and, or, not*/ -#include /* errno, EINTR */ -#include /* size_t, ssize_t, pid_t, DIR, struct - dirent */ -#include /* NULL */ -#include /* strlen(), memcmp() */ -#include /* asprintf(), perror(), sscanf() */ -#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() */ -#include /* getenv() */ -#include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, SCNdMAX */ -#include /* struct stat, lstat(), S_ISLNK */ - -sig_atomic_t interrupted_by_signal = 0; - -static void termination_handler(__attribute__((unused))int signum){ - interrupted_by_signal = 1; -} - -static bool usplash_write(const char *cmd, const char *arg){ - /* - * usplash_write("TIMEOUT", "15") will write "TIMEOUT 15\0" - * usplash_write("PULSATE", NULL) will write "PULSATE\0" - * SEE ALSO - * usplash_write(8) - */ - int ret; - 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; - } - }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); - }else{ - do{ - ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg); - if(ret == -1 and (errno != EINTR or interrupted_by_signal)){ - int e = errno; - close(fifo_fd); - errno = e; - return false; - } - }while(ret == -1); - cmd_line = cmd_line_alloc; - cmd_line_len = (size_t)ret + 1; - } - - size_t written = 0; - ssize_t sret = 0; - while(not interrupted_by_signal and written < cmd_line_len){ - sret = write(fifo_fd, cmd_line + written, - cmd_line_len - written); - if(sret == -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; - } - 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; -} - -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; - 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; - { - intmax_t tmpmax; - int numchars; - ret = sscanf(proc_ent->d_name, "%" SCNdMAX "%n", &tmpmax, - &numchars); - if(ret < 1 or tmpmax != (pid_t)tmpmax - or proc_ent->d_name[numchars] != '\0'){ - /* Not a process */ - 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){ - 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; - } - 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 == ((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); - } - 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); - } - break; - } - } - closedir(proc_dir); - } - if(usplash_pid == 0){ - free(prompt); - return EXIT_FAILURE; - } - - /* Set up the signal handler */ - { - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - 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){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - ret = sigaction(SIGHUP, NULL, &old_action); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - ret = sigaction(SIGTERM, NULL, &old_action); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &new_action, NULL); - if(ret == -1){ - perror("sigaction"); - free(prompt); - return EXIT_FAILURE; - } - } - } - - /* Write command to FIFO */ - 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); - - /* 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){ - 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); - while(kill(usplash_pid, 0) == 0){ - 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) */ - 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); - } - - execv(usplash_name, cmdline_argv); - if(not interrupted_by_signal){ - perror("execv"); - } - free(cmdline); - free(cmdline_argv); - _exit(EXIT_FAILURE); - } - free(cmdline); - free(cmdline_argv); - sleep(2); - if(not usplash_write("PULSATE", NULL) - and (errno != EINTR)){ - perror("usplash_write"); - } - - return EXIT_FAILURE; -} === removed file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/usplash.xml 1970-01-01 00:00:00 +0000 @@ -1,297 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use usplash to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - usplash8 - and outputs any given password to standard - output. If no usplash8 - 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 - usplash8 - to abort requesting a password, because - usplash - 8 does not support this. - Therefore, this program will then kill the - running usplash - 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 - - - /dev/.initramfs/usplash_fifo - - - This is the FIFO to where this program - will write the commands for usplash8 - . See fifo7 - . - - - - - /dev/.initramfs/usplash_outfifo - - - This is the FIFO where this program - will read the password from usplash8 - . See fifo7 - . - - - - - /proc - - - To find the running usplash8 - , 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/usplash - - - This is the name of the binary which will be searched for - in the process list. See usplash8 - . - - - - - - - - BUGS - - Killing usplash - 8 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 usplash8 - 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 usplash - 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, - fifo - 7, - plugin-runner - 8mandos, - proc - 5, - usplash - 8 - - -
- - - - -