=== modified file '.bzrignore' --- .bzrignore 2008-10-03 09:32:30 +0000 +++ .bzrignore 2008-09-06 16:33:54 +0000 @@ -1,14 +1,8 @@ *.5 *.8 *.8mandos +plugin-runner +plugins.d/password-prompt +plugins.d/mandos-client 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 === modified file 'INSTALL' --- INSTALL 2008-10-28 18:00:20 +0000 +++ INSTALL 2008-09-08 18:54:47 +0000 @@ -97,28 +97,10 @@ 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 + 4. 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. @@ -127,6 +109,6 @@ 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 + Is 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-09-17 00:34:09 +0000 @@ -9,15 +9,10 @@ #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 @@ -44,9 +39,8 @@ # 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 DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \ @@ -59,152 +53,71 @@ $(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) +MANPOST=sed --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g' + +PLUGINS=plugins.d/password-prompt plugins.d/mandos-client +PROGS=plugin-runner $(PLUGINS) DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \ plugins.d/mandos-client.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) +%.5: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +%.8: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +%.8mandos: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos.8: mandos.xml mandos-options.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos-keygen.8: mandos-keygen.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos.conf.5: mandos.conf.xml mandos-options.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugin-runner.8mandos: plugin-runner.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) 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 $(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 ./plugin-runner --plugin-dir=plugins.d \ --config-file=plugin-runner.conf \ - --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt \ - $(CLIENTARGS) + --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt # Used by run-client keydir/seckey.txt keydir/pubkey.txt: mandos-keygen @@ -213,7 +126,7 @@ # 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 @@ -227,11 +140,6 @@ install: install-server install-client-nokey -install-html: html - install --directory $(htmldir) - install --mode=u=rw,go=r --target-directory=$(htmldir) \ - $(htmldocs) - install-server: doc install --directory $(CONFDIR) install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos @@ -243,9 +151,7 @@ $(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 + update-rc.d mandos defaults gzip --best --to-stdout mandos.8 \ > $(MANDIR)/man8/mandos.8.gz gzip --best --to-stdout mandos.conf.5 \ @@ -259,7 +165,9 @@ $(PREFIX)/lib/mandos/plugins.d if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ install --mode=u=rwx \ - --directory "$(CONFDIR)/plugins.d"; \ + --directory "$(CONFDIR)/plugins.d" && \ + install --mode=u=rw,go=r etc-plugins.d-README \ + $(CONFDIR)/plugins.d/README ; \ fi install --mode=u=rwx,go=rx \ --target-directory=$(PREFIX)/lib/mandos plugin-runner @@ -271,15 +179,9 @@ install --mode=u=rwxs,go=rx \ --target-directory=$(PREFIX)/lib/mandos/plugins.d \ plugins.d/mandos-client - install --mode=u=rwxs,go=rx \ + install --mode=u=rwx,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 initramfs-tools-hook \ $(INITRAMFSTOOLS)/hooks/mandos install --mode=u=rw,go=r initramfs-tools-hook-conf \ @@ -295,12 +197,6 @@ > $(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 @@ -328,18 +224,16 @@ $(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 \ $(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 + if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ + rm --force $(CONFDIR)/plugins.d/README; \ + fi -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \ $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR) update-initramfs -k all -u === 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. === modified file 'README' --- README 2009-01-04 21:54:55 +0000 +++ README 2008-09-17 00:34:09 +0000 @@ -130,38 +130,10 @@ 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. - - (None of these take any options or reads any files.) - - More plugins could easily be written and added by the system - administrator; see the section called "WRITING PLUGINS" in - plugin-runner(8mandos) to learn the plugin requirements. - * Copyright - Copyright © 2008,2009 Teddy Hogeborn - Copyright © 2008,2009 Björn Påhlsson + Copyright © 2008 Teddy Hogeborn + 2008 Björn Påhlsson ** License: === modified file 'TODO' --- TODO 2008-12-29 02:44:54 +0000 +++ TODO 2008-09-19 00:00:51 +0000 @@ -1,13 +1,15 @@ -*- org -*- +* plugin-runner +** TODO Man page for plugin-runner.conf.5 + link to plugin-runner.8 + * mandos-client ** TODO [#B] Temporarily lower kernel log level for less printouts during sucessfull boot. - klogctl(6, NULL, 0); klogctl(7, NULL, 0); ** TODO [#C] IPv4 support -* plugin-runner -** TODO [#B] use scandir(3) instead of readdir(3) +* DONE password-prompt * mandos (server) ** TODO [#B] Log level :bugs: @@ -17,12 +19,6 @@ ** TODO [#B] Run-time communication with server :bugs: Probably using D-Bus See also [[*Mandos-tools]] -*** Client class -*** Main server - + SetLogLevel - syslogger.setLevel(logging.WARNING) - + Quit - + [[http://log.ometer.com/2007-05.html][Best D-Bus practices]] ** TODO Implement --foreground :bugs: [[info:standards:Option%20Table][Table of Long Options]] ** TODO Implement --socket @@ -30,28 +26,34 @@ ** TODO 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 + +* Mandos-tools/utilities + All of this probably using D-Bus +** TODO List clients ** 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. +* Man pages +** TODO Use xinclude for all common sections + Like authors, etc. + + +* Installer +** Client-side +*** TODO 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) +*** mandos-keygen +**** TODO [#A] Ask for password twice for confirmation +**** TODO "--passfile" option + Using the "secfile" option instead of "secret" +**** TODO [#B] "--test" option + For testing decryption before rebooting. +** Server-side +*** TODO [#A] Create mandos user and group for server + * [#A] Package ** /usr/share/initramfs-tools/hooks/mandos @@ -60,6 +62,22 @@ question. ** TODO /etc/bash_completion.d/mandos From XML sources directly? +** TODO unperish +** DONE bzr-builddeb +** TODO mandos user/group creation +** TODO Key creation in postinst + +* TODO Web site +** DONE http://www.fukt.bsnet.se/mandos + Redirects to the wiki page +** TODO http://wiki.fukt.bsnet.se/wiki/Mandos + +* Mailing list +** DONE mandos-dev +*** TODO http://gmane.org/subscribe.php + +* TODO Announce project on Usenet + [[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 @@ - - - === modified file 'debian/changelog' --- debian/changelog 2009-01-06 22:43:19 +0000 +++ debian/changelog 2008-09-17 00:34:09 +0000 @@ -1,52 +1,5 @@ -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 +mandos (1.0) unstable; urgency=low + + * Initial Release. + + -- Mandos Maintainers Sun, 07 Sep 2008 11:55:51 +0200 === modified file 'debian/control' --- debian/control 2009-01-06 20:39:35 +0000 +++ debian/control 2008-09-17 00:34:09 +0000 @@ -2,20 +2,17 @@ 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 +Build-Depends: debhelper (>= 7), docbook-xsl, docbook (<5.0), + libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc 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 +Vcs-Bzr: ftp://anonymous@ftp.fukt.bsnet.se/pub/mandos/latest 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 +Depends: ${shlibs:Depends}, ${misc:Depends}, python (>=2.5), + python-gnutls, python-dbus, python-avahi, avahi-daemon, + gnupg (< 2) Recommends: fping Description: a server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows @@ -34,7 +31,7 @@ Package: mandos-client Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup +Depends: ${shlibs:Depends}, ${misc:Depends} Enhances: cryptsetup Description: do unattended reboots with an encrypted root file system This is the client part of the Mandos system, which allows === modified file 'debian/copyright' --- debian/copyright 2009-01-04 21:54:55 +0000 +++ debian/copyright 2008-09-17 00:34:09 +0000 @@ -1,26 +1,27 @@ -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+ +Authors: Teddy Hogeborn, Björn Påhlsson + +Homepage: + +Copyright: + + Copyright © 2008 Teddy Hogeborn + 2008 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 . - . - On Debian systems, the complete text of the GNU General Public - License can be found in "/usr/share/common-licenses/GPL". + +On Debian systems, the complete text of the GNU General Public License +can be found in "/usr/share/common-licenses/GPL". === modified file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2008-10-05 17:38:31 +0000 +++ debian/mandos-client.README.Debian 2008-09-19 00:00:51 +0000 @@ -1,45 +1,15 @@ -* 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. - - -- Teddy Hogeborn , Sun, 5 Oct 2008 19:04:26 +0200 +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 +stanza to copy and paste into /etc/mandos/clients.conf on the Mandos +server. + +Also, 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 file is changed, it will be necessary +to update the initrd image by doing "update-initramfs -k all -u". + +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. === 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 === modified file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2008-10-01 15:29:01 +0000 +++ debian/mandos-client.lintian-overrides 2008-09-17 00:34:09 +0000 @@ -1,32 +1,4 @@ -# 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: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root 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 === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2009-01-06 05:08:37 +0000 +++ debian/mandos-client.postinst 2008-09-19 01:10:27 +0000 @@ -15,49 +15,26 @@ # If prerm fails during replacement due to conflict: # abort-remove in-favour -# Update the initial RAM file system image +. /usr/share/debconf/confmodule + +# Update the initramfs 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 + if which update-initramfs >/dev/null 2>&1; then + update-initramfs -u + fi } case "$1" in configure) - add_mandos_user - create_key + if ! getent passwd mandos >/dev/null; then + adduser --disabled-password --quiet --system \ + --home /var/run/mandos --no-create-home \ + --gecos "Mandos password daemon" --group mandos + fi update_initramfs ;; + abort-upgrade|abort-deconfigure|abort-remove) ;; === modified file 'debian/mandos-client.postrm' --- debian/mandos-client.postrm 2008-09-19 20:54:58 +0000 +++ debian/mandos-client.postrm 2008-09-19 00:00:51 +0000 @@ -27,24 +27,26 @@ # abort-upgrade -# Update the initial RAM file system image +# Update the initramfs update_initramfs() { - if [ -x /usr/sbin/update-initramfs ]; then - update-initramfs -u -k all - fi + if type update-initramfs >/dev/null 2>&1 ; then + update-initramfs -u -k all + fi } + case "$1" in remove) update_initramfs ;; purge) - shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || : + shred --remove /etc/keys/mandos/seckey.txt || : rm --force /etc/mandos/plugin-runner.conf \ /etc/keys/mandos/pubkey.txt \ - /etc/keys/mandos/seckey.txt 2>/dev/null + /etc/keys/mandos/seckey.txt + rmdir /etc/keys/mandos /etc/mandos/plugins.d /etc/mandos || : ;; upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) ;; === 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.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 === modified file 'debian/mandos.lintian-overrides' --- debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000 +++ debian/mandos.lintian-overrides 2008-09-17 00:34:09 +0000 @@ -1,4 +1,1 @@ -# 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 === modified file 'debian/mandos.postinst' --- debian/mandos.postinst 2009-01-06 05:08:37 +0000 +++ debian/mandos.postinst 2008-09-19 00:54:24 +0000 @@ -15,21 +15,14 @@ # If prerm fails during replacement due to conflict: # abort-remove in-favour +. /usr/share/debconf/confmodule + case "$1" in configure) - # Rename old "mandos" user and group - 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 + if ! getent passwd mandos >/dev/null; then + adduser --disabled-password --quiet --system \ + --home /var/run/mandos --no-create-home \ + --gecos "Mandos password daemon" --group mandos fi ;; === 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' === modified file 'debian/rules' --- debian/rules 2009-01-04 22:26:07 +0000 +++ debian/rules 2008-09-17 00:34:09 +0000 @@ -10,7 +10,7 @@ # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 +export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS @@ -18,17 +18,21 @@ configure: configure-stamp configure-stamp: dh_testdir +# Add here commands to configure the package. touch configure-stamp +#Architecture build: build-arch build-indep build-arch: build-arch-stamp build-arch-stamp: configure-stamp +# Add here commands to compile the arch part of the package. dh_auto_build -- all doc touch $@ build-indep: build-indep-stamp build-indep-stamp: configure-stamp +# Add here commands to compile the indep part of the package. dh_auto_build -- doc touch $@ @@ -36,6 +40,7 @@ dh_testdir dh_testroot rm -f build-arch-stamp build-indep-stamp configure-stamp +# Add here commands to clean up after the build process. dh_auto_clean dh_clean @@ -45,10 +50,11 @@ dh_testroot dh_prep dh_installdirs --indep +# Add here commands to install the indep part of the package into +# debian/-doc. $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server dh_lintian - dh_installinit --onlyscripts --no-start \ - --update-rcd-params="defaults 25 15" + dh_installinit --onlyscripts --no-start dh_install --indep install-arch: @@ -56,28 +62,46 @@ dh_testroot dh_prep dh_installdirs --same-arch + +# Add here commands to install the arch part of the package into +# debian/tmp. $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client install-client-nokey dh_lintian dh_install --same-arch +# Must not depend on anything. This is to be called by +# binary-arch/binary-indep +# in another 'make' thread. binary-common: dh_testdir dh_testroot dh_installchangelogs dh_installdocs +# dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_pycentral +# dh_installinit +# dh_installcron +# dh_installinfo +# dh_installman 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_perl + dh_makeshlibs 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 === added file 'etc-plugins.d-README' --- etc-plugins.d-README 1970-01-01 00:00:00 +0000 +++ etc-plugins.d-README 2008-09-06 16:11:50 +0000 @@ -0,0 +1,5 @@ +Any plugins found here 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 === modified file 'init.d-mandos' --- init.d-mandos 2008-09-21 12:04:02 +0000 +++ init.d-mandos 2008-09-05 16:24:33 +0000 @@ -1,7 +1,7 @@ #! /bin/sh ### BEGIN INIT INFO # Provides: mandos -# Required-Start: $remote_fs avahi-daemon +# Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 === modified file 'initramfs-tools-hook' --- initramfs-tools-hook 2008-12-10 01:26:02 +0000 +++ initramfs-tools-hook 2008-09-19 00:54:24 +0000 @@ -51,13 +51,11 @@ exit 1 fi -mandos_user="`{ getent passwd _mandos \ - || getent passwd mandos \ +mandos_user="`{ getent passwd mandos \ || getent passwd nobody \ || echo ::65534::::; } \ | awk --field-separator=: '{ print $3 }'`" -mandos_group="`{ getent group _mandos \ - || getent group mandos \ +mandos_group="`{ getent group mandos \ || getent group nogroup \ || echo ::65534:; } \ | awk --field-separator=: '{ print $3 }'`" @@ -91,7 +89,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 @@ -101,7 +99,7 @@ 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 === modified file 'mandos' --- mandos 2009-01-08 03:54:06 +0000 +++ mandos 2008-09-05 18:37:28 +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 @@ -56,24 +56,21 @@ 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 +79,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 +108,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 +127,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 +146,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 +169,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 +325,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 +348,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 +373,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 +402,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 +464,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 +485,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 +503,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,8 +513,7 @@ 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 @@ -737,7 +529,7 @@ 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,10 +564,10 @@ # if_nametoindex # (self.settings # ["interface"])) - return super(IPv6_TCPServer, self).server_bind() + return super(type(self), self).server_bind() def server_activate(self): if self.enabled: - return super(IPv6_TCPServer, self).server_activate() + return super(type(self), self).server_activate() def enable(self): self.enabled = True @@ -799,8 +591,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 +632,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 +650,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 +683,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 +693,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 +706,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 +721,6 @@ "priority": "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", "servicename": "Mandos", - "use_dbus": "True", } # Parse config file for server-global settings @@ -937,34 +729,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,7 +759,7 @@ # 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) @@ -982,7 +769,7 @@ clients = Set() tcp_server = IPv6_TCPServer((server_settings["address"], server_settings["port"]), - TCP_handler, + tcp_handler, settings=server_settings, clients=clients) pidfilename = "/var/run/mandos.pid" @@ -991,20 +778,22 @@ 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 + uid = 65534 + gid = 65534 + try: + uid = pwd.getpwnam("mandos").pw_uid + except KeyError: + try: + uid = pwd.getpwnam("nobody").pw_uid + except KeyError: + pass + try: + gid = pwd.getpwnam("mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("nogroup").pw_gid + except KeyError: + pass try: os.setuid(uid) os.setgid(gid) @@ -1014,10 +803,10 @@ 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 +819,21 @@ 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) + + 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 @@ -1059,7 +852,7 @@ pidfile.write(str(pid) + "\n") pidfile.close() del pidfile - except IOError: + except IOError, err: logger.error(u"Could not write to file %r with PID %d", pidfilename, pid) except NameError: @@ -1078,8 +871,8 @@ while clients: client = clients.pop() - client.disable_hook = None - client.disable() + client.stop_hook = None + client.stop() atexit.register(cleanup) @@ -1088,61 +881,8 @@ 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() + client.start() tcp_server.enable() tcp_server.server_activate() @@ -1166,13 +906,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-09-12 19:12:40 +0000 @@ -1,11 +1,10 @@ /etc/mandos/clients.conf"> - - -%common; + ]> @@ -13,7 +12,7 @@ Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -33,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -170,7 +168,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 @@ -241,11 +239,6 @@ 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. - @@ -351,7 +344,7 @@ [DEFAULT] timeout = 1h interval = 5m -checker = fping -q -- %%(host)s +checker = fping -q -- %(host)s # Client "foo" [foo] === modified file 'mandos-keygen' --- mandos-keygen 2009-01-06 02:42:53 +0000 +++ mandos-keygen 2008-09-08 12:03:16 +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" 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 @@ -41,8 +40,8 @@ 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 +53,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 @@ -76,14 +74,10 @@ -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 + -p, --password Create an encrypted password using the keys in the key directory. All options other than --dir and --name are ignored. EOF @@ -93,7 +87,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;; @@ -242,7 +235,7 @@ FILECOMMENT="$FILECOMMENT <$KEYEMAIL>" fi - # Export key from key rings to key files + # Export keys 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" \ @@ -253,7 +246,7 @@ fi if [ "$mode" = password ]; then - # Import key into temporary key rings + # Import keys into temporary key rings gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor \ --import "$SECKEYFILE" @@ -272,37 +265,22 @@ 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 \ + stty -echo + echo -n "Enter passphrase: " >&2 + head --lines=1 | tr --delete '\n' \ + | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor --encrypt \ - --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \ + --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 +EOF sed --quiet --expression=' /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{ /^$/,${ === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2009-01-04 21:54:55 +0000 +++ mandos-keygen.xml 2008-09-12 19:12:40 +0000 @@ -1,10 +1,9 @@ - - -%common; + ]> @@ -12,7 +11,7 @@ Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,7 +31,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -124,10 +122,6 @@ - - - FILE @@ -173,9 +167,8 @@ This program can also be used with the - or - options to generate a ready-made section for - clients.conf (see + option to generate a ready-made + section for clients.conf (see mandos-clients.conf 5). @@ -333,18 +326,6 @@ - - - - - - The same as , but read from - FILE, not the terminal. - - - @@ -383,7 +364,7 @@ - + FILES Use the option to change where === 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-09-06 16:31:49 +0000 @@ -58,18 +58,12 @@ 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-09-12 19:12:40 +0000 @@ -1,11 +1,10 @@ /etc/mandos/mandos.conf"> - - -%common; + ]> @@ -13,7 +12,7 @@ Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -33,7 +32,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -131,17 +129,6 @@ - - - - - - - @@ -184,7 +171,6 @@ debug = true priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP servicename = Daena -use_dbus = False === 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-09-12 19:12:40 +0000 @@ -1,18 +1,17 @@ - - -%common; + ]> - + Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,7 +31,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -85,8 +83,6 @@ DIRECTORY - - &COMMANDNAME; @@ -231,16 +227,6 @@ - - - - - - - See also . - - - @@ -336,16 +322,6 @@ - - D-BUS INTERFACE - - The server will by default provide a D-Bus system bus interface. - This interface will only be accessible by the root user or a - Mandos-specific user, if such a user exists. - - - - EXIT STATUS @@ -374,7 +350,7 @@ - + FILES Use the option to change where @@ -444,7 +420,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 @@ -506,7 +482,7 @@ SECURITY - + SERVER Running this &COMMANDNAME; server program @@ -515,7 +491,7 @@ soon after startup. - + CLIENTS The server only gives out its stored data to clients which @@ -528,7 +504,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 === modified file 'plugin-runner.c' --- plugin-runner.c 2009-01-06 22:49:50 +0000 +++ plugin-runner.c 2008-09-19 00:00:51 +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, @@ -70,7 +69,7 @@ #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{ @@ -309,7 +308,6 @@ struct stat st; fd_set rfds_all; int ret, maxfd = 0; - ssize_t sret; uid_t uid = 65534; gid_t gid = 65534; bool debug = false; @@ -547,6 +545,7 @@ 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[] = "#"; @@ -701,7 +700,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); @@ -848,12 +846,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"); @@ -909,11 +907,12 @@ if (maxfd < new_plugin->fd){ maxfd = new_plugin->fd; } + } closedir(dir); dir = NULL; - + for(plugin *p = plugin_list; p != NULL; p = p->next){ if(p->pid != 0){ break; @@ -924,7 +923,7 @@ free_plugin_list(); } } - + /* Main loop while running plugins exist */ while(plugin_list){ fd_set rfds = rfds_all; @@ -970,10 +969,6 @@ goto fallback; } - plugin *next_plugin = proc->next; - free_plugin(proc); - proc = next_plugin; - /* We are done modifying process list, so unblock signal */ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); @@ -987,11 +982,14 @@ break; } + plugin *next_plugin = proc->next; + free_plugin(proc); + proc = next_plugin; continue; } /* This process exited nicely, so print its buffer */ - + bool bret = print_out_password(proc->buffer, proc->buffer_length); if(not bret){ @@ -1019,18 +1017,18 @@ 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; } } } @@ -1063,7 +1061,7 @@ perror("sigaction"); exitstatus = EXIT_FAILURE; } - + if(custom_argv != NULL){ for(char **arg = custom_argv+1; *arg != NULL; arg++){ free(*arg); @@ -1075,7 +1073,7 @@ closedir(dir); } - /* Kill the processes */ + /* Free the process list and kill the processes */ for(plugin *p = plugin_list; p != NULL; p = p->next){ if(p->pid != 0){ close(p->fd); @@ -1094,7 +1092,7 @@ if(errno != ECHILD){ perror("wait"); } - + free_plugin_list(); free(plugindir); === modified file 'plugin-runner.conf' --- plugin-runner.conf 2008-10-05 17:38:31 +0000 +++ plugin-runner.conf 2008-09-06 16:31:49 +0000 @@ -1,11 +1,9 @@ ## 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-09-19 00:00:51 +0000 @@ -1,10 +1,9 @@ - - -%common; + ]> @@ -12,7 +11,7 @@ Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,7 +31,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson === 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/mandos-client.c' --- plugins.d/mandos-client.c 2009-01-10 06:00:50 +0000 +++ plugins.d/mandos-client.c 2008-09-07 01:44:44 +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 @@ -102,13 +101,17 @@ #define BUFFER_SIZE 256 +/* + #define PATHDIR "/conf/conf.d/mandos" +*/ + #define PATHDIR "/conf/conf.d/mandos" #define SECKEY "seckey.txt" #define PUBKEY "pubkey.txt" bool debug = false; static const char mandos_protocol_version[] = "1"; -const char *argp_program_version = "mandos-client " VERSION; +const char *argp_program_version = "mandos-client 1.0"; const char *argp_program_bug_address = ""; /* Used for passing in values through the Avahi callback functions */ @@ -156,7 +159,7 @@ int fd; gpgme_data_t pgp_data; - fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); + fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); if(fd == -1){ perror("open"); return false; @@ -176,7 +179,7 @@ return false; } - ret = (int)TEMP_FAILURE_RETRY(close(fd)); + ret = TEMP_FAILURE_RETRY(close(fd)); if(ret == -1){ perror("close"); } @@ -501,7 +504,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; @@ -578,7 +580,7 @@ written = 0; 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){ perror("write"); @@ -633,13 +635,13 @@ 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; @@ -662,7 +664,7 @@ goto mandos_end; } } else { - buffer_length += (size_t) sret; + buffer_length += (size_t) ret; } } @@ -704,7 +706,7 @@ mandos_end: free(buffer); - ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd)); + ret = TEMP_FAILURE_RETRY(close(tcp_sd)); if(ret == -1){ perror("close"); } @@ -941,7 +943,7 @@ goto end; } } - ret = (int)TEMP_FAILURE_RETRY(close(sd)); + ret = TEMP_FAILURE_RETRY(close(sd)); if(ret == -1){ perror("close"); } === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/mandos-client.xml 2008-09-12 19:12:40 +0000 @@ -1,18 +1,17 @@ - - -%common; + ]> Mandos Manual - + Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,7 +31,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -345,7 +343,7 @@
- + FILES @@ -451,11 +449,11 @@ 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.
=== modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2009-01-10 06:00:50 +0000 +++ plugins.d/password-prompt.c 2008-09-07 01:44:44 +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,7 +78,7 @@ .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. */ @@ -100,7 +99,7 @@ } return 0; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos password-prompt -- Read and" @@ -111,7 +110,7 @@ return EXIT_FAILURE; } } - + if (debug){ fprintf(stderr, "Starting %s\n", argv[0]); } @@ -250,7 +249,7 @@ fprintf(stderr, "getline() returned 0, retrying.\n"); } } - + free(buffer); if (debug){ === 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-09-06 16:31:49 +0000 @@ -1,10 +1,9 @@ - - -%common; + ]> @@ -12,7 +11,7 @@ Mandos Manual Mandos - &version; + &VERSION; &TIMESTAMP; @@ -32,7 +31,6 @@ 2008 - 2009 Teddy Hogeborn Björn Påhlsson @@ -85,14 +83,12 @@ 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 retrieving passwords from a Mandos server. This program is little more than a . - * - * 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() */ -#include /* EXIT_FAILURE, free(), strtoul(), - EXIT_SUCCESS */ -#include /* pid_t, DIR, struct dirent, - ssize_t */ -#include /* opendir(), readdir(), closedir() */ -#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 = (pid_t) strtoul(proc_ent->d_name, NULL, 10); - if(pid == 0){ - /* Not a process */ - continue; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - 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){ - 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-10 06:00:50 +0000 +++ plugins.d/usplash.c 1970-01-01 00:00:00 +0000 @@ -1,522 +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() */ -#include /* close(), write(), readlink(), - read(), STDOUT_FILENO, sleep(), - fork(), setuid(), geteuid(), - setsid(), chdir(), dup2(), - STDERR_FILENO, execv() */ -#include /* free(), EXIT_FAILURE, strtoul(), - realloc(), EXIT_SUCCESS, malloc(), - _exit() */ -#include /* getenv() */ -#include /* opendir(), readdir(), closedir() */ -#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 = (pid_t) strtoul(proc_ent->d_name, NULL, 10); - if(pid == 0){ - /* Not a process */ - continue; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - 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){ - perror("lstat"); - free(exe_link); - free(prompt); - closedir(proc_dir); - return EXIT_FAILURE; - } - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - continue; - } - - sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - if(sret == -1){ - continue; - } - } - 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 - - -
- - - - -