=== modified file 'DBUS-API' --- DBUS-API 2020-01-18 00:57:30 +0000 +++ DBUS-API 2020-07-04 11:58:52 +0000 @@ -131,8 +131,8 @@ * Copyright - Copyright © 2010-2019 Teddy Hogeborn - Copyright © 2010-2019 Björn Påhlsson + Copyright © 2010-2020 Teddy Hogeborn + Copyright © 2010-2020 Björn Påhlsson ** License: === modified file 'Makefile' --- Makefile 2020-04-08 18:40:10 +0000 +++ Makefile 2023-02-07 18:59:50 +0000 @@ -29,7 +29,7 @@ # For info about _FORTIFY_SOURCE, see feature_test_macros(7) # and . -FORTIFY:=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC +FORTIFY:=-D_FORTIFY_SOURCE=3 -fstack-protector-all -fPIC LINK_FORTIFY_LD:=-z relro -z now LINK_FORTIFY:= @@ -43,7 +43,7 @@ LANGUAGE:=-std=gnu11 FEATURES:=-D_FILE_OFFSET_BITS=64 htmldir:=man -version:=1.8.11 +version:=1.8.15 SED:=sed PKG_CONFIG?=pkg-config @@ -96,8 +96,10 @@ GNUTLS_LIBS:=$(shell $(PKG_CONFIG) --libs gnutls) AVAHI_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I avahi-core) AVAHI_LIBS:=$(shell $(PKG_CONFIG) --libs avahi-core) -GPGME_CFLAGS:=$(shell gpgme-config --cflags; getconf LFS_CFLAGS) -GPGME_LIBS:=$(shell gpgme-config --libs; getconf LFS_LIBS; \ +GPGME_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I gpgme 2>/dev/null \ + || gpgme-config --cflags; getconf LFS_CFLAGS) +GPGME_LIBS:=$(shell $(PKG_CONFIG) --libs gpgme 2>/dev/null \ + || gpgme-config --libs; getconf LFS_LIBS; \ getconf LFS_LDFLAGS) LIBNL3_CFLAGS:=$(shell $(PKG_CONFIG) --cflags-only-I libnl-route-3.0) LIBNL3_LIBS:=$(shell $(PKG_CONFIG) --libs libnl-route-3.0) @@ -296,6 +298,7 @@ # Need to add the GLib and pthread libraries dracut-module/password-agent: CFLAGS += $(GLIB_CFLAGS) +# Note: -lpthread is unnecessary with the GNU C library 2.34 or later dracut-module/password-agent: LDLIBS += $(GLIB_LIBS) -lpthread .PHONY: clean === modified file 'NEWS' --- NEWS 2020-04-08 18:40:10 +0000 +++ NEWS 2022-04-25 18:46:48 +0000 @@ -1,7 +1,29 @@ This NEWS file records noteworthy changes, very tersely. See the manual for detailed information. -Version 1.8.11 +Version 1.8.15 (2022-04-25) +* Server +** Bug fix: When running "mandos-keygen --password" to read a password + interactively (to save in a section in the clients.conf file), + backslashes in the password are no longer interpreted as backslash + escapes. +** GnuTLS debug output no longer has a "b'" prefix. + +Version 1.8.14 (2021-02-03) +* Client +** Create /dev/fd symlink (if necessary) in plugin-runner(8mandos) and + mandos-client(8mandos) (Workaround for Debian bug #981302) + +Version 1.8.13 (2020-11-30) +* Client +** Fix unreliable test in password-agent(8mandos). + +Version 1.8.12 (2020-07-04) +* Client +** Fix compatibility with the GNU C Library version 2.31. +** In initramfs-tools boots, only use setsid(1) when available. + +Version 1.8.11 (2020-04-08) * Client ** Fix file descriptor leak when adding or removing local routes to unreachable hosts on the local network. === modified file 'clients.conf' --- clients.conf 2019-02-09 23:34:15 +0000 +++ clients.conf 2023-02-07 19:29:28 +0000 @@ -4,6 +4,7 @@ # How long until a client is disabled and not be allowed to get the # data this server holds. +# (RFC 3339 duration syntax) ;timeout = PT5M # How often to run the checker to confirm that a client is still up. @@ -11,11 +12,13 @@ # running. The server will wait for a checker to complete until the # above "timeout" occurs, at which time the client will be disabled, # and any running checker killed. +# (RFC 3339 duration syntax) ;interval = PT2M # Extended timeout is an added timeout that is given once after a # password has been sent sucessfully to a client. This allows for # additional delays caused by file system checks and quota checks. +# (RFC 3339 duration syntax) ;extended_timeout = PT15M # What command to run as "the checker". @@ -25,9 +28,11 @@ ;approved_by_default = True # How long to wait for approval. +# (RFC 3339 duration syntax) ;approval_delay = PT0S # How long one approval will last. +# (RFC 3339 duration syntax) ;approval_duration = PT1S # Whether this client is enabled by default @@ -45,8 +50,9 @@ ;fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920 ; ;# This is base64-encoded binary data. It will be decoded and sent to -;# the client matching the above fingerprint. This should, of course, -;# be OpenPGP encrypted data, decryptable only by the client. +;# the client matching the above key_id (for GnuTLS 3.6.6 or later) or +;# the above fingerprint (for GnuTLS before 3.6.0). This should, of +;# course, be OpenPGP encrypted data, decryptable only by the client. ;secret = ; hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234 ; REJMVv7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+N === modified file 'common.ent' --- common.ent 2020-04-08 18:40:10 +0000 +++ common.ent 2022-04-25 18:46:48 +0000 @@ -1,3 +1,3 @@ - + === modified file 'debian/changelog' --- debian/changelog 2020-04-08 18:40:10 +0000 +++ debian/changelog 2022-04-25 18:46:48 +0000 @@ -1,3 +1,44 @@ +mandos (1.8.15-1) unstable; urgency=medium + + * New upstream release. + * debian/po/fr.po: Add missing whitespace to the id and translation + for msgid " ${key_id}". + * debian/mandos-client.lintian-overrides: Remove all empty commented + lines. Rename "setuid-binary" tag to "elevated-privileges". + * debian/control (Standards-Version): Change to "4.6.0". + * debian/copyright: Update copyright year to 2022. + * debian/po/es.po: Add Spanish translation of the debconf template + (Closes: #987595). + + -- Teddy Hogeborn Mon, 25 Apr 2022 20:43:27 +0200 + +mandos (1.8.14-1) unstable; urgency=medium + + * New upstream release. Includes workaround for #981302 + * debian/po/fr.po: Fix msgid to match template. + + -- Teddy Hogeborn Wed, 03 Feb 2021 09:53:29 +0100 + +mandos (1.8.13-1) unstable; urgency=medium + + * New upstream release. + * Fix "password-agent autopkgtest does not seem to be reliable" + by making the test more reliable (Closes: #975457) + * debian/mandos-client.templates: Rename "_description" header to + "_Description". + * debian/control (Standards-Version): Change to "4.5.1". + + -- Teddy Hogeborn Mon, 30 Nov 2020 19:15:43 +0100 + +mandos (1.8.12-1) unstable; urgency=medium + + * New upstream release. + * Fix "FTBFS with glibc 2.31 (uses removed stime function)" by using + clock_settime() instead. (Closes: #964226) + * debian/copyright: Update copyright year to 2020. + + -- Teddy Hogeborn Sat, 04 Jul 2020 14:58:26 +0200 + mandos (1.8.11-1) unstable; urgency=medium * New upstream release. === modified file 'debian/control' --- debian/control 2020-03-21 16:23:36 +0000 +++ debian/control 2023-02-07 23:45:13 +0000 @@ -11,7 +11,7 @@ xsltproc, pkg-config, libnl-route-3-dev, systemd Build-Depends-Indep: python3 (>= 3), python3-dbus, python3-gi, po-debconf -Standards-Version: 4.5.0 +Standards-Version: 4.6.2 Vcs-Bzr: https://ftp.recompile.se/pub/mandos/trunk Vcs-Browser: https://bzr.recompile.se/loggerhead/mandos/trunk/files Homepage: https://www.recompile.se/mandos === modified file 'debian/copyright' --- debian/copyright 2019-02-10 04:20:26 +0000 +++ debian/copyright 2022-04-24 16:54:30 +0000 @@ -4,8 +4,8 @@ Source: Files: * -Copyright: Copyright © 2008-2019 Teddy Hogeborn - Copyright © 2008-2019 Björn Påhlsson +Copyright: Copyright © 2008-2022 Teddy Hogeborn + Copyright © 2008-2022 Björn Påhlsson License: GPL-3+ This file is part of Mandos. . === modified file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2019-08-05 21:14:05 +0000 +++ debian/mandos-client.lintian-overrides 2022-04-23 21:14:38 +0000 @@ -1,32 +1,28 @@ # 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 +# binaries which are only meant to be run inside an initial RAM disk # environment (except for test purposes). It would be insecure to # allow anyone to run them. -# mandos-client binary: non-standard-dir-perm usr/lib/*/mandos/plugins.d/ 0700 != 0755 # Likewise for helper executables for plugins mandos-client binary: non-standard-dir-perm usr/lib/*/mandos/plugin-helpers/ 0700 != 0755 # These binaries must be setuid root, since they need root powers, but # are started by plugin-runner(8mandos), which runs all plugins as -# user/group "_mandos". These binaries are not run in a running -# system, but in an initial RAM disk environment. Here they are +# user/group "_mandos". These binaries are never run in a running +# system, but only in an initial RAM disk environment. Here they are # protected from non-root access by the directory permissions, above. -# -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/mandos-client 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/askpass-fifo 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/splashy 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/usplash 4755 root/root -mandos-client binary: setuid-binary usr/lib/*/mandos/plugins.d/plymouth 4755 root/root +mandos-client binary: elevated-privileges usr/lib/*/mandos/plugins.d/mandos-client 4755 root/root +mandos-client binary: elevated-privileges usr/lib/*/mandos/plugins.d/askpass-fifo 4755 root/root +mandos-client binary: elevated-privileges usr/lib/*/mandos/plugins.d/splashy 4755 root/root +mandos-client binary: elevated-privileges usr/lib/*/mandos/plugins.d/usplash 4755 root/root +mandos-client binary: elevated-privileges usr/lib/*/mandos/plugins.d/plymouth 4755 root/root # The directory /etc/mandos/plugins.d can be used by local system # administrators to place plugins in, overriding and complementing # /usr/lib//mandos/plugins.d, and must be likewise protected. -# mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755 # Likewise for plugin-helpers directory mandos-client binary: non-standard-dir-perm etc/mandos/plugin-helpers/ 0700 != 0755 === modified file 'debian/mandos-client.templates' --- debian/mandos-client.templates 2019-08-05 21:00:35 +0000 +++ debian/mandos-client.templates 2020-11-30 16:19:48 +0000 @@ -1,6 +1,6 @@ Template: mandos-client/key_id Type: note -_description: New client option "${key_id}" is REQUIRED on server +_Description: New client option "${key_id}" is REQUIRED on server A new "key_id" client option is REQUIRED in the server's clients.conf file, otherwise this computer most likely will not reboot unattended. This option: === added file 'debian/po/es.po' --- debian/po/es.po 1970-01-01 00:00:00 +0000 +++ debian/po/es.po 2022-04-25 18:28:52 +0000 @@ -0,0 +1,155 @@ +# mandos po-debconf translation to Spanish. +# Copyright (C) 2021 +# This file is distributed under the same license as the mandos package. +# Camaleón , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: mandos\n" +"Report-Msgid-Bugs-To: Mandos Maintainers \n" +"POT-Creation-Date: 2021-04-14 17:23+0000\n" +"PO-Revision-Date: 2021-04-15 17:43+0200\n" +"Last-Translator: Camaleón \n" +"Language-Team: Debian Spanish \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "New client option \"key_id\" is REQUIRED on server" +msgstr "Se requiere una nueva opción del cliente «key_id» en el servidor" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"A new \"key_id\" client option is REQUIRED in the clients.conf file, " +"otherwise the client most likely will not reboot unattended. This option:" +msgstr "" +"Se requiere una nueva opción de cliente «key_id» en el archivo clients." +"conf, de lo contrario es probable que el cliente no pueda reiniciarse " +"sin supervisión. Debe añadir esta opción:" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid " key_id = " +msgstr " key_id = " + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"must be added in the file /etc/mandos/clients.conf, right before the " +"\"fingerprint\" option, for each Mandos client. You must edit that file and " +"add this option for all clients. To see the correct key ID for each client, " +"run this command (on each client):" +msgstr "" +"en el archivo «/etc/mandos/clients.conf», justo antes de la opción «fingerprint»" +"para cada cliente Mandos. Debe editar ese archivo y añadir esta opción para " +"todos los clientes. Para ver el identificador de la clave correcto de cada cliente, " +"ejecute la siguiente orden en cada uno de los clientes:" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid " mandos-keygen -F/dev/null|grep ^key_id" +msgstr "mandos-keygen -F/dev/null|grep ^key_id" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"Note: the clients must all also be using GnuTLS 3.6.6 or later; the server " +"cannot serve passwords for both old and new clients!" +msgstr "" +"Nota: todos los clientes deben usar también GnuTLS 3.6.6 o una versión superior; " +"¡el servidor no puede servir contraseñas para clientes antiguos y nuevos al " +"mismo tiempo!" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"Rationale: With GnuTLS 3.6.6, Mandos has been forced to stop using OpenPGP " +"keys as TLS session keys. A new TLS key pair will be generated on each " +"client and will be used as identification, but the key ID of the public key " +"needs to be added to this server, since this will now be used to identify " +"the client to the server." +msgstr "" +"Explicación: Con GnuTLS 3.6.6, Mandos se ha visto obligado a dejar de usar " +"claves OpenPGP como claves de sesión TLS. Se generará un nuevo par de claves " +"para cada cliente que se utilizarán para su identificación, pero necesita añadir " +"el identificador de la clave pública a este servidor, ya que ahora se utilizará " +"para identificar al cliente en el servidor." + +#. Type: note +#. Description +#: ../mandos.templates:2001 +msgid "Bad key IDs have been removed from clients.conf" +msgstr "Se han eliminado identificadores de claves incorrectas de clients.conf" + +#. Type: note +#. Description +#: ../mandos.templates:2001 +msgid "" +"Bad key IDs, which were created by a bug in Mandos client 1.8.0, have been " +"removed from /etc/mandos/clients.conf" +msgstr "" +"Se han eliminado los identificadores de claves incorrectas de «/etc/mandos/clients" +".conf» que se habían creado debido a un fallo en la versión 1.8.0 del cliente " +"Mandos." + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "New client option \"${key_id}\" is REQUIRED on server" +msgstr "Se requiere una nueva opción del cliente «${key_id}» en el servidor" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"A new \"key_id\" client option is REQUIRED in the server's clients.conf " +"file, otherwise this computer most likely will not reboot unattended. This " +"option:" +msgstr "" +"Se requiere una nueva opción del cliente «key_id» en el archivo clients." +"conf del servidor, de lo contrario es probable que este equipo no pueda " +"reiniciarse sin supervisión. Debe añadir esta opción:" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid " ${key_id}" +msgstr " ${key_id}" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"must be added (all on one line!) on the Mandos server host, in the file /etc/" +"mandos/clients.conf, right before the \"fingerprint\" option for this Mandos " +"client. You must edit that file on that server and add this option." +msgstr "" +"(¡en una sola línea!) en el servidor Mandos, en el archivo «/etc/mandos/" +"clients.conf», justo antes de la opción «fingerprint» para este cliente Mandos. " +"Debe editar ese archivo en el servidor y añadir esta opción." + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"With GnuTLS 3.6.6, Mandos has been forced to stop using OpenPGP keys as TLS " +"session keys. A new TLS key pair has been generated and will be used as " +"identification, but the key ID of the public key needs to be added to the " +"server, since this will now be used to identify the client to the server." +msgstr "" +"Con GnuTLS 3.6.6, Mandos se ha visto obligado a dejar de usar claves OpenPGP " +"como claves de sesión TLS. Se ha generado un nuevo par de claves TLS que se " +"utilizarán para su identificación, pero necesita añadir el identificador de la " +"clave pública al servidor, ya que ahora se utilizará para identificar al cliente " +"en el servidor." === modified file 'debian/po/fr.po' --- debian/po/fr.po 2019-08-16 19:32:47 +0000 +++ debian/po/fr.po 2023-02-07 23:29:39 +0000 @@ -35,8 +35,8 @@ #. Type: note #. Description #: ../mandos.templates:1001 -msgid "key_id = " -msgstr "key_id = " +msgid " key_id = " +msgstr " key_id = " #. Type: note #. Description @@ -56,8 +56,8 @@ #. Type: note #. Description #: ../mandos.templates:1001 -msgid "mandos-keygen -F/dev/null|grep ^key_id" -msgstr "mandos-keygen -F/dev/null|grep ^key_id" +msgid " mandos-keygen -F/dev/null|grep ^key_id" +msgstr " mandos-keygen -F/dev/null|grep ^key_id" #. Type: note #. Description @@ -124,8 +124,8 @@ #. Type: note #. description #: ../mandos-client.templates:1001 -msgid "${key_id}" -msgstr "${key_id}" +msgid " ${key_id}" +msgstr " ${key_id}" #. Type: note #. description === added file 'debian/po/pt_BR.po' --- debian/po/pt_BR.po 1970-01-01 00:00:00 +0000 +++ debian/po/pt_BR.po 2023-02-07 23:18:19 +0000 @@ -0,0 +1,156 @@ +# Debconf translations for mandos. +# Copyright (C) 2022 THE mandos'S COPYRIGHT HOLDER +# This file is distributed under the same license as the mandos package. +# Paulo Henrique de Lima Santana (phls) , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: mandos_1.8.15-1.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-29 18:51+0000\n" +"PO-Revision-Date: 2022-12-19 12:35-0300\n" +"Last-Translator: Paulo Henrique de Lima Santana (phls) \n" +"Language-Team: Brazilian Portuguese \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"X-Generator: Gtranslator 42.0\n" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "New client option \"key_id\" is REQUIRED on server" +msgstr "A nova opção para cliente \"key_id\" é NECESSÁRIA no servidor" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"A new \"key_id\" client option is REQUIRED in the clients.conf file, " +"otherwise the client most likely will not reboot unattended. This option:" +msgstr "" +"Uma nova opção para cliente \"key_id\" é NECESSÁRIA no arquivo clients.conf, " +"caso contrário, o cliente provavelmente não será reinicializado " +"automaticamente. Esta opção:" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid " key_id = " +msgstr " key_id = " + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"must be added in the file /etc/mandos/clients.conf, right before the " +"\"fingerprint\" option, for each Mandos client. You must edit that file and " +"add this option for all clients. To see the correct key ID for each client, " +"run this command (on each client):" +msgstr "" +"deve ser adicionada no arquivo /etc/mandos/clients.conf, logo antes da opção " +"\"fingerprint\", para cada cliente Mandos. Você deve editar esse arquivo e " +"adicionar essa opção para todos os clientes. Para ver o ID da chave correta " +"para cada cliente, execute este comando (em cada cliente):" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid " mandos-keygen -F/dev/null|grep ^key_id" +msgstr " mandos-keygen -F/dev/null|grep ^key_id" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"Note: the clients must all also be using GnuTLS 3.6.6 or later; the server " +"cannot serve passwords for both old and new clients!" +msgstr "" +"Nota: todos os clientes também devem estar usando GnuTLS 3.6.6 ou posterior; " +"o servidor não pode fornecer senhas para clientes antigos e novos!" + +#. Type: note +#. Description +#: ../mandos.templates:1001 +msgid "" +"Rationale: With GnuTLS 3.6.6, Mandos has been forced to stop using OpenPGP " +"keys as TLS session keys. A new TLS key pair will be generated on each " +"client and will be used as identification, but the key ID of the public key " +"needs to be added to this server, since this will now be used to identify " +"the client to the server." +msgstr "" +"Justificativa: com o GnuTLS 3.6.6, o Mandos foi forçado a parar de usar " +"chaves OpenPGP como chaves de sessão TLS. Um novo par de chaves TLS será " +"gerado em cada cliente e será usado como identificação, mas o ID da chave " +"pública precisa ser adicionado a este servidor, pois agora será usado para " +"identificar o cliente para o servidor." + +#. Type: note +#. Description +#: ../mandos.templates:2001 +msgid "Bad key IDs have been removed from clients.conf" +msgstr "IDs de chaves ruins foram removidos de clients.conf" + +#. Type: note +#. Description +#: ../mandos.templates:2001 +msgid "" +"Bad key IDs, which were created by a bug in Mandos client 1.8.0, have been " +"removed from /etc/mandos/clients.conf" +msgstr "" +"IDs de chaves ruins, que foram criados por um bug no cliente Mandos 1.8.0, " +"foram removidos de /etc/mandos/clients.conf" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "New client option \"${key_id}\" is REQUIRED on server" +msgstr "A nova opção para cliente \"${key_id}\" é OBRIGATÓRIA no servidor" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"A new \"key_id\" client option is REQUIRED in the server's clients.conf " +"file, otherwise this computer most likely will not reboot unattended. This " +"option:" +msgstr "" +"Uma nova opção para cliente \"key_id\" é NECESSÁRIA no arquivo clients.conf " +"do servidor, caso contrário, este computador provavelmente não será " +"reinicializado automaticamente. Esta opção:" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid " ${key_id}" +msgstr " ${key_id}" + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"must be added (all on one line!) on the Mandos server host, in the file /etc/" +"mandos/clients.conf, right before the \"fingerprint\" option for this Mandos " +"client. You must edit that file on that server and add this option." +msgstr "" +"deve ser adicionada (tudo em uma linha!) na máquina do servidor Mandos, no " +"arquivo /etc/mandos/clients.conf, logo antes da opção \"fingerprint\" para " +"este cliente do Mandos. Você deve editar esse arquivo nesse servidor e " +"adicionar essa opção." + +#. Type: note +#. Description +#: ../mandos-client.templates:1001 +msgid "" +"With GnuTLS 3.6.6, Mandos has been forced to stop using OpenPGP keys as TLS " +"session keys. A new TLS key pair has been generated and will be used as " +"identification, but the key ID of the public key needs to be added to the " +"server, since this will now be used to identify the client to the server." +msgstr "" +"Com o GnuTLS 3.6.6, o Mandos foi forçado a parar de usar chaves OpenPGP como " +"chaves de sessão TLS. Um novo par de chaves TLS foi gerado e será usado como " +"identificação, mas o ID da chave pública precisa ser adicionado ao servidor, " +"pois agora será usado para identificar o cliente para o servidor." === modified file 'debian/rules' --- debian/rules 2019-04-09 22:31:23 +0000 +++ debian/rules 2023-02-07 19:40:12 +0000 @@ -35,7 +35,7 @@ --exclude etc/mandos/plugin-helpers \ --exclude usr/lib/$(DEB_HOST_MULTIARCH)/mandos/plugins.d \ --exclude usr/lib/$(DEB_HOST_MULTIARCH)/mandos/plugin-helpers \ - --exclude usr/share/doc/mandos-client/examples/network-hooks.d + --exclude usr/share/doc/mandos-client/examples/network-hooks.d/ chmod --recursive g-w -- \ "$(CURDIR)/debian/mandos-client/usr/share/doc/mandos-client/examples/network-hooks.d" === modified file 'dracut-module/ask-password-mandos.service' --- dracut-module/ask-password-mandos.service 2020-02-05 21:39:28 +0000 +++ dracut-module/ask-password-mandos.service 2020-07-04 11:58:52 +0000 @@ -1,7 +1,7 @@ # -*- systemd -*- # -# Copyright © 2019 Teddy Hogeborn -# Copyright © 2019 Björn Påhlsson +# Copyright © 2019-2020 Teddy Hogeborn +# Copyright © 2019-2020 Björn Påhlsson # # This file is part of Mandos. # === modified file 'dracut-module/password-agent.c' --- dracut-module/password-agent.c 2020-03-14 03:22:36 +0000 +++ dracut-module/password-agent.c 2023-02-07 19:50:53 +0000 @@ -1,9 +1,9 @@ -/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */ +/* -*- coding: utf-8; lexical-binding: t -*- */ /* * Mandos password agent - Simple password agent to run Mandos client * - * Copyright © 2019 Teddy Hogeborn - * Copyright © 2019 Björn Påhlsson + * Copyright © 2019-2022 Teddy Hogeborn + * Copyright © 2019-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,11 +23,13 @@ * Contact the authors at . */ -#define _GNU_SOURCE -#include /* uintmax_t, PRIuMAX, PRIdMAX, - intmax_t, uint32_t, SCNx32, - SCNuMAX, SCNxMAX */ -#include /* size_t */ +#define _GNU_SOURCE /* pipe2(), O_CLOEXEC, setresgid(), + setresuid(), asprintf(), getline(), + basename() */ +#include /* uintmax_t, strtoumax(), PRIuMAX, + PRIdMAX, intmax_t, uint32_t, + SCNx32, SCNuMAX, SCNxMAX */ +#include /* size_t, NULL */ #include /* pid_t, uid_t, gid_t, getuid(), getpid() */ #include /* bool, true, false */ @@ -40,10 +42,17 @@ NSIG, sigismember(), SA_ONSTACK, SIG_DFL, SIG_IGN, SIGINT, SIGQUIT, SIGHUP, SIGSTOP, SIG_UNBLOCK */ +#include /* uid_t, gid_t, close(), pipe2(), + fork(), _exit(), dup2(), + STDOUT_FILENO, setresgid(), + setresuid(), execv(), ssize_t, + read(), dup3(), getuid(), dup(), + STDERR_FILENO, pause(), write(), + rmdir(), unlink(), getpid() */ #include /* EXIT_SUCCESS, EXIT_FAILURE, - malloc(), free(), strtoumax(), - realloc(), setenv(), calloc(), - mkdtemp(), mkostemp() */ + malloc(), free(), realloc(), + setenv(), calloc(), mkdtemp(), + mkostemp() */ #include /* not, or, and, xor */ #include /* error() */ #include /* EX_USAGE, EX_OSERR, EX_OSFILE */ @@ -57,7 +66,7 @@ #include /* strdup(), memcpy(), explicit_bzero(), memset(), strcmp(), strlen(), strncpy(), - memcmp(), basename() */ + memcmp(), basename(), strerror() */ #include /* argz_create(), argz_count(), argz_extract(), argz_next(), argz_add() */ @@ -73,14 +82,7 @@ ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS, struct argp, argp_parse(), ARGP_NO_EXIT */ -#include /* SIZE_MAX */ -#include /* uid_t, gid_t, close(), pipe2(), - fork(), _exit(), dup2(), - STDOUT_FILENO, setresgid(), - setresuid(), execv(), ssize_t, - read(), dup3(), getuid(), dup(), - STDERR_FILENO, pause(), write(), - rmdir(), unlink(), getpid() */ +#include /* SIZE_MAX, uint32_t */ #include /* munlock(), mlock() */ #include /* O_CLOEXEC, O_NONBLOCK, fcntl(), F_GETFD, F_GETFL, FD_CLOEXEC, @@ -110,8 +112,9 @@ g_assert_null(), g_assert_false(), g_assert_cmpint(), g_assert_cmpuint(), g_test_skip(), g_assert_cmpstr(), - g_test_init(), g_test_add(), g_test_run(), - GOptionContext, g_option_context_new(), + g_test_message(), g_test_init(), g_test_add(), + g_test_run(), GOptionContext, + g_option_context_new(), g_option_context_set_help_enabled(), FALSE, g_option_context_set_ignore_unknown_options(), gboolean, GOptionEntry, G_OPTION_ARG_NONE, @@ -1095,7 +1098,15 @@ } ievent_buffer; struct inotify_event *const ievent = &ievent_buffer.event; +#if defined(__GNUC__) and __GNUC__ >= 7 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif const ssize_t read_length = read(fd, ievent, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 7 +#pragma GCC diagnostic pop +#endif if(read_length == 0){ /* EOF */ error(0, 0, "Got EOF from inotify fd for directory %s", filename); *quit_now = true; @@ -1193,8 +1204,8 @@ bool *const password_is_read = task.password_is_read; /* We use the GLib "Key-value file parser" functions to parse the - question file. See for specification of contents */ + question file. See for + specification of contents */ __attribute__((nonnull)) void cleanup_g_key_file(GKeyFile **key_file){ if(*key_file != NULL){ @@ -1490,8 +1501,8 @@ not. You may but don't have to include a final NUL byte in your message. - — (Wed 08 Oct 2014 02:14:28 AM UTC) + — (Tue, 15 Sep 2020 + 14:24:20 GMT) */ send_buffer[0] = '+'; /* Prefix with "+" */ /* Always add an extra NUL */ @@ -1502,7 +1513,7 @@ errno = 0; ssize_t ssret = send(fd, send_buffer, send_buffer_length, MSG_NOSIGNAL); - const error_t saved_errno = errno; + const error_t saved_errno = (ssret < 0) ? errno : 0; #if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25) explicit_bzero(send_buffer, send_buffer_length); #else @@ -1526,8 +1537,8 @@ /* Retry, below */ break; case EMSGSIZE: - error(0, 0, "Password of size %" PRIuMAX " is too big", - (uintmax_t)password->length); + error(0, saved_errno, "Password of size %" PRIuMAX + " is too big", (uintmax_t)password->length); #if __GNUC__ < 7 /* FALLTHROUGH */ #else @@ -1535,7 +1546,9 @@ #endif case 0: if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){ - error(0, 0, "Password only partially sent to socket"); + error(0, 0, "Password only partially sent to socket %s: %" + PRIuMAX " out of %" PRIuMAX " bytes sent", filename, + (uintmax_t)ssret, (uintmax_t)send_buffer_length); } #if __GNUC__ < 7 /* FALLTHROUGH */ @@ -2648,7 +2661,7 @@ bool password_is_read = false; const char helper_directory[] = "/nonexistent"; const char *const argv[] = { "/bin/sh", "-c", - "echo -n ${MANDOSPLUGINHELPERDIR}", NULL }; + "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL }; const bool success = start_mandos_client(queue, epoll_fd, &mandos_client_exited, @@ -4177,8 +4190,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4272,8 +4293,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4369,8 +4398,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4454,8 +4491,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4538,8 +4583,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4614,8 +4667,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -4693,8 +4754,16 @@ memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name)); const size_t ievent_size = (sizeof(struct inotify_event) + sizeof(dummy_file_name)); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic push + /* ievent is pointing into a struct which is of sufficient size */ +#pragma GCC diagnostic ignored "-Wstringop-overread" +#endif g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size), ==, ievent_size); +#if defined(__GNUC__) and __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif g_assert_cmpint(close(pipefds[1]), ==, 0); bool quit_now = false; @@ -5807,7 +5876,7 @@ char write_data[PIPE_BUF]; { /* Construct test password buffer */ - /* Start with + since that is what the real procotol uses */ + /* Start with + since that is what the real protocol uses */ write_data[0] = '+'; /* Set a special character at string end just to mark the end */ write_data[sizeof(write_data)-2] = 'y'; @@ -5958,9 +6027,6 @@ test_fixture *fixture, __attribute__((unused)) gconstpointer user_data){ -#ifndef __amd64__ - g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform"); -#else __attribute__((cleanup(cleanup_close))) const int epoll_fd = epoll_create1(EPOLL_CLOEXEC); g_assert_cmpint(epoll_fd, >=, 0); @@ -5968,31 +6034,70 @@ char *const filename = strdup("/nonexistent/socket"); __attribute__((cleanup(string_set_clear))) string_set cancelled_filenames = {}; - const size_t oversized = 1024*1024; /* Limit seems to be 212960 */ - __attribute__((cleanup(cleanup_buffer))) - buffer password = { - .data=malloc(oversized), - .length=oversized, - .allocated=oversized, + int socketfds[2]; + + /* Find a message size which triggers EMSGSIZE */ + __attribute__((cleanup(cleanup_string))) + char *message_buffer = NULL; + size_t message_size = PIPE_BUF + 1; + for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){ + if(message_size >= 1024*1024*1024){ /* 1 GiB */ + g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB"); + return; + } + message_buffer = realloc(message_buffer, message_size); + if(message_buffer == NULL){ + g_test_skip("Skipping EMSGSIZE test"); + g_test_message("Failed to malloc() %" PRIuMAX " bytes", + (uintmax_t)message_size); + return; + } + /* Fill buffer with 'x' */ + memset(message_buffer, 'x', message_size); + /* Create a new socketpair for each message size to avoid having + to empty the pipe by reading the message to a separate buffer + */ + g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM + | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, + socketfds), ==, 0); + ssret = send(socketfds[1], message_buffer, message_size, + MSG_NOSIGNAL); + error_t saved_errno = errno; + g_assert_cmpint(close(socketfds[0]), ==, 0); + g_assert_cmpint(close(socketfds[1]), ==, 0); + + if(ssret < 0){ + if(saved_errno != EMSGSIZE) { + g_test_skip("Skipping EMSGSIZE test"); + g_test_message("Error on send(%" PRIuMAX " bytes): %s", + (uintmax_t)message_size, + strerror(saved_errno)); + return; + } + break; + } else if(ssret != (ssize_t)message_size){ + g_test_skip("Skipping EMSGSIZE test"); + g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX + " bytes", (uintmax_t)ssret, + (intmax_t)message_size); + return; + } + } + g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes", + (intmax_t)message_size); + + buffer password = { + .data=message_buffer, + .length=message_size - 2, /* Compensate for added '+' and NUL */ + .allocated=message_size, }; - g_assert_nonnull(password.data); if(mlock(password.data, password.allocated) != 0){ g_assert_true(errno == EPERM or errno == ENOMEM); } - /* Construct test password buffer */ - /* Start with + since that is what the real procotol uses */ - password.data[0] = '+'; - /* Set a special character at string end just to mark the end */ - password.data[oversized-3] = 'y'; - /* Set NUL at buffer end, as suggested by the protocol */ - password.data[oversized-2] = '\0'; - /* Fill rest of password with 'x' */ - memset(password.data+1, 'x', oversized-3); __attribute__((cleanup(cleanup_queue))) task_queue *queue = create_queue(); g_assert_nonnull(queue); - int socketfds[2]; g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, socketfds), ==, 0); @@ -6019,7 +6124,6 @@ g_assert_cmpuint((unsigned int)queue->length, ==, 0); g_assert_true(string_set_contains(cancelled_filenames, question_filename)); -#endif } static void test_send_password_to_socket_retry(__attribute__((unused)) @@ -8155,3 +8259,69 @@ g_option_context_free(context); return should_run_tests != FALSE; } + +/* +Local Variables: +run-tests: +(lambda () + (if (not (funcall run-tests-in-test-buffer default-directory)) + (funcall show-test-buffer-in-test-window) + (funcall remove-test-window))) +run-tests-in-test-buffer: +(lambda (dir) + (with-current-buffer (get-buffer-create "*Test*") + (setq buffer-read-only nil + default-directory dir) + (erase-buffer) + (compilation-mode)) + (let ((process-result + (let ((inhibit-read-only t)) + (process-file-shell-command + (funcall get-command-line) nil "*Test*")))) + (and (numberp process-result) + (= process-result 0)))) +get-command-line: +(lambda () + (let* + ((build-directory + (funcall find-build-directory (buffer-file-name))) + (local-build-directory + (if (fboundp 'file-local-name) + (file-local-name build-directory) + (or (file-remote-p build-directory 'localname) + build-directory))) + (command + (file-relative-name (file-name-sans-extension + (buffer-file-name)) build-directory)) + (qbdir (shell-quote-argument local-build-directory)) + (qcmd (shell-quote-argument command))) + (format (concat "cd %s && CFLAGS=-Werror make --silent %s" + " && %s --test --verbose") qbdir qcmd qcmd))) +find-build-directory: +(lambda (try-directory &optional base-directory) + (let ((base-directory (or base-directory try-directory))) + (cond ((equal try-directory "/") base-directory) + ((file-readable-p + (concat (file-name-as-directory try-directory) + "Makefile")) try-directory) + ((funcall find-build-directory + (directory-file-name (file-name-directory + try-directory)) + base-directory))))) +show-test-buffer-in-test-window: +(lambda () + (when (not (get-buffer-window-list "*Test*")) + (setq next-error-last-buffer (get-buffer "*Test*")) + (let* ((side (if (>= (window-width) 146) 'right 'bottom)) + (display-buffer-overriding-action + `((display-buffer-in-side-window) (side . ,side) + (window-height . fit-window-to-buffer) + (window-width . fit-window-to-buffer)))) + (display-buffer "*Test*")))) +remove-test-window: +(lambda () + (let ((test-window (get-buffer-window "*Test*"))) + (if test-window (delete-window test-window)))) +eval: (add-hook 'after-save-hook run-tests 90 t) +End: +*/ === modified file 'dracut-module/password-agent.xml' --- dracut-module/password-agent.xml 2019-11-16 15:56:49 +0000 +++ dracut-module/password-agent.xml 2022-04-24 16:54:30 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -32,6 +32,7 @@ 2019 + 2020 Teddy Hogeborn Björn Påhlsson @@ -113,9 +114,9 @@ be a systemd 1 Password Agent (See Password Agents). The aim of this program is therefore - to acquire and then send a password to some other program which + url="https://systemd.io/PASSWORD_AGENTS/">Password + Agents). The aim of this program is therefore to + acquire and then send a password to some other program which will use the password to unlock the encrypted root disk. @@ -146,9 +147,8 @@ Specify a different agent directory. The default is /run/systemd/ask-password as per the - Password Agents specification. + Password + Agents specification. @@ -270,8 +270,8 @@ responsible for getting a password from the Mandos client program itself, and to send that password to whatever is currently asking for a password using the systemd Password Agents mechanism. + url="https://systemd.io/PASSWORD_AGENTS/">Password + Agents mechanism. To accomplish this, &COMMANDNAME; runs the mandos-client program (which is the actual @@ -281,9 +281,8 @@ password is acquired from the MANDOS_CLIENT program, sends that password (as per the Password Agents specification) to all currently - unanswered password questions. + url="https://systemd.io/PASSWORD_AGENTS/">Password Agents + specification) to all currently unanswered password questions. This program should be started (normally as a systemd service, @@ -330,9 +329,9 @@ The default directory to watch for password questions as per the Password Agents specification; can be changed - by the option. + url="https://systemd.io/PASSWORD_AGENTS/">Password + Agents specification; can be changed by the + option. @@ -446,9 +445,8 @@ - Password Agents + Password + Agents === modified file 'initramfs-tools-script' --- initramfs-tools-script 2018-08-19 01:35:11 +0000 +++ initramfs-tools-script 2020-07-04 08:59:37 +0000 @@ -177,5 +177,10 @@ rm -f /conf/conf.d/cryptroot.mandos fi elif [ -x /usr/bin/cryptroot-unlock ]; then - setsid /lib/mandos/mandos-to-cryptroot-unlock & + # Use setsid if available + if command -v setsid >/dev/null 2>&1; then + setsid /lib/mandos/mandos-to-cryptroot-unlock & + else + /lib/mandos/mandos-to-cryptroot-unlock & + fi fi === modified file 'intro.xml' --- intro.xml 2019-08-04 12:42:49 +0000 +++ intro.xml 2022-04-24 16:54:30 +0000 @@ -1,7 +1,7 @@ + %common; ]> @@ -39,6 +39,7 @@ 2017 2018 2019 + 2020 Teddy Hogeborn Björn Påhlsson @@ -403,11 +404,11 @@ As for systemd1 in particular, it has its own Password Agents system. Mandos uses this via its + url="https://systemd.io/PASSWORD_AGENTS/">Password + Agents system. Mandos uses this via its password-agent8mandos program, which - is run instead of 8mandos program, which is + run instead of plugin-runner8mandos when systemd1 === modified file 'mandos' --- mandos 2020-04-08 18:40:10 +0000 +++ mandos 2023-02-07 23:03:33 +0000 @@ -1,5 +1,5 @@ #!/usr/bin/python3 -bI -# -*- mode: python; after-save-hook: (lambda () (let ((command (if (fboundp 'file-local-name) (file-local-name (buffer-file-name)) (or (file-remote-p (buffer-file-name) 'localname) (buffer-file-name))))) (if (= (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (format "%s --check" (shell-quote-argument command)) nil "*Test*")) 0) (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w))) (progn (with-current-buffer "*Test*" (compilation-mode)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); coding: utf-8 -*- +# -*- coding: utf-8; lexical-binding: t -*- # # Mandos server - give out binary blobs to connecting clients. # @@ -11,8 +11,8 @@ # "AvahiService" class, and some lines in "main". # # Everything else is -# Copyright © 2008-2019 Teddy Hogeborn -# Copyright © 2008-2019 Björn Påhlsson +# Copyright © 2008-2022 Teddy Hogeborn +# Copyright © 2008-2022 Björn Påhlsson # # This file is part of Mandos. # @@ -31,7 +31,6 @@ # # Contact the authors at . # - from __future__ import (division, absolute_import, print_function, unicode_literals) @@ -40,26 +39,27 @@ except ImportError: pass +import sys +import unittest +import argparse +import logging +import os try: import SocketServer as socketserver except ImportError: import socketserver import socket -import argparse import datetime import errno try: import ConfigParser as configparser except ImportError: import configparser -import sys import re -import os import signal import subprocess import atexit import stat -import logging import logging.handlers import pwd import contextlib @@ -77,7 +77,6 @@ import itertools import collections import codecs -import unittest import random import shlex @@ -94,6 +93,7 @@ if sys.version_info.major == 2: __metaclass__ = type str = unicode + input = raw_input # Add collections.abc.Callable if it does not exist try: @@ -143,10 +143,10 @@ if sys.version_info < (3, 2): configparser.Configparser = configparser.SafeConfigParser -version = "1.8.11" +version = "1.8.15" stored_state_file = "clients.pickle" -logger = logging.getLogger() +log = logging.getLogger(os.path.basename(sys.argv[0])) logging.captureWarnings(True) # Show warnings via the logging system syslogger = None @@ -189,18 +189,18 @@ facility=logging.handlers.SysLogHandler.LOG_DAEMON, address="/dev/log")) syslogger.setFormatter(logging.Formatter - ('Mandos [%(process)d]: %(levelname)s:' - ' %(message)s')) - logger.addHandler(syslogger) + ("Mandos [%(process)d]: %(levelname)s:" + " %(message)s")) + log.addHandler(syslogger) if debug: console = logging.StreamHandler() - console.setFormatter(logging.Formatter('%(asctime)s %(name)s' - ' [%(process)d]:' - ' %(levelname)s:' - ' %(message)s')) - logger.addHandler(console) - logger.setLevel(level) + console.setFormatter(logging.Formatter("%(asctime)s %(name)s" + " [%(process)d]:" + " %(levelname)s:" + " %(message)s")) + log.addHandler(console) + log.setLevel(level) class PGPError(Exception): @@ -224,10 +224,10 @@ except OSError as e: if e.errno != errno.ENOENT: raise - self.gnupgargs = ['--batch', - '--homedir', self.tempdir, - '--force-mdc', - '--quiet'] + self.gnupgargs = ["--batch", + "--homedir", self.tempdir, + "--force-mdc", + "--quiet"] # Only GPG version 1 has the --no-use-agent option. if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"): self.gnupgargs.append("--no-use-agent") @@ -272,8 +272,8 @@ dir=self.tempdir) as passfile: passfile.write(passphrase) passfile.flush() - proc = subprocess.Popen([self.gpg, '--symmetric', - '--passphrase-file', + proc = subprocess.Popen([self.gpg, "--symmetric", + "--passphrase-file", passfile.name] + self.gnupgargs, stdin=subprocess.PIPE, @@ -290,8 +290,8 @@ dir=self.tempdir) as passfile: passfile.write(passphrase) passfile.flush() - proc = subprocess.Popen([self.gpg, '--decrypt', - '--passphrase-file', + proc = subprocess.Popen([self.gpg, "--decrypt", + "--passphrase-file", passfile.name] + self.gnupgargs, stdin=subprocess.PIPE, @@ -350,8 +350,8 @@ Attributes: interface: integer; avahi.IF_UNSPEC or an interface index. Used to optionally bind to the specified interface. - name: string; Example: 'Mandos' - type: string; Example: '_mandos._tcp'. + name: string; Example: "Mandos" + type: string; Example: "_mandos._tcp". See port: integer; what port to announce TXT: list of strings; TXT record for the service @@ -394,15 +394,15 @@ def rename(self, remove=True): """Derived from the Avahi example code""" if self.rename_count >= self.max_renames: - logger.critical("No suitable Zeroconf service name found" - " after %i retries, exiting.", - self.rename_count) + log.critical("No suitable Zeroconf service name found" + " after %i retries, exiting.", + self.rename_count) raise AvahiServiceError("Too many renames") self.name = str( self.server.GetAlternativeServiceName(self.name)) self.rename_count += 1 - logger.info("Changing Zeroconf service name to %r ...", - self.name) + log.info("Changing Zeroconf service name to %r ...", + self.name) if remove: self.remove() try: @@ -410,10 +410,10 @@ except dbus.exceptions.DBusException as error: if (error.get_dbus_name() == "org.freedesktop.Avahi.CollisionError"): - logger.info("Local Zeroconf service name collision.") + log.info("Local Zeroconf service name collision.") return self.rename(remove=False) else: - logger.critical("D-Bus Exception", exc_info=error) + log.critical("D-Bus Exception", exc_info=error) self.cleanup() os._exit(1) @@ -435,9 +435,9 @@ avahi.DBUS_INTERFACE_ENTRY_GROUP) self.entry_group_state_changed_match = ( self.group.connect_to_signal( - 'StateChanged', self.entry_group_state_changed)) - logger.debug("Adding Zeroconf service '%s' of type '%s' ...", - self.name, self.type) + "StateChanged", self.entry_group_state_changed)) + log.debug("Adding Zeroconf service '%s' of type '%s' ...", + self.name, self.type) self.group.AddService( self.interface, self.protocol, @@ -450,16 +450,16 @@ def entry_group_state_changed(self, state, error): """Derived from the Avahi example code""" - logger.debug("Avahi entry group state change: %i", state) + log.debug("Avahi entry group state change: %i", state) if state == avahi.ENTRY_GROUP_ESTABLISHED: - logger.debug("Zeroconf service established.") + log.debug("Zeroconf service established.") elif state == avahi.ENTRY_GROUP_COLLISION: - logger.info("Zeroconf service name collision.") + log.info("Zeroconf service name collision.") self.rename() elif state == avahi.ENTRY_GROUP_FAILURE: - logger.critical("Avahi: Error in group state changed %s", - str(error)) + log.critical("Avahi: Error in group state changed %s", + str(error)) raise AvahiGroupError("State changed: {!s}".format(error)) def cleanup(self): @@ -475,7 +475,7 @@ def server_state_changed(self, state, error=None): """Derived from the Avahi example code""" - logger.debug("Avahi server state change: %i", state) + log.debug("Avahi server state change: %i", state) bad_states = { avahi.SERVER_INVALID: "Zeroconf server invalid", avahi.SERVER_REGISTERING: None, @@ -485,9 +485,9 @@ if state in bad_states: if bad_states[state] is not None: if error is None: - logger.error(bad_states[state]) + log.error(bad_states[state]) else: - logger.error(bad_states[state] + ": %r", error) + log.error(bad_states[state] + ": %r", error) self.cleanup() elif state == avahi.SERVER_RUNNING: try: @@ -495,18 +495,17 @@ except dbus.exceptions.DBusException as error: if (error.get_dbus_name() == "org.freedesktop.Avahi.CollisionError"): - logger.info("Local Zeroconf service name" - " collision.") + log.info("Local Zeroconf service name collision.") return self.rename(remove=False) else: - logger.critical("D-Bus Exception", exc_info=error) + log.critical("D-Bus Exception", exc_info=error) self.cleanup() os._exit(1) else: if error is None: - logger.debug("Unknown state: %r", state) + log.debug("Unknown state: %r", state) else: - logger.debug("Unknown state: %r: %r", state, error) + log.debug("Unknown state: %r: %r", state, error) def activate(self): """Derived from the Avahi example code""" @@ -524,9 +523,10 @@ class AvahiServiceToSyslog(AvahiService): def rename(self, *args, **kwargs): """Add the new name to the syslog messages""" - ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs) + ret = super(AvahiServiceToSyslog, self).rename(*args, + **kwargs) syslogger.setFormatter(logging.Formatter( - 'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s' + "Mandos ({}) [%(process)d]: %(levelname)s: %(message)s" .format(self.name))) return ret @@ -562,9 +562,9 @@ OPENPGP_FMT_RAW = 0 # gnutls/openpgp.h # Types - class session_int(ctypes.Structure): + class _session_int(ctypes.Structure): _fields_ = [] - session_t = ctypes.POINTER(session_int) + session_t = ctypes.POINTER(_session_int) class certificate_credentials_st(ctypes.Structure): _fields_ = [] @@ -573,12 +573,12 @@ certificate_type_t = ctypes.c_int class datum_t(ctypes.Structure): - _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)), - ('size', ctypes.c_uint)] + _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)), + ("size", ctypes.c_uint)] - class openpgp_crt_int(ctypes.Structure): + class _openpgp_crt_int(ctypes.Structure): _fields_ = [] - openpgp_crt_t = ctypes.POINTER(openpgp_crt_int) + openpgp_crt_t = ctypes.POINTER(_openpgp_crt_int) openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) credentials_type_t = ctypes.c_int @@ -593,77 +593,100 @@ # gnutls.strerror() self.code = code if message is None and code is not None: - message = gnutls.strerror(code) + message = gnutls.strerror(code).decode( + "utf-8", errors="replace") return super(gnutls.Error, self).__init__( message, *args) class CertificateSecurityError(Error): pass + class PointerTo: + def __init__(self, cls): + self.cls = cls + + def from_param(self, obj): + if not isinstance(obj, self.cls): + raise TypeError("Not of type {}: {!r}" + .format(self.cls.__name__, obj)) + return ctypes.byref(obj.from_param(obj)) + + class CastToVoidPointer: + def __init__(self, cls): + self.cls = cls + + def from_param(self, obj): + if not isinstance(obj, self.cls): + raise TypeError("Not of type {}: {!r}" + .format(self.cls.__name__, obj)) + return ctypes.cast(obj.from_param(obj), ctypes.c_void_p) + + class With_from_param: + @classmethod + def from_param(cls, obj): + return obj._as_parameter_ + # Classes - class Credentials: + class Credentials(With_from_param): def __init__(self): - self._c_object = gnutls.certificate_credentials_t() - gnutls.certificate_allocate_credentials( - ctypes.byref(self._c_object)) + self._as_parameter_ = gnutls.certificate_credentials_t() + gnutls.certificate_allocate_credentials(self) self.type = gnutls.CRD_CERTIFICATE def __del__(self): - gnutls.certificate_free_credentials(self._c_object) + gnutls.certificate_free_credentials(self) - class ClientSession: + class ClientSession(With_from_param): def __init__(self, socket, credentials=None): - self._c_object = gnutls.session_t() + self._as_parameter_ = gnutls.session_t() gnutls_flags = gnutls.CLIENT if gnutls.check_version(b"3.5.6"): gnutls_flags |= gnutls.NO_TICKETS if gnutls.has_rawpk: gnutls_flags |= gnutls.ENABLE_RAWPK - gnutls.init(ctypes.byref(self._c_object), gnutls_flags) + gnutls.init(self, gnutls_flags) del gnutls_flags - gnutls.set_default_priority(self._c_object) - gnutls.transport_set_ptr(self._c_object, socket.fileno()) - gnutls.handshake_set_private_extensions(self._c_object, - True) + gnutls.set_default_priority(self) + gnutls.transport_set_ptr(self, socket.fileno()) + gnutls.handshake_set_private_extensions(self, True) self.socket = socket if credentials is None: credentials = gnutls.Credentials() - gnutls.credentials_set(self._c_object, credentials.type, - ctypes.cast(credentials._c_object, - ctypes.c_void_p)) + gnutls.credentials_set(self, credentials.type, + credentials) self.credentials = credentials def __del__(self): - gnutls.deinit(self._c_object) + gnutls.deinit(self) def handshake(self): - return gnutls.handshake(self._c_object) + return gnutls.handshake(self) def send(self, data): data = bytes(data) data_len = len(data) while data_len > 0: - data_len -= gnutls.record_send(self._c_object, - data[-data_len:], + data_len -= gnutls.record_send(self, data[-data_len:], data_len) def bye(self): - return gnutls.bye(self._c_object, gnutls.SHUT_RDWR) + return gnutls.bye(self, gnutls.SHUT_RDWR) # Error handling functions def _error_code(result): """A function to raise exceptions on errors, suitable - for the 'restype' attribute on ctypes functions""" - if result >= 0: + for the "restype" attribute on ctypes functions""" + if result >= gnutls.E_SUCCESS: return result if result == gnutls.E_NO_CERTIFICATE_FOUND: raise gnutls.CertificateSecurityError(code=result) raise gnutls.Error(code=result) - def _retry_on_error(result, func, arguments): + def _retry_on_error(result, func, arguments, + _error_code=_error_code): """A function to retry on some errors, suitable - for the 'errcheck' attribute on ctypes functions""" - while result < 0: + for the "errcheck" attribute on ctypes functions""" + while result < gnutls.E_SUCCESS: if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN): return _error_code(result) result = func(*arguments) @@ -674,20 +697,20 @@ # Functions priority_set_direct = _library.gnutls_priority_set_direct - priority_set_direct.argtypes = [session_t, ctypes.c_char_p, + priority_set_direct.argtypes = [ClientSession, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p)] priority_set_direct.restype = _error_code init = _library.gnutls_init - init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int] + init.argtypes = [PointerTo(ClientSession), ctypes.c_int] init.restype = _error_code set_default_priority = _library.gnutls_set_default_priority - set_default_priority.argtypes = [session_t] + set_default_priority.argtypes = [ClientSession] set_default_priority.restype = _error_code record_send = _library.gnutls_record_send - record_send.argtypes = [session_t, ctypes.c_void_p, + record_send.argtypes = [ClientSession, ctypes.c_void_p, ctypes.c_size_t] record_send.restype = ctypes.c_ssize_t record_send.errcheck = _retry_on_error @@ -695,24 +718,23 @@ certificate_allocate_credentials = ( _library.gnutls_certificate_allocate_credentials) certificate_allocate_credentials.argtypes = [ - ctypes.POINTER(certificate_credentials_t)] + PointerTo(Credentials)] certificate_allocate_credentials.restype = _error_code certificate_free_credentials = ( _library.gnutls_certificate_free_credentials) - certificate_free_credentials.argtypes = [ - certificate_credentials_t] + certificate_free_credentials.argtypes = [Credentials] certificate_free_credentials.restype = None handshake_set_private_extensions = ( _library.gnutls_handshake_set_private_extensions) - handshake_set_private_extensions.argtypes = [session_t, + handshake_set_private_extensions.argtypes = [ClientSession, ctypes.c_int] handshake_set_private_extensions.restype = None credentials_set = _library.gnutls_credentials_set - credentials_set.argtypes = [session_t, credentials_type_t, - ctypes.c_void_p] + credentials_set.argtypes = [ClientSession, credentials_type_t, + CastToVoidPointer(Credentials)] credentials_set.restype = _error_code strerror = _library.gnutls_strerror @@ -720,11 +742,11 @@ strerror.restype = ctypes.c_char_p certificate_type_get = _library.gnutls_certificate_type_get - certificate_type_get.argtypes = [session_t] + certificate_type_get.argtypes = [ClientSession] certificate_type_get.restype = _error_code certificate_get_peers = _library.gnutls_certificate_get_peers - certificate_get_peers.argtypes = [session_t, + certificate_get_peers.argtypes = [ClientSession, ctypes.POINTER(ctypes.c_uint)] certificate_get_peers.restype = ctypes.POINTER(datum_t) @@ -737,21 +759,21 @@ global_set_log_function.restype = None deinit = _library.gnutls_deinit - deinit.argtypes = [session_t] + deinit.argtypes = [ClientSession] deinit.restype = None handshake = _library.gnutls_handshake - handshake.argtypes = [session_t] - handshake.restype = _error_code + handshake.argtypes = [ClientSession] + handshake.restype = ctypes.c_int handshake.errcheck = _retry_on_error transport_set_ptr = _library.gnutls_transport_set_ptr - transport_set_ptr.argtypes = [session_t, transport_ptr_t] + transport_set_ptr.argtypes = [ClientSession, transport_ptr_t] transport_set_ptr.restype = None bye = _library.gnutls_bye - bye.argtypes = [session_t, close_request_t] - bye.restype = _error_code + bye.argtypes = [ClientSession, close_request_t] + bye.restype = ctypes.c_int bye.errcheck = _retry_on_error check_version = _library.gnutls_check_version @@ -774,7 +796,8 @@ x509_crt_fmt_t = ctypes.c_int - # All the function declarations below are from gnutls/abstract.h + # All the function declarations below are from + # gnutls/abstract.h pubkey_init = _library.gnutls_pubkey_init pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)] pubkey_init.restype = _error_code @@ -794,7 +817,8 @@ pubkey_deinit.argtypes = [pubkey_t] pubkey_deinit.restype = None else: - # All the function declarations below are from gnutls/openpgp.h + # All the function declarations below are from + # gnutls/openpgp.h openpgp_crt_init = _library.gnutls_openpgp_crt_init openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)] @@ -806,9 +830,13 @@ openpgp_crt_fmt_t] openpgp_crt_import.restype = _error_code - openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self - openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint, - ctypes.POINTER(ctypes.c_uint)] + openpgp_crt_verify_self = \ + _library.gnutls_openpgp_crt_verify_self + openpgp_crt_verify_self.argtypes = [ + openpgp_crt_t, + ctypes.c_uint, + ctypes.POINTER(ctypes.c_uint), + ] openpgp_crt_verify_self.restype = _error_code openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit @@ -825,7 +853,7 @@ if check_version(b"3.6.4"): certificate_type_get2 = _library.gnutls_certificate_type_get2 - certificate_type_get2.argtypes = [session_t, ctypes.c_int] + certificate_type_get2.argtypes = [ClientSession, ctypes.c_int] certificate_type_get2.restype = _error_code # Remove non-public functions @@ -847,11 +875,11 @@ """A representation of a client host served by this server. Attributes: - approved: bool(); 'None' if not yet approved/disapproved + approved: bool(); None if not yet approved/disapproved approval_delay: datetime.timedelta(); Time to wait for approval approval_duration: datetime.timedelta(); Duration of one approval checker: multiprocessing.Process(); a running checker process used - to see if the client lives. 'None' if no process is + to see if the client lives. None if no process is running. checker_callback_tag: a GLib event source tag, or None checker_command: string; External command which is run to check @@ -981,9 +1009,9 @@ self.last_enabled = None self.expires = None - logger.debug("Creating client %r", self.name) - logger.debug(" Key ID: %s", self.key_id) - logger.debug(" Fingerprint: %s", self.fingerprint) + log.debug("Creating client %r", self.name) + log.debug(" Key ID: %s", self.key_id) + log.debug(" Fingerprint: %s", self.fingerprint) self.created = settings.get("created", datetime.datetime.utcnow()) @@ -1017,7 +1045,6 @@ if getattr(self, "enabled", False): # Already enabled return - self.expires = datetime.datetime.utcnow() + self.timeout self.enabled = True self.last_enabled = datetime.datetime.utcnow() self.init_checker() @@ -1028,7 +1055,7 @@ if not getattr(self, "enabled", False): return False if not quiet: - logger.info("Disabling client %s", self.name) + log.info("Disabling client %s", self.name) if getattr(self, "disable_initiator_tag", None) is not None: GLib.source_remove(self.disable_initiator_tag) self.disable_initiator_tag = None @@ -1046,22 +1073,35 @@ def __del__(self): self.disable() - def init_checker(self): - # Schedule a new checker to be started an 'interval' from now, - # and every interval from then on. + def init_checker(self, randomize_start=False): + # Schedule a new checker to be started a randomly selected + # time (a fraction of 'interval') from now. This spreads out + # the startup of checkers over time when the server is + # started. if self.checker_initiator_tag is not None: GLib.source_remove(self.checker_initiator_tag) + interval_milliseconds = int(self.interval.total_seconds() + * 1000) + if randomize_start: + delay_milliseconds = random.randrange( + interval_milliseconds + 1) + else: + delay_milliseconds = interval_milliseconds self.checker_initiator_tag = GLib.timeout_add( - random.randrange(int(self.interval.total_seconds() * 1000 - + 1)), - self.start_checker) - # Schedule a disable() when 'timeout' has passed + delay_milliseconds, self.start_checker, randomize_start) + delay = datetime.timedelta(0, 0, 0, delay_milliseconds) + # A checker might take up to an 'interval' of time, so we can + # expire at the soonest one interval after a checker was + # started. Since the initial checker is delayed, the expire + # time might have to be extended. + now = datetime.datetime.utcnow() + self.expires = now + delay + self.interval + # Schedule a disable() at expire time if self.disable_initiator_tag is not None: GLib.source_remove(self.disable_initiator_tag) self.disable_initiator_tag = GLib.timeout_add( - int(self.timeout.total_seconds() * 1000), self.disable) - # Also start a new checker *right now*. - self.start_checker() + int((self.expires - now).total_seconds() * 1000), + self.disable) def checker_callback(self, source, condition, connection, command): @@ -1078,16 +1118,14 @@ self.last_checker_status = returncode self.last_checker_signal = None if self.last_checker_status == 0: - logger.info("Checker for %(name)s succeeded", - vars(self)) + log.info("Checker for %(name)s succeeded", vars(self)) self.checked_ok() else: - logger.info("Checker for %(name)s failed", vars(self)) + log.info("Checker for %(name)s failed", vars(self)) else: self.last_checker_status = -1 self.last_checker_signal = -returncode - logger.warning("Checker for %(name)s crashed?", - vars(self)) + log.warning("Checker for %(name)s crashed?", vars(self)) return False def checked_ok(self): @@ -1112,7 +1150,7 @@ def need_approval(self): self.last_approval_request = datetime.datetime.utcnow() - def start_checker(self): + def start_checker(self, start_was_randomized=False): """Start a new checker subprocess if one is not running. If a checker already exists, leave it running and do @@ -1127,7 +1165,7 @@ # should be. if self.checker is not None and not self.checker.is_alive(): - logger.warning("Checker was not alive; joining") + log.warning("Checker was not alive; joining") self.checker.join() self.checker = None # Start a new checker if needed @@ -1139,13 +1177,11 @@ try: command = self.checker_command % escaped_attrs except TypeError as error: - logger.error('Could not format string "%s"', - self.checker_command, - exc_info=error) + log.error('Could not format string "%s"', + self.checker_command, exc_info=error) return True # Try again later self.current_checker_command = command - logger.info("Starting checker %r for %s", command, - self.name) + log.info("Starting checker %r for %s", command, self.name) # We don't need to redirect stdout and stderr, since # in normal mode, that is already done by daemon(), # and in debug mode we don't want to. (Stdin is @@ -1170,6 +1206,17 @@ GLib.IOChannel.unix_new(pipe[0].fileno()), GLib.PRIORITY_DEFAULT, GLib.IO_IN, self.checker_callback, pipe[0], command) + if start_was_randomized: + # We were started after a random delay; Schedule a new + # checker to be started an 'interval' from now, and every + # interval from then on. + now = datetime.datetime.utcnow() + self.checker_initiator_tag = GLib.timeout_add( + int(self.interval.total_seconds() * 1000), + self.start_checker) + self.expires = max(self.expires, now + self.interval) + # Don't start a new checker again after same random delay + return False # Re-run this periodically if run by GLib.timeout_add return True @@ -1180,7 +1227,7 @@ self.checker_callback_tag = None if getattr(self, "checker", None) is None: return - logger.debug("Stopping checker for %(name)s", vars(self)) + log.debug("Stopping checker for %(name)s", vars(self)) self.checker.terminate() self.checker = None @@ -1213,7 +1260,7 @@ func._dbus_name = func.__name__ if func._dbus_name.endswith("_dbus_property"): func._dbus_name = func._dbus_name[:-14] - func._dbus_get_args_options = {'byte_arrays': byte_arrays} + func._dbus_get_args_options = {"byte_arrays": byte_arrays} return func return decorator @@ -1308,8 +1355,8 @@ @dbus.service.method(dbus.INTROSPECTABLE_IFACE, out_signature="s", - path_keyword='object_path', - connection_keyword='connection') + path_keyword="object_path", + connection_keyword="connection") def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. @@ -1364,8 +1411,8 @@ document.unlink() except (AttributeError, xml.dom.DOMException, xml.parsers.expat.ExpatError) as error: - logger.error("Failed to override Introspection method", - exc_info=error) + log.error("Failed to override Introspection method", + exc_info=error) return xmlstring @@ -1468,8 +1515,8 @@ @dbus.service.method(dbus.INTROSPECTABLE_IFACE, out_signature="s", - path_keyword='object_path', - connection_keyword='connection') + path_keyword="object_path", + connection_keyword="connection") def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. @@ -1531,8 +1578,8 @@ document.unlink() except (AttributeError, xml.dom.DOMException, xml.parsers.expat.ExpatError) as error: - logger.error("Failed to override Introspection method", - exc_info=error) + log.error("Failed to override Introspection method", + exc_info=error) return xmlstring @@ -1570,8 +1617,8 @@ @dbus.service.method(dbus.INTROSPECTABLE_IFACE, out_signature="s", - path_keyword='object_path', - connection_keyword='connection') + path_keyword="object_path", + connection_keyword="connection") def Introspect(self, object_path, connection): """Overloading of standard D-Bus method. @@ -1602,8 +1649,8 @@ document.unlink() except (AttributeError, xml.dom.DOMException, xml.parsers.expat.ExpatError) as error: - logger.error("Failed to override Introspection method", - exc_info=error) + log.error("Failed to override Introspection method", + exc_info=error) return xmlstring @@ -2243,29 +2290,29 @@ class ProxyClient: def __init__(self, child_pipe, key_id, fpr, address): self._pipe = child_pipe - self._pipe.send(('init', key_id, fpr, address)) + self._pipe.send(("init", key_id, fpr, address)) if not self._pipe.recv(): raise KeyError(key_id or fpr) def __getattribute__(self, name): - if name == '_pipe': + if name == "_pipe": return super(ProxyClient, self).__getattribute__(name) - self._pipe.send(('getattr', name)) + self._pipe.send(("getattr", name)) data = self._pipe.recv() - if data[0] == 'data': + if data[0] == "data": return data[1] - if data[0] == 'function': + if data[0] == "function": def func(*args, **kwargs): - self._pipe.send(('funcall', name, args, kwargs)) + self._pipe.send(("funcall", name, args, kwargs)) return self._pipe.recv()[1] return func def __setattr__(self, name, value): - if name == '_pipe': + if name == "_pipe": return super(ProxyClient, self).__setattr__(name, value) - self._pipe.send(('setattr', name, value)) + self._pipe.send(("setattr", name, value)) class ClientHandler(socketserver.BaseRequestHandler, object): @@ -2276,14 +2323,13 @@ def handle(self): with contextlib.closing(self.server.child_pipe) as child_pipe: - logger.info("TCP connection from: %s", - str(self.client_address)) - logger.debug("Pipe FD: %d", - self.server.child_pipe.fileno()) + log.info("TCP connection from: %s", + str(self.client_address)) + log.debug("Pipe FD: %d", self.server.child_pipe.fileno()) session = gnutls.ClientSession(self.request) - # priority = ':'.join(("NONE", "+VERS-TLS1.1", + # priority = ":".join(("NONE", "+VERS-TLS1.1", # "+AES-256-CBC", "+SHA1", # "+COMP-NULL", "+CTYPE-OPENPGP", # "+DHE-DSS")) @@ -2291,30 +2337,29 @@ priority = self.server.gnutls_priority if priority is None: priority = "NORMAL" - gnutls.priority_set_direct(session._c_object, - priority.encode("utf-8"), - None) + gnutls.priority_set_direct(session, + priority.encode("utf-8"), None) # Start communication using the Mandos protocol # Get protocol number line = self.request.makefile().readline() - logger.debug("Protocol version: %r", line) + log.debug("Protocol version: %r", line) try: if int(line.strip().split()[0]) > 1: raise RuntimeError(line) except (ValueError, IndexError, RuntimeError) as error: - logger.error("Unknown protocol version: %s", error) + log.error("Unknown protocol version: %s", error) return # Start GnuTLS connection try: session.handshake() except gnutls.Error as error: - logger.warning("Handshake failed: %s", error) + log.warning("Handshake failed: %s", error) # Do not run session.bye() here: the session is not # established. Just abandon the request. return - logger.debug("Handshake succeeded") + log.debug("Handshake succeeded") approval_required = False try: @@ -2324,9 +2369,11 @@ key_id = self.key_id( self.peer_certificate(session)) except (TypeError, gnutls.Error) as error: - logger.warning("Bad certificate: %s", error) + log.warning("Bad certificate: %s", error) return - logger.debug("Key ID: %s", key_id) + log.debug("Key ID: %s", + key_id.decode("utf-8", + errors="replace")) else: key_id = b"" @@ -2334,9 +2381,9 @@ fpr = self.fingerprint( self.peer_certificate(session)) except (TypeError, gnutls.Error) as error: - logger.warning("Bad certificate: %s", error) + log.warning("Bad certificate: %s", error) return - logger.debug("Fingerprint: %s", fpr) + log.debug("Fingerprint: %s", fpr) try: client = ProxyClient(child_pipe, key_id, fpr, @@ -2351,8 +2398,7 @@ while True: if not client.enabled: - logger.info("Client %s is disabled", - client.name) + log.info("Client %s is disabled", client.name) if self.server.use_dbus: # Emit D-Bus signal client.Rejected("Disabled") @@ -2362,16 +2408,16 @@ # We are approved or approval is disabled break elif client.approved is None: - logger.info("Client %s needs approval", - client.name) + log.info("Client %s needs approval", + client.name) if self.server.use_dbus: # Emit D-Bus signal client.NeedApproval( client.approval_delay.total_seconds() * 1000, client.approved_by_default) else: - logger.warning("Client %s was not approved", - client.name) + log.warning("Client %s was not approved", + client.name) if self.server.use_dbus: # Emit D-Bus signal client.Rejected("Denied") @@ -2385,9 +2431,9 @@ time2 = datetime.datetime.now() if (time2 - time) >= delay: if not client.approved_by_default: - logger.warning("Client %s timed out while" - " waiting for approval", - client.name) + log.warning("Client %s timed out while" + " waiting for approval", + client.name) if self.server.use_dbus: # Emit D-Bus signal client.Rejected("Approval timed out") @@ -2400,11 +2446,10 @@ try: session.send(client.secret) except gnutls.Error as error: - logger.warning("gnutls send failed", - exc_info=error) + log.warning("gnutls send failed", exc_info=error) return - logger.info("Sending secret to %s", client.name) + log.info("Sending secret to %s", client.name) # bump the timeout using extended_timeout client.bump_timeout(client.extended_timeout) if self.server.use_dbus: @@ -2417,30 +2462,29 @@ try: session.bye() except gnutls.Error as error: - logger.warning("GnuTLS bye failed", - exc_info=error) + log.warning("GnuTLS bye failed", exc_info=error) @staticmethod def peer_certificate(session): "Return the peer's certificate as a bytestring" try: - cert_type = gnutls.certificate_type_get2(session._c_object, - gnutls.CTYPE_PEERS) + cert_type = gnutls.certificate_type_get2( + session, gnutls.CTYPE_PEERS) except AttributeError: - cert_type = gnutls.certificate_type_get(session._c_object) + cert_type = gnutls.certificate_type_get(session) if gnutls.has_rawpk: valid_cert_types = frozenset((gnutls.CRT_RAWPK,)) else: valid_cert_types = frozenset((gnutls.CRT_OPENPGP,)) # If not a valid certificate type... if cert_type not in valid_cert_types: - logger.info("Cert type %r not in %r", cert_type, - valid_cert_types) + log.info("Cert type %r not in %r", cert_type, + valid_cert_types) # ...return invalid data return b"" list_size = ctypes.c_uint(1) cert_list = (gnutls.certificate_get_peers - (session._c_object, ctypes.byref(list_size))) + (session, ctypes.byref(list_size))) if not bool(cert_list) and list_size.value != 0: raise gnutls.Error("error getting peer certificate") if list_size.value == 0: @@ -2468,11 +2512,12 @@ buf = ctypes.create_string_buffer(32) buf_len = ctypes.c_size_t(len(buf)) # Get the key ID from the raw public key into the buffer - gnutls.pubkey_get_key_id(pubkey, - gnutls.KEYID_USE_SHA256, - ctypes.cast(ctypes.byref(buf), - ctypes.POINTER(ctypes.c_ubyte)), - ctypes.byref(buf_len)) + gnutls.pubkey_get_key_id( + pubkey, + gnutls.KEYID_USE_SHA256, + ctypes.cast(ctypes.byref(buf), + ctypes.POINTER(ctypes.c_ubyte)), + ctypes.byref(buf_len)) # Deinit the certificate gnutls.pubkey_deinit(pubkey) @@ -2559,7 +2604,7 @@ class IPv6_TCPServer(MultiprocessingMixInWithPipe, socketserver.TCPServer): - """IPv6-capable TCP server. Accepts 'None' as address and/or port + """IPv6-capable TCP server. Accepts None as address and/or port Attributes: enabled: Boolean; whether this server is activated yet @@ -2616,7 +2661,7 @@ if SO_BINDTODEVICE is None: # Fall back to a hard-coded value which seems to be # common enough. - logger.warning("SO_BINDTODEVICE not found, trying 25") + log.warning("SO_BINDTODEVICE not found, trying 25") SO_BINDTODEVICE = 25 try: self.socket.setsockopt( @@ -2624,15 +2669,14 @@ (self.interface + "\0").encode("utf-8")) except socket.error as error: if error.errno == errno.EPERM: - logger.error("No permission to bind to" - " interface %s", self.interface) + log.error("No permission to bind to interface %s", + self.interface) elif error.errno == errno.ENOPROTOOPT: - logger.error("SO_BINDTODEVICE not available;" - " cannot bind to interface %s", - self.interface) + log.error("SO_BINDTODEVICE not available; cannot" + " bind to interface %s", self.interface) elif error.errno == errno.ENODEV: - logger.error("Interface %s does not exist," - " cannot bind", self.interface) + log.error("Interface %s does not exist, cannot" + " bind", self.interface) else: raise # Only bind(2) the socket if we really need to. @@ -2717,13 +2761,14 @@ request = parent_pipe.recv() command = request[0] - if command == 'init': + if command == "init": key_id = request[1].decode("ascii") fpr = request[2].decode("ascii") address = request[3] for c in self.clients.values(): - if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855": + if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924" + "27AE41E4649B934CA495991B7852B855"): continue if key_id and c.key_id == key_id: client = c @@ -2732,8 +2777,8 @@ client = c break else: - logger.info("Client not found for key ID: %s, address" - ": %s", key_id or fpr, address) + log.info("Client not found for key ID: %s, address:" + " %s", key_id or fpr, address) if self.use_dbus: # Emit D-Bus signal mandos_dbus_service.ClientNotFound(key_id or fpr, @@ -2752,25 +2797,25 @@ # remove the old hook in favor of the new above hook on # same fileno return False - if command == 'funcall': + if command == "funcall": funcname = request[1] args = request[2] kwargs = request[3] - parent_pipe.send(('data', getattr(client_object, + parent_pipe.send(("data", getattr(client_object, funcname)(*args, **kwargs))) - if command == 'getattr': + if command == "getattr": attrname = request[1] if isinstance(client_object.__getattribute__(attrname), collections.abc.Callable): - parent_pipe.send(('function', )) + parent_pipe.send(("function", )) else: parent_pipe.send(( - 'data', client_object.__getattribute__(attrname))) + "data", client_object.__getattribute__(attrname))) - if command == 'setattr': + if command == "setattr": attrname = request[1] value = request[2] setattr(client_object, attrname, value) @@ -2781,20 +2826,22 @@ def rfc3339_duration_to_delta(duration): """Parse an RFC 3339 "duration" and return a datetime.timedelta - >>> rfc3339_duration_to_delta("P7D") == datetime.timedelta(7) - True - >>> rfc3339_duration_to_delta("PT60S") == datetime.timedelta(0, 60) - True - >>> rfc3339_duration_to_delta("PT60M") == datetime.timedelta(0, 3600) - True - >>> rfc3339_duration_to_delta("PT24H") == datetime.timedelta(1) - True - >>> rfc3339_duration_to_delta("P1W") == datetime.timedelta(7) - True - >>> rfc3339_duration_to_delta("PT5M30S") == datetime.timedelta(0, 330) - True - >>> rfc3339_duration_to_delta("P1DT3M20S") == datetime.timedelta(1, 200) - True + >>> timedelta = datetime.timedelta + >>> rfc3339_duration_to_delta("P7D") == timedelta(7) + True + >>> rfc3339_duration_to_delta("PT60S") == timedelta(0, 60) + True + >>> rfc3339_duration_to_delta("PT60M") == timedelta(0, 3600) + True + >>> rfc3339_duration_to_delta("PT24H") == timedelta(1) + True + >>> rfc3339_duration_to_delta("P1W") == timedelta(7) + True + >>> rfc3339_duration_to_delta("PT5M30S") == timedelta(0, 330) + True + >>> rfc3339_duration_to_delta("P1DT3M20S") == timedelta(1, 200) + True + >>> del timedelta """ # Parsing an RFC 3339 duration with regular expressions is not @@ -2880,17 +2927,17 @@ def string_to_delta(interval): """Parse a string and return a datetime.timedelta - >>> string_to_delta('7d') == datetime.timedelta(7) - True - >>> string_to_delta('60s') == datetime.timedelta(0, 60) - True - >>> string_to_delta('60m') == datetime.timedelta(0, 3600) - True - >>> string_to_delta('24h') == datetime.timedelta(1) - True - >>> string_to_delta('1w') == datetime.timedelta(7) - True - >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330) + >>> string_to_delta("7d") == datetime.timedelta(7) + True + >>> string_to_delta("60s") == datetime.timedelta(0, 60) + True + >>> string_to_delta("60m") == datetime.timedelta(0, 3600) + True + >>> string_to_delta("24h") == datetime.timedelta(1) + True + >>> string_to_delta("1w") == datetime.timedelta(7) + True + >>> string_to_delta("5m 30s") == datetime.timedelta(0, 330) True """ @@ -3100,8 +3147,8 @@ if server_settings["servicename"] != "Mandos": syslogger.setFormatter( - logging.Formatter('Mandos ({}) [%(process)d]:' - ' %(levelname)s: %(message)s'.format( + logging.Formatter("Mandos ({}) [%(process)d]:" + " %(levelname)s: %(message)s".format( server_settings["servicename"]))) # Parse config file with clients @@ -3131,8 +3178,8 @@ try: pidfile = codecs.open(pidfilename, "w", encoding="utf-8") except IOError as e: - logger.error("Could not open file %r", pidfilename, - exc_info=e) + log.error("Could not open file %r", pidfilename, + exc_info=e) for name, group in (("_mandos", "_mandos"), ("mandos", "mandos"), @@ -3149,12 +3196,10 @@ try: os.setgid(gid) os.setuid(uid) - if debug: - logger.debug("Did setuid/setgid to {}:{}".format(uid, - gid)) + log.debug("Did setuid/setgid to %s:%s", uid, gid) except OSError as error: - logger.warning("Failed to setuid/setgid to {}:{}: {}" - .format(uid, gid, os.strerror(error.errno))) + log.warning("Failed to setuid/setgid to %s:%s: %s", uid, gid, + os.strerror(error.errno)) if error.errno != errno.EPERM: raise @@ -3167,7 +3212,8 @@ @gnutls.log_func def debug_gnutls(level, string): - logger.debug("GnuTLS: %s", string[:-1]) + log.debug("GnuTLS: %s", + string[:-1].decode("utf-8", errors="replace")) gnutls.global_set_log_function(debug_gnutls) @@ -3202,7 +3248,7 @@ "se.bsnet.fukt.Mandos", bus, do_not_queue=True) except dbus.exceptions.DBusException as e: - logger.error("Disabling D-Bus:", exc_info=e) + log.error("Disabling D-Bus:", exc_info=e) use_dbus = False server_settings["use_dbus"] = False tcp_server.use_dbus = False @@ -3293,16 +3339,15 @@ os.remove(stored_state_path) except IOError as e: if e.errno == errno.ENOENT: - logger.warning("Could not load persistent state:" - " {}".format(os.strerror(e.errno))) + log.warning("Could not load persistent state:" + " %s", os.strerror(e.errno)) else: - logger.critical("Could not load persistent state:", - exc_info=e) + log.critical("Could not load persistent state:", + exc_info=e) raise except EOFError as e: - logger.warning("Could not load persistent state: " - "EOFError:", - exc_info=e) + log.warning("Could not load persistent state: EOFError:", + exc_info=e) with PGPEngine() as pgp: for client_name, client in clients_data.items(): @@ -3335,34 +3380,30 @@ if client["enabled"]: if datetime.datetime.utcnow() >= client["expires"]: if not client["last_checked_ok"]: - logger.warning( - "disabling client {} - Client never " - "performed a successful checker".format( - client_name)) + log.warning("disabling client %s - Client" + " never performed a successful" + " checker", client_name) client["enabled"] = False elif client["last_checker_status"] != 0: - logger.warning( - "disabling client {} - Client last" - " checker failed with error code" - " {}".format( - client_name, - client["last_checker_status"])) + log.warning("disabling client %s - Client" + " last checker failed with error" + " code %s", client_name, + client["last_checker_status"]) client["enabled"] = False else: client["expires"] = ( datetime.datetime.utcnow() + client["timeout"]) - logger.debug("Last checker succeeded," - " keeping {} enabled".format( - client_name)) + log.debug("Last checker succeeded, keeping %s" + " enabled", client_name) try: client["secret"] = pgp.decrypt( client["encrypted_secret"], client_settings[client_name]["secret"]) except PGPError: # If decryption fails, we use secret from new settings - logger.debug("Failed to decrypt {} old secret".format( - client_name)) + log.debug("Failed to decrypt %s old secret", + client_name) client["secret"] = (client_settings[client_name] ["secret"]) @@ -3382,7 +3423,7 @@ server_settings=server_settings) if not tcp_server.clients: - logger.warning("No clients defined") + log.warning("No clients defined") if not foreground: if pidfile is not None: @@ -3391,8 +3432,8 @@ with pidfile: print(pid, file=pidfile) except IOError: - logger.error("Could not write to file %r with PID %d", - pidfilename, pid) + log.error("Could not write to file %r with PID %d", + pidfilename, pid) del pidfile del pidfilename @@ -3548,9 +3589,9 @@ try: with tempfile.NamedTemporaryFile( - mode='wb', + mode="wb", suffix=".pickle", - prefix='clients-', + prefix="clients-", dir=os.path.dirname(stored_state_path), delete=False) as stored_state: pickle.dump((clients, client_settings), stored_state, @@ -3564,11 +3605,11 @@ except NameError: pass if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST): - logger.warning("Could not save persistent state: {}" - .format(os.strerror(e.errno))) + log.warning("Could not save persistent state: %s", + os.strerror(e.errno)) else: - logger.warning("Could not save persistent state:", - exc_info=e) + log.warning("Could not save persistent state:", + exc_info=e) raise # Delete all clients, and settings from config @@ -3591,7 +3632,7 @@ mandos_dbus_service.client_added_signal(client) # Need to initiate checking of clients if client.enabled: - client.init_checker() + client.init_checker(randomize_start=True) tcp_server.enable() tcp_server.server_activate() @@ -3600,12 +3641,11 @@ if zeroconf: service.port = tcp_server.socket.getsockname()[1] if use_ipv6: - logger.info("Now listening on address %r, port %d," - " flowinfo %d, scope_id %d", - *tcp_server.socket.getsockname()) + log.info("Now listening on address %r, port %d, flowinfo %d," + " scope_id %d", *tcp_server.socket.getsockname()) else: # IPv4 - logger.info("Now listening on address %r, port %d", - *tcp_server.socket.getsockname()) + log.info("Now listening on address %r, port %d", + *tcp_server.socket.getsockname()) # service.interface = tcp_server.socket.getsockname()[3] @@ -3615,7 +3655,7 @@ try: service.activate() except dbus.exceptions.DBusException as error: - logger.critical("D-Bus Exception", exc_info=error) + log.critical("D-Bus Exception", exc_info=error) cleanup() sys.exit(1) # End of Avahi example code @@ -3626,30 +3666,31 @@ lambda *args, **kwargs: (tcp_server.handle_request (*args[2:], **kwargs) or True)) - logger.debug("Starting main loop") + log.debug("Starting main loop") main_loop.run() except AvahiError as error: - logger.critical("Avahi Error", exc_info=error) + log.critical("Avahi Error", exc_info=error) cleanup() sys.exit(1) except KeyboardInterrupt: if debug: print("", file=sys.stderr) - logger.debug("Server received KeyboardInterrupt") - logger.debug("Server exiting") + log.debug("Server received KeyboardInterrupt") + log.debug("Server exiting") # Must run before the D-Bus bus name gets deregistered cleanup() -def should_only_run_tests(): +def parse_test_args(): + # type: () -> argparse.Namespace parser = argparse.ArgumentParser(add_help=False) - parser.add_argument("--check", action='store_true') + parser.add_argument("--check", action="store_true") + parser.add_argument("--prefix", ) args, unknown_args = parser.parse_known_args() - run_tests = args.check - if run_tests: - # Remove --check argument from sys.argv + if args.check: + # Remove test options from sys.argv sys.argv[1:] = unknown_args - return run_tests + return args # Add all tests from doctest strings def load_tests(loader, tests, none): @@ -3657,12 +3698,82 @@ tests.addTests(doctest.DocTestSuite()) return tests -if __name__ == '__main__': +if __name__ == "__main__": + options = parse_test_args() try: - if should_only_run_tests(): - # Call using ./mandos --check [--verbose] - unittest.main() + if options.check: + extra_test_prefix = options.prefix + if extra_test_prefix is not None: + if not (unittest.main(argv=[""], exit=False) + .result.wasSuccessful()): + sys.exit(1) + class ExtraTestLoader(unittest.TestLoader): + testMethodPrefix = extra_test_prefix + # Call using ./scriptname --test [--verbose] + unittest.main(argv=[""], testLoader=ExtraTestLoader()) + else: + unittest.main(argv=[""]) else: main() finally: logging.shutdown() + +# Local Variables: +# run-tests: +# (lambda (&optional extra) +# (if (not (funcall run-tests-in-test-buffer default-directory +# extra)) +# (funcall show-test-buffer-in-test-window) +# (funcall remove-test-window) +# (if extra (message "Extra tests run successfully!")))) +# run-tests-in-test-buffer: +# (lambda (dir &optional extra) +# (with-current-buffer (get-buffer-create "*Test*") +# (setq buffer-read-only nil +# default-directory dir) +# (erase-buffer) +# (compilation-mode)) +# (let ((process-result +# (let ((inhibit-read-only t)) +# (process-file-shell-command +# (funcall get-command-line extra) nil "*Test*")))) +# (and (numberp process-result) +# (= process-result 0)))) +# get-command-line: +# (lambda (&optional extra) +# (let ((quoted-script +# (shell-quote-argument (funcall get-script-name)))) +# (format +# (concat "%s --check" (if extra " --prefix=atest" "")) +# quoted-script))) +# get-script-name: +# (lambda () +# (if (fboundp 'file-local-name) +# (file-local-name (buffer-file-name)) +# (or (file-remote-p (buffer-file-name) 'localname) +# (buffer-file-name)))) +# remove-test-window: +# (lambda () +# (let ((test-window (get-buffer-window "*Test*"))) +# (if test-window (delete-window test-window)))) +# show-test-buffer-in-test-window: +# (lambda () +# (when (not (get-buffer-window-list "*Test*")) +# (setq next-error-last-buffer (get-buffer "*Test*")) +# (let* ((side (if (>= (window-width) 146) 'right 'bottom)) +# (display-buffer-overriding-action +# `((display-buffer-in-side-window) (side . ,side) +# (window-height . fit-window-to-buffer) +# (window-width . fit-window-to-buffer)))) +# (display-buffer "*Test*")))) +# eval: +# (progn +# (let* ((run-extra-tests (lambda () (interactive) +# (funcall run-tests t))) +# (inner-keymap `(keymap (116 . ,run-extra-tests))) ; t +# (outer-keymap `(keymap (3 . ,inner-keymap)))) ; C-c +# (setq minor-mode-overriding-map-alist +# (cons `(run-tests . ,outer-keymap) +# minor-mode-overriding-map-alist))) +# (add-hook 'after-save-hook run-tests 90 t)) +# End: === modified file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2019-02-10 04:20:26 +0000 +++ mandos-clients.conf.xml 2023-02-07 19:29:28 +0000 @@ -228,13 +228,16 @@ >HEXSTRING - This option is required. + This option is required if the + is not set, and + optional otherwise. - This option sets the OpenPGP fingerprint that identifies - the public key that clients authenticate themselves with - through TLS. The string needs to be in hexadecimal form, - but spaces or upper/lower case are not significant. + This option sets the OpenPGP fingerprint that (before + GnuTLS 3.6.0) identified the public key that clients + authenticate themselves with through TLS. The string + needs to be in hexadecimal form, but spaces or upper/lower + case are not significant. @@ -244,13 +247,16 @@ >HEXSTRING - This option is optional. + This option is required if the + is not set, and + optional otherwise. - This option sets the certificate key ID that identifies - the public key that clients authenticate themselves with - through TLS. The string needs to be in hexadecimal form, - but spaces or upper/lower case are not significant. + This option sets the certificate key ID that (with GnuTLS + 3.6.6 or later) identifies the public key that clients + authenticate themselves with through TLS. The string + needs to be in hexadecimal form, but spaces or upper/lower + case are not significant. === modified file 'mandos-ctl' --- mandos-ctl 2020-04-08 18:40:10 +0000 +++ mandos-ctl 2022-04-25 18:46:48 +0000 @@ -1,10 +1,10 @@ #!/usr/bin/python3 -bbI -# -*- after-save-hook: (lambda () (let ((command (if (fboundp 'file-local-name) (file-local-name (buffer-file-name)) (or (file-remote-p (buffer-file-name) 'localname) (buffer-file-name))))) (if (= (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (format "%s --check" (shell-quote-argument command)) nil "*Test*")) 0) (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w))) (progn (with-current-buffer "*Test*" (compilation-mode)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); coding: utf-8 -*- -# -# Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2008-2019 Teddy Hogeborn -# Copyright © 2008-2019 Björn Påhlsson +# -*- coding: utf-8; lexical-binding: t -*- +# +# Mandos Control - Control or query the Mandos server +# +# Copyright © 2008-2022 Teddy Hogeborn +# Copyright © 2008-2022 Björn Påhlsson # # This file is part of Mandos. # @@ -23,7 +23,6 @@ # # Contact the authors at . # - from __future__ import (division, absolute_import, print_function, unicode_literals) @@ -33,15 +32,15 @@ pass import sys +import unittest import argparse +import logging +import os import locale import datetime import re -import os import collections import json -import unittest -import logging import io import tempfile import contextlib @@ -49,6 +48,7 @@ if sys.version_info.major == 2: __metaclass__ = type str = unicode + input = raw_input class gi: """Dummy gi module, for the tests""" @@ -77,7 +77,7 @@ import warnings warnings.simplefilter("default") -log = logging.getLogger(sys.argv[0]) +log = logging.getLogger(os.path.basename(sys.argv[0])) logging.basicConfig(level="INFO", # Show info level messages format="%(message)s") # Show basic log messages @@ -89,7 +89,7 @@ locale.setlocale(locale.LC_ALL, "") -version = "1.8.11" +version = "1.8.15" def main(): @@ -102,7 +102,7 @@ clientnames = options.client if options.debug: - log.setLevel(logging.DEBUG) + logging.getLogger("").setLevel(logging.DEBUG) if dbussy is not None and ravel is not None: bus = dbussy_adapter.CachingBus(dbussy, ravel) @@ -256,7 +256,7 @@ return rfc3339_duration_to_delta(interval) except ValueError as e: log.warning("%s - Parsing as pre-1.6.1 interval instead", - ' '.join(e.args)) + " ".join(e.args)) return parse_pre_1_6_1_interval(interval) @@ -395,22 +395,22 @@ """Parse an interval string as documented by Mandos before 1.6.1, and return a datetime.timedelta - >>> parse_pre_1_6_1_interval('7d') == datetime.timedelta(days=7) - True - >>> parse_pre_1_6_1_interval('60s') == datetime.timedelta(0, 60) - True - >>> parse_pre_1_6_1_interval('60m') == datetime.timedelta(hours=1) - True - >>> parse_pre_1_6_1_interval('24h') == datetime.timedelta(days=1) - True - >>> parse_pre_1_6_1_interval('1w') == datetime.timedelta(days=7) - True - >>> parse_pre_1_6_1_interval('5m 30s') == datetime.timedelta(0, 330) - True - >>> parse_pre_1_6_1_interval('') == datetime.timedelta(0) + >>> parse_pre_1_6_1_interval("7d") == datetime.timedelta(days=7) + True + >>> parse_pre_1_6_1_interval("60s") == datetime.timedelta(0, 60) + True + >>> parse_pre_1_6_1_interval("60m") == datetime.timedelta(hours=1) + True + >>> parse_pre_1_6_1_interval("24h") == datetime.timedelta(days=1) + True + >>> parse_pre_1_6_1_interval("1w") == datetime.timedelta(days=7) + True + >>> parse_pre_1_6_1_interval("5m 30s") == datetime.timedelta(0, 330) + True + >>> parse_pre_1_6_1_interval("") == datetime.timedelta(0) True >>> # Ignore unknown characters, allow any order and repetitions - >>> parse_pre_1_6_1_interval('2dxy7zz11y3m5m') == datetime.timedelta(2, 480, 18000) + >>> parse_pre_1_6_1_interval("2dxy7zz11y3m5m") == datetime.timedelta(2, 480, 18000) True """ @@ -726,7 +726,7 @@ with self.convert_exception(dbus.Error): value = method(*args) # DBussy returns values either as an empty list or as a - # tuple: (signature, value) + # list of one element with the return value if value: return self.type_filter(value[0]) @@ -738,6 +738,8 @@ def type_filter(self, value): """Convert the most bothersome types to Python types""" + # A D-Bus Variant value is represented as the Python type + # Tuple[dbussy.DBUS.Signature, Any] if isinstance(value, tuple): if (len(value) == 2 and isinstance(value[0], @@ -874,7 +876,7 @@ {key: properties[key] for key in self.all_keywords} for properties in clients.values()} - print(json.dumps(data, indent=4, separators=(',', ': '))) + print(json.dumps(data, indent=4, separators=(",", ": "))) class PrintTable(Output): @@ -2437,6 +2439,7 @@ busname = "se.recompile.Mandos" client_interface = "se.recompile.Mandos.Client" command.Approve().run(self.bus.clients, self.bus) + self.assertTrue(self.bus.clients) for clientpath in self.bus.clients: self.assertIn(("Approve", busname, clientpath, client_interface, (True,)), self.bus.calls) @@ -2445,16 +2448,22 @@ busname = "se.recompile.Mandos" client_interface = "se.recompile.Mandos.Client" command.Deny().run(self.bus.clients, self.bus) + self.assertTrue(self.bus.clients) for clientpath in self.bus.clients: self.assertIn(("Approve", busname, clientpath, client_interface, (False,)), self.bus.calls) def test_Remove(self): + busname = "se.recompile.Mandos" + server_path = "/" + server_interface = "se.recompile.Mandos" + orig_clients = self.bus.clients.copy() command.Remove().run(self.bus.clients, self.bus) - for clientpath in self.bus.clients: - self.assertIn(("RemoveClient", dbus_busname, - dbus_server_path, dbus_server_interface, + self.assertFalse(self.bus.clients) + for clientpath in orig_clients: + self.assertIn(("RemoveClient", busname, + server_path, server_interface, (clientpath,)), self.bus.calls) expected_json = { @@ -2662,11 +2671,13 @@ else: cmd_args = [() for x in range(len(self.values_to_get))] values_to_get = self.values_to_get + self.assertTrue(values_to_get) for value_to_get, cmd_arg in zip(values_to_get, cmd_args): for clientpath in self.bus.clients: self.bus.clients[clientpath][self.propname] = ( Unique()) self.command(*cmd_arg).run(self.bus.clients, self.bus) + self.assertTrue(self.bus.clients) for clientpath in self.bus.clients: value = (self.bus.clients[clientpath] [self.propname]) @@ -2731,9 +2742,12 @@ class TestSetSecretCmd(TestPropertySetterCmd): command = command.SetSecret propname = "Secret" - values_to_set = [io.BytesIO(b""), - io.BytesIO(b"secret\0xyzzy\nbar")] - values_to_get = [f.getvalue() for f in values_to_set] + def __init__(self, *args, **kwargs): + self.values_to_set = [io.BytesIO(b""), + io.BytesIO(b"secret\0xyzzy\nbar")] + self.values_to_get = [f.getvalue() for f in + self.values_to_set] + super(TestSetSecretCmd, self).__init__(*args, **kwargs) class TestSetTimeoutCmd(TestPropertySetterCmd): @@ -2792,15 +2806,16 @@ -def should_only_run_tests(): +def parse_test_args(): + # type: () -> argparse.Namespace parser = argparse.ArgumentParser(add_help=False) - parser.add_argument("--check", action='store_true') + parser.add_argument("--check", action="store_true") + parser.add_argument("--prefix", ) args, unknown_args = parser.parse_known_args() - run_tests = args.check - if run_tests: - # Remove --check argument from sys.argv + if args.check: + # Remove test options from sys.argv sys.argv[1:] = unknown_args - return run_tests + return args # Add all tests from doctest strings def load_tests(loader, tests, none): @@ -2809,11 +2824,81 @@ return tests if __name__ == "__main__": + options = parse_test_args() try: - if should_only_run_tests(): - # Call using ./tdd-python-script --check [--verbose] - unittest.main() + if options.check: + extra_test_prefix = options.prefix + if extra_test_prefix is not None: + if not (unittest.main(argv=[""], exit=False) + .result.wasSuccessful()): + sys.exit(1) + class ExtraTestLoader(unittest.TestLoader): + testMethodPrefix = extra_test_prefix + # Call using ./scriptname --check [--verbose] + unittest.main(argv=[""], testLoader=ExtraTestLoader()) + else: + unittest.main(argv=[""]) else: main() finally: logging.shutdown() + +# Local Variables: +# run-tests: +# (lambda (&optional extra) +# (if (not (funcall run-tests-in-test-buffer default-directory +# extra)) +# (funcall show-test-buffer-in-test-window) +# (funcall remove-test-window) +# (if extra (message "Extra tests run successfully!")))) +# run-tests-in-test-buffer: +# (lambda (dir &optional extra) +# (with-current-buffer (get-buffer-create "*Test*") +# (setq buffer-read-only nil +# default-directory dir) +# (erase-buffer) +# (compilation-mode)) +# (let ((process-result +# (let ((inhibit-read-only t)) +# (process-file-shell-command +# (funcall get-command-line extra) nil "*Test*")))) +# (and (numberp process-result) +# (= process-result 0)))) +# get-command-line: +# (lambda (&optional extra) +# (let ((quoted-script +# (shell-quote-argument (funcall get-script-name)))) +# (format +# (concat "%s --check" (if extra " --prefix=atest" "")) +# quoted-script))) +# get-script-name: +# (lambda () +# (if (fboundp 'file-local-name) +# (file-local-name (buffer-file-name)) +# (or (file-remote-p (buffer-file-name) 'localname) +# (buffer-file-name)))) +# remove-test-window: +# (lambda () +# (let ((test-window (get-buffer-window "*Test*"))) +# (if test-window (delete-window test-window)))) +# show-test-buffer-in-test-window: +# (lambda () +# (when (not (get-buffer-window-list "*Test*")) +# (setq next-error-last-buffer (get-buffer "*Test*")) +# (let* ((side (if (>= (window-width) 146) 'right 'bottom)) +# (display-buffer-overriding-action +# `((display-buffer-in-side-window) (side . ,side) +# (window-height . fit-window-to-buffer) +# (window-width . fit-window-to-buffer)))) +# (display-buffer "*Test*")))) +# eval: +# (progn +# (let* ((run-extra-tests (lambda () (interactive) +# (funcall run-tests t))) +# (inner-keymap `(keymap (116 . ,run-extra-tests))) ; t +# (outer-keymap `(keymap (3 . ,inner-keymap)))) ; C-c +# (setq minor-mode-overriding-map-alist +# (cons `(run-tests . ,outer-keymap) +# minor-mode-overriding-map-alist))) +# (add-hook 'after-save-hook run-tests 90 t)) +# End: === modified file 'mandos-keygen' --- mandos-keygen 2020-04-08 18:40:10 +0000 +++ mandos-keygen 2023-02-07 19:11:25 +0000 @@ -23,7 +23,7 @@ # Contact the authors at . # -VERSION="1.8.11" +VERSION="1.8.15" KEYDIR="/etc/keys/mandos" KEYTYPE=RSA @@ -147,28 +147,28 @@ echo "Empty key type" >&2 exit 1 fi - + if [ -z "$KEYNAME" ]; then echo "Empty key name" >&2 exit 1 fi - + if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then echo "Invalid key length" >&2 exit 1 fi - + if [ -z "$KEYEXPIRE" ]; then echo "Empty key expiration" >&2 exit 1 fi - + # Make FORCE be 0 or 1 case "$FORCE" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;; [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;; esac - + if { [ -e "$SECKEYFILE" ] || [ -e "$PUBKEYFILE" ] \ || [ -e "$TLS_PRIVKEYFILE" ] \ || [ -e "$TLS_PUBKEYFILE" ]; } \ @@ -176,7 +176,7 @@ echo "Refusing to overwrite old key files; use --force" >&2 exit 1 fi - + # Set lines for GnuPG batch file if [ -n "$KEYCOMMENT" ]; then KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT" @@ -184,7 +184,7 @@ if [ -n "$KEYEMAIL" ]; then KEYEMAILLINE="Name-Email: $KEYEMAIL" fi - + # Create temporary gpg batch file BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`" TLS_PRIVKEYTMP="`mktemp -t mandos-keygen-privkey.XXXXXXXXXX`" @@ -233,7 +233,7 @@ %no-protection %commit EOF - + if tty --quiet; then cat <<-EOF Note: Due to entropy requirements, key generation could take @@ -276,7 +276,7 @@ fi fi fi - + # Make sure trustdb.gpg exists; # this is a workaround for Debian bug #737128 gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ @@ -287,12 +287,12 @@ --homedir "$RINGDIR" --trust-model always \ --gen-key "$BATCHFILE" rm --force "$BATCHFILE" - + if tty --quiet; then echo -n "Finished: " date fi - + # Backup any old key files if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \ 2>/dev/null; then @@ -302,16 +302,16 @@ 2>/dev/null; then rm --force "$PUBKEYFILE" fi - + FILECOMMENT="Mandos client key for $KEYNAME" if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)" fi - + if [ -n "$KEYEMAIL" ]; then FILECOMMENT="$FILECOMMENT <$KEYEMAIL>" fi - + # Export key from key rings to key files gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --armor --export-options export-minimal \ @@ -323,13 +323,13 @@ fi if [ "$mode" = password ]; then - + # Make SSH be 0 or 1 case "$SSH" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) SSH=1;; [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) SSH=0;; esac - + if [ $SSH -eq 1 ]; then for ssh_keytype in ecdsa-sha2-nistp256 ed25519 rsa; do set +e @@ -346,7 +346,7 @@ fi done fi - + # Import key into temporary key rings gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor \ @@ -354,16 +354,16 @@ gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor \ --import "$PUBKEYFILE" - + # Get fingerprint of key FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \ --enable-dsa2 --homedir "$RINGDIR" --trust-model always \ --fingerprint --with-colons \ | sed --quiet \ --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`" - + test -n "$FINGERPRINT" - + if [ -r "$TLS_PUBKEYFILE" ]; then KEY_ID="$(certtool --key-id --hash=sha256 \ --infile="$TLS_PUBKEYFILE" 2>/dev/null || :)" @@ -376,9 +376,9 @@ fi test -n "$KEY_ID" fi - + FILECOMMENT="Encrypted password for a Mandos client" - + while [ ! -s "$SECFILE" ]; do if [ -n "$PASSFILE" ]; then cat -- "$PASSFILE" @@ -397,7 +397,7 @@ echo "Passphrase mismatch" >&2 touch "$RINGDIR"/mismatch else - echo -n "$first" + printf "%s" "$first" fi fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ --homedir "$RINGDIR" --trust-model always --armor \ @@ -412,7 +412,7 @@ fi fi done - + cat <<-EOF [$KEYNAME] host = $KEYNAME === modified file 'mandos-monitor' --- mandos-monitor 2020-04-08 18:40:10 +0000 +++ mandos-monitor 2022-04-25 18:46:48 +0000 @@ -23,20 +23,20 @@ # # Contact the authors at . # - from __future__ import (division, absolute_import, print_function, unicode_literals) + try: from future_builtins import * except ImportError: pass import sys +import logging import os import warnings import datetime import locale -import logging import urwid.curses_display import urwid @@ -49,6 +49,11 @@ if sys.version_info.major == 2: __metaclass__ = type str = unicode + input = raw_input + +# Show warnings by default +if not sys.warnoptions: + warnings.simplefilter("default") log = logging.getLogger(os.path.basename(sys.argv[0])) logging.basicConfig(level="NOTSET", # Show all messages @@ -64,7 +69,7 @@ domain = "se.recompile" server_interface = domain + ".Mandos" client_interface = domain + ".Mandos.Client" -version = "1.8.11" +version = "1.8.15" try: dbus.OBJECT_MANAGER_IFACE === modified file 'mandos-to-cryptroot-unlock' --- mandos-to-cryptroot-unlock 2019-07-24 11:02:24 +0000 +++ mandos-to-cryptroot-unlock 2020-07-04 11:58:52 +0000 @@ -2,8 +2,8 @@ # # Script to get password from plugin-runner to cryptroot-unlock # -# Copyright © 2018 Teddy Hogeborn -# Copyright © 2018 Björn Påhlsson +# Copyright © 2018-2019 Teddy Hogeborn +# Copyright © 2018-2019 Björn Påhlsson # # This file is part of Mandos. # === modified file 'mandos.lsm' --- mandos.lsm 2020-04-08 18:40:10 +0000 +++ mandos.lsm 2022-04-25 18:46:48 +0000 @@ -1,7 +1,7 @@ Begin4 Title: Mandos -Version: 1.8.11 -Entered-date: 2020-04-08 +Version: 1.8.15 +Entered-date: 2022-04-25 Description: The Mandos system allows computers to have encrypted root file systems and at the same time be capable of remote and/or unattended reboots. @@ -12,9 +12,9 @@ Maintained-by: teddy@recompile.se (Teddy Hogeborn), belorn@recompile.se (Björn Påhlsson) Primary-site: https://www.recompile.se/mandos - 234K mandos_1.8.11.orig.tar.gz + 239K mandos_1.8.15.orig.tar.gz Alternate-site: ftp://ftp.recompile.se/pub/mandos - 234K mandos_1.8.11.orig.tar.gz + 239K mandos_1.8.15.orig.tar.gz Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.7, and various other libraries. While made for Debian GNU/Linux, it is probably portable to other === modified file 'mandos.xml' --- mandos.xml 2019-07-24 06:16:09 +0000 +++ mandos.xml 2022-04-23 23:25:49 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -136,8 +136,8 @@ DESCRIPTION &COMMANDNAME; is a server daemon which - handles incoming request for passwords for a pre-defined list of - client host computers. For an introduction, see + handles incoming requests for passwords for a pre-defined list + of client host computers. For an introduction, see intro 8mandos. The Mandos server uses Zeroconf to announce itself on the local network, and uses @@ -739,7 +739,7 @@ The clients use IPv6 link-local addresses, which are - immediately usable since a link-local addresses is + immediately usable since a link-local address is automatically assigned to a network interfaces when it is brought up. === modified file 'plugin-helpers/mandos-client-iprouteadddel.c' --- plugin-helpers/mandos-client-iprouteadddel.c 2018-02-18 01:29:21 +0000 +++ plugin-helpers/mandos-client-iprouteadddel.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * iprouteadddel - Add or delete direct route to a local IP address * - * Copyright © 2015-2018 Teddy Hogeborn - * Copyright © 2015-2018 Björn Påhlsson + * Copyright © 2015-2018, 2021-2022 Teddy Hogeborn + * Copyright © 2015-2018, 2021-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -25,49 +25,57 @@ #define _GNU_SOURCE /* program_invocation_short_name */ #include /* bool, false, true */ -#include /* fprintf(), stderr, FILE, vfprintf */ -#include /* program_invocation_short_name, - errno, perror(), EINVAL, ENOMEM */ -#include /* va_list, va_start */ -#include /* EXIT_SUCCESS */ -#include /* struct argp_option, error_t, struct - argp_state, ARGP_KEY_ARG, +#include /* argp_program_version, + argp_program_bug_address, + struct argp_option, + struct argp_state, ARGP_KEY_ARG, argp_usage(), ARGP_KEY_END, ARGP_ERR_UNKNOWN, struct argp, - argp_parse() */ -#include /* EX_USAGE, EX_OSERR */ -#include /* sa_family_t, AF_INET6, AF_INET */ -#include /* PRIdMAX, intmax_t */ - + argp_parse(), ARGP_IN_ORDER */ +#include /* errno, + program_invocation_short_name, + error_t, EINVAL, ENOMEM */ +#include /* fprintf(), stderr, perror(), FILE, + vfprintf() */ +#include /* va_list, va_start(), vfprintf() */ +#include /* EXIT_SUCCESS */ #include /* struct nl_addr, nl_addr_parse(), nl_geterror(), - nl_addr_get_family(), + nl_addr_get_family(), NLM_F_EXCL, nl_addr_put() */ -#include /* struct rtnl_route, - struct rtnl_nexthop, - rtnl_route_alloc(), - rtnl_route_set_family(), - rtnl_route_set_protocol(), - RTPROT_BOOT, - rtnl_route_set_scope(), - RT_SCOPE_LINK, - rtnl_route_set_type(), - RTN_UNICAST, - rtnl_route_set_dst(), - rtnl_route_set_table(), - RT_TABLE_MAIN, - rtnl_route_nh_alloc(), - rtnl_route_nh_set_ifindex(), - rtnl_route_add_nexthop(), - rtnl_route_add(), - rtnl_route_delete(), - rtnl_route_put(), - rtnl_route_nh_free() */ +#include /* NULL */ +#include /* struct rtnl_route, + struct rtnl_nexthop, NETLINK_ROUTE, + rtnl_route_alloc(), + rtnl_route_set_family(), + rtnl_route_set_protocol(), + RTPROT_BOOT, + rtnl_route_set_scope(), + RT_SCOPE_LINK, + rtnl_route_set_type(), RTN_UNICAST, + rtnl_route_set_dst(), + rtnl_route_set_table(), + RT_TABLE_MAIN, + rtnl_route_nh_alloc(), + rtnl_route_nh_set_ifindex(), + rtnl_route_add_nexthop(), + rtnl_route_add(), + rtnl_route_delete(), + rtnl_route_put(), + rtnl_route_nh_free() */ #include /* struct nl_sock, nl_socket_alloc(), nl_connect(), nl_socket_free() */ -#include /* rtnl_link_get_kernel(), +#include /* strcasecmp() */ +#include /* AF_UNSPEC, AF_INET6, AF_INET */ +#include /* EX_USAGE, EX_OSERR */ +#include /* struct rtnl_link, + rtnl_link_get_kernel(), rtnl_link_get_ifindex(), rtnl_link_put() */ +#include /* sa_family_t */ +#include /* PRIdMAX, intmax_t */ +#include /* uint8_t */ + bool debug = false; const char *argp_program_version = "mandos-client-iprouteadddel " VERSION; @@ -85,7 +93,7 @@ __attribute__((format (gnu_printf, 2, 3), nonnull)) int fprintf_plus(FILE *stream, const char *format, ...){ va_list ap; - va_start (ap, format); + va_start(ap, format); fprintf(stream, "Mandos plugin helper %s: ", program_invocation_short_name); === modified file 'plugin-runner.c' --- plugin-runner.c 2020-02-09 03:38:33 +0000 +++ plugin-runner.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008-2018 Teddy Hogeborn - * Copyright © 2008-2018 Björn Påhlsson + * Copyright © 2008-2022 Teddy Hogeborn + * Copyright © 2008-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,55 +23,69 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(), - O_CLOEXEC, pipe2() */ +#define _GNU_SOURCE /* strchrnul(), TEMP_FAILURE_RETRY(), + getline(), asprintf(), O_CLOEXEC, + scandirat(), pipe2() */ +#include /* argp_program_version, + argp_program_bug_address, + struct argp_option, + struct argp_state, argp_error(), + ARGP_NO_EXIT, argp_state_help, + ARGP_HELP_STD_HELP, + ARGP_HELP_USAGE, ARGP_HELP_EXIT_OK, + ARGP_KEY_ARG, ARGP_ERR_UNKNOWN, + struct argp, argp_parse(), + ARGP_IN_ORDER, ARGP_NO_HELP */ +#include /* bool, false, true */ +#include /* pid_t, sig_atomic_t, uid_t, gid_t, + getuid(), setgid(), setuid() */ #include /* size_t, NULL */ -#include /* malloc(), reallocarray(), realloc(), - EXIT_SUCCESS, exit() */ -#include /* bool, true, false */ -#include /* fileno(), fprintf(), - stderr, STDOUT_FILENO, fclose() */ -#include /* fstat(), struct stat, waitpid(), - WIFEXITED(), WEXITSTATUS(), wait(), - pid_t, uid_t, gid_t, getuid(), - getgid() */ -#include /* fd_set, select(), FD_ZERO(), - FD_SET(), FD_ISSET(), FD_CLR */ -#include /* wait(), waitpid(), WIFEXITED(), - WEXITSTATUS(), WTERMSIG() */ -#include /* struct stat, fstat(), S_ISREG() */ -#include /* and, or, not */ -#include /* struct dirent, scandirat() */ -#include /* fcntl(), F_GETFD, F_SETFD, - FD_CLOEXEC, write(), STDOUT_FILENO, - struct stat, fstat(), close(), - setgid(), setuid(), S_ISREG(), - faccessat() pipe2(), fork(), - _exit(), dup2(), fexecve(), read() - */ +#include /* or, and, not */ +#include /* strcmp(), strdup(), strchrnul(), + strncmp(), strlen(), strcpy(), + strsep(), strchr(), strsignal() */ +#include /* malloc(), free(), reallocarray(), + realloc(), EXIT_SUCCESS */ +#include /* errno, EINTR, ENOMEM, ECHILD, + error_t, EINVAL, EMFILE, ENFILE, + ENOENT, ESRCH */ +#include /* SIZE_MAX */ +#define _GNU_SOURCE /* strchrnul(), TEMP_FAILURE_RETRY(), + getline(), asprintf(), O_CLOEXEC, + scandirat(), pipe2() */ +#include /* TEMP_FAILURE_RETRY(), ssize_t, + write(), STDOUT_FILENO, uid_t, + gid_t, getuid(), fchown(), close(), + symlink(), setgid(), setuid(), + faccessat(), X_OK, pipe(), pipe2(), + fork(), _exit(), dup2(), fexecve(), + read(), getpass() */ #include /* fcntl(), F_GETFD, F_SETFD, - FD_CLOEXEC, openat(), scandirat(), - pipe2() */ -#include /* strsep, strlen(), strsignal(), - strcmp(), strncmp() */ -#include /* errno */ -#include /* struct argp_option, struct - argp_state, struct argp, - argp_parse(), ARGP_ERR_UNKNOWN, - ARGP_KEY_END, ARGP_KEY_ARG, - error_t */ -#include /* struct sigaction, sigemptyset(), - sigaddset(), sigaction(), - sigprocmask(), SIG_BLOCK, SIGCHLD, - SIG_UNBLOCK, kill(), sig_atomic_t - */ -#include /* errno, EBADF */ -#include /* intmax_t, PRIdMAX, strtoimax() */ + FD_CLOEXEC, open(), O_RDONLY, + O_CLOEXEC, openat() */ +#include /* waitpid(), WNOHANG, WIFEXITED(), + WEXITSTATUS(), WIFSIGNALED(), + WTERMSIG(), wait() */ +#include /* error() */ +#include /* FILE, fprintf(), fopen(), + getline(), fclose(), EOF, + asprintf(), stderr */ +#include /* struct dirent, scandirat(), + alphasort() */ +#include /* struct stat, fstat(), S_ISDIR(), + lstat(), S_ISREG() */ +#include /* fd_set, FD_ZERO(), FD_SETSIZE, + FD_SET(), select(), FD_CLR(), + FD_ISSET() */ +#include /* struct sigaction, SA_NOCLDSTOP, + sigemptyset(), sigaddset(), + SIGCHLD, sigprocmask(), SIG_BLOCK, + SIG_UNBLOCK, kill(), SIGTERM */ #include /* EX_OSERR, EX_USAGE, EX_IOERR, EX_CONFIG, EX_UNAVAILABLE, EX_OK */ -#include /* errno */ -#include /* error() */ -#include /* fnmatch() */ +#include /* intmax_t, strtoimax(), PRIdMAX */ +#include /* fnmatch(), FNM_FILE_NAME, + FNM_PERIOD, FNM_NOMATCH */ #define BUFFER_SIZE 256 @@ -720,7 +734,8 @@ custom_argc += 1; { #if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26) - char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1, + char **new_argv = reallocarray(custom_argv, + (size_t)custom_argc + 1, sizeof(char *)); #else char **new_argv = NULL; @@ -858,6 +873,15 @@ } close(plugindir_fd); } + + /* Work around Debian bug #981302 + */ + if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){ + ret = symlink("/proc/self/fd", "/dev/fd"); + if(ret == -1){ + error(0, errno, "Failed to create /dev/fd symlink"); + } + } } /* Lower permissions */ === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2019-02-11 07:06:55 +0000 +++ plugins.d/askpass-fifo.c 2021-02-03 08:33:43 +0000 @@ -2,8 +2,8 @@ /* * Askpass-FIFO - Read a password from a FIFO and output it * - * Copyright © 2008-2019 Teddy Hogeborn - * Copyright © 2008-2019 Björn Påhlsson + * Copyright © 2008-2019, 2021 Teddy Hogeborn + * Copyright © 2008-2019, 2021 Björn Påhlsson * * This file is part of Mandos. * @@ -23,27 +23,31 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ -#include /* uid_t, gid_t, ssize_t */ -#include /* mkfifo(), S_IRUSR, S_IWUSR */ -#include /* and */ -#include /* errno, EACCES, ENOTDIR, ELOOP, +#define _GNU_SOURCE /* vasprintf(), + program_invocation_short_name */ +#include /* uid_t, gid_t, getuid(), getgid(), + setgid(), setuid() */ +#include /* uid_t, gid_t, ssize_t, getuid(), + getgid(), setgid(), setuid(), + read(), close(), write(), + STDOUT_FILENO */ +#include /* va_list, va_start(), vfprintf() */ +#include /* vasprintf(), fprintf(), stderr, + vfprintf() */ +#include /* program_invocation_short_name, + errno, EACCES, ENOTDIR, ELOOP, ENAMETOOLONG, ENOSPC, EROFS, ENOENT, EEXIST, EFAULT, EMFILE, ENFILE, ENOMEM, EBADF, EINVAL, EIO, EISDIR, EFBIG */ +#include /* strerror() */ #include /* error() */ -#include /* fprintf(), vfprintf(), - vasprintf() */ -#include /* EXIT_FAILURE, NULL, size_t, free(), - realloc(), EXIT_SUCCESS */ +#include /* free(), realloc(), EXIT_SUCCESS */ +#include /* mkfifo(), S_IRUSR, S_IWUSR */ +#include /* EX_OSFILE, EX_OSERR, + EX_UNAVAILABLE, EX_IOERR */ #include /* open(), O_RDONLY */ -#include /* read(), close(), write(), - STDOUT_FILENO */ -#include /* EX_OSERR, EX_OSFILE, - EX_UNAVAILABLE, EX_IOERR */ -#include /* strerror() */ -#include /* va_list, va_start(), ... */ +#include /* NULL, size_t */ uid_t uid = 65534; gid_t gid = 65534; === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2020-04-05 21:30:59 +0000 +++ plugins.d/mandos-client.c 2022-04-24 16:54:30 +0000 @@ -9,8 +9,8 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008-2019 Teddy Hogeborn - * Copyright © 2008-2019 Björn Påhlsson + * Copyright © 2008-2022 Teddy Hogeborn + * Copyright © 2008-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -38,69 +38,103 @@ #define _FILE_OFFSET_BITS 64 #endif /* not _FILE_OFFSET_BITS */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ - -#include /* fprintf(), stderr, fwrite(), - stdout, ferror() */ -#include /* uint16_t, uint32_t, intptr_t */ -#include /* NULL, size_t, ssize_t */ -#include /* free(), EXIT_SUCCESS, srand(), - strtof(), abort() */ +#define _GNU_SOURCE /* program_invocation_short_name, + TEMP_FAILURE_RETRY(), O_CLOEXEC, + scandirat(), asprintf() */ #include /* bool, false, true */ -#include /* strcmp(), strlen(), strerror(), - asprintf(), strncpy(), strsignal() - */ -#include /* ioctl */ -#include /* socket(), inet_pton(), sockaddr, - sockaddr_in6, PF_INET6, - SOCK_STREAM, uid_t, gid_t, open(), - opendir(), DIR */ -#include /* open(), S_ISREG */ -#include /* socket(), struct sockaddr_in6, - inet_pton(), connect(), - getnameinfo() */ -#include /* open(), unlinkat(), AT_REMOVEDIR */ -#include /* opendir(), struct dirent, readdir() - */ -#include /* PRIu16, PRIdMAX, intmax_t, - strtoimax() */ -#include /* perror(), errno, EINTR, EINVAL, - EAI_SYSTEM, ENETUNREACH, +#include /* argp_program_version, + argp_program_bug_address, + struct argp_option, + struct argp_state, argp_error(), + argp_state_help, + ARGP_HELP_STD_HELP, + ARGP_HELP_EXIT_ERR, + ARGP_HELP_EXIT_OK, ARGP_HELP_USAGE, + argp_err_exit_status, + ARGP_ERR_UNKNOWN, struct argp, + argp_parse(), ARGP_IN_ORDER, + ARGP_NO_HELP */ +#include /* NULL, size_t */ +#include /* uid_t, gid_t, sig_atomic_t, + seteuid(), setuid(), pid_t, + setgid(), getuid(), getgid() */ +#include /* uid_t, gid_t, TEMP_FAILURE_RETRY(), + seteuid(), setuid(), close(), + ssize_t, read(), fork(), setgid(), + _exit(), dup2(), STDIN_FILENO, + STDERR_FILENO, STDOUT_FILENO, + fexecve(), write(), getuid(), + getgid(), fchown(), symlink(), + sleep(), unlinkat(), pause() */ +#include /* in_port_t, struct sockaddr_in6, + sa_family_t, struct sockaddr_in, + htons(), IN6_IS_ADDR_LINKLOCAL, + INET_ADDRSTRLEN, INET6_ADDRSTRLEN, + ntohl(), IPPROTO_IP */ +#include /* struct timespec, clock_gettime(), + CLOCK_MONOTONIC, time_t, struct tm, + gmtime_r(), clock_settime(), + CLOCK_REALTIME, nanosleep() */ +#include /* errno, + program_invocation_short_name, + EINTR, EINVAL, ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, EPROTO, - EIO, ENOENT, ENXIO, ENOMEM, EISDIR, - ENOTEMPTY, - program_invocation_short_name */ -#include /* nanosleep(), time(), sleep() */ -#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, - SIOCSIFFLAGS, if_indextoname(), - if_nametoindex(), IF_NAMESIZE */ -#include /* IN6_IS_ADDR_LINKLOCAL, - INET_ADDRSTRLEN, INET6_ADDRSTRLEN - */ -#include /* close(), SEEK_SET, off_t, write(), - getuid(), getgid(), seteuid(), - setgid(), pause(), _exit(), - unlinkat() */ -#include /* inet_pton(), htons() */ -#include /* not, or, and */ -#include /* struct argp_option, error_t, struct - argp_state, struct argp, - argp_parse(), ARGP_KEY_ARG, - ARGP_KEY_END, ARGP_ERR_UNKNOWN */ -#include /* sigemptyset(), sigaddset(), - sigaction(), SIGTERM, sig_atomic_t, - raise() */ -#include /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE, - EX_NOHOST, EX_IOERR, EX_PROTOCOL */ + EIO, ENOENT, ENXIO, error_t, + ENOMEM, EISDIR, ENOTEMPTY */ +#include /* fprintf(), stderr, perror(), FILE, + vfprintf(), off_t, SEEK_SET, + stdout, fwrite(), ferror(), + fflush(), asprintf() */ +#include /* va_list, va_start(), vfprintf() */ +#include /* realloc(), free(), malloc(), + getenv(), EXIT_FAILURE, setenv(), + EXIT_SUCCESS, strtof(), strtod(), + srand(), mkdtemp(), abort() */ +#include /* strdup(), strcmp(), strlen(), + strerror(), strncpy(), strspn(), + memcpy(), strrchr(), strchr(), + strsignal() */ +#include /* open(), O_RDONLY, O_DIRECTORY, + O_PATH, O_CLOEXEC, openat(), + O_NOFOLLOW, AT_REMOVEDIR */ +#include /* or, and, not */ +#include /* struct stat, fstat(), fstatat(), + S_ISREG(), S_IXUSR, S_IXGRP, + S_IXOTH, lstat() */ +#include /* IF_NAMESIZE, if_indextoname(), + if_nametoindex(), SIOCGIFFLAGS, + IFF_LOOPBACK, IFF_POINTOPOINT, + IFF_BROADCAST, IFF_NOARP, IFF_UP, + IFF_RUNNING, SIOCSIFFLAGS */ +#include /* EX_NOPERM, EX_OSERR, + EX_UNAVAILABLE, EX_USAGE */ +#include /* setgroups() */ #include /* waitpid(), WIFEXITED(), - WEXITSTATUS(), WTERMSIG() */ -#include /* setgroups() */ -#include /* argz_add_sep(), argz_next(), - argz_delete(), argz_append(), - argz_stringify(), argz_add(), - argz_count() */ + WEXITSTATUS(), WIFSIGNALED(), + WTERMSIG() */ +#include /* kill(), SIGTERM, struct sigaction, + SIG_DFL, sigemptyset(), + sigaddset(), SIGINT, SIGHUP, + SIG_IGN, raise() */ +#include /* struct sockaddr_storage, AF_INET6, + PF_INET6, AF_INET, PF_INET, + socket(), SOCK_STREAM, + SOCK_CLOEXEC, struct sockaddr, + connect(), SOCK_DGRAM */ +#include /* argz_next(), argz_add_sep(), + argz_delete(), argz_stringify(), + argz_add(), argz_count() */ +#include /* PRIuMAX, uintmax_t, uint32_t, + PRIdMAX, PRIu16, intmax_t, + strtoimax() */ +#include /* inet_pton() */ +#include /* uint32_t, intptr_t, uint16_t */ #include /* getnameinfo(), NI_NUMERICHOST, EAI_SYSTEM, gai_strerror() */ +#include /* ioctl() */ +#include /* struct dirent, scandirat(), + alphasort(), scandir() */ +#include /* INT_MAX */ #ifdef __linux__ #include /* klogctl() */ @@ -119,26 +153,22 @@ /* GnuTLS */ #include /* All GnuTLS types, constants and - functions: - gnutls_* - init_gnutls_session(), - GNUTLS_* */ + functions: gnutls_*, GNUTLS_* */ #if GNUTLS_VERSION_NUMBER < 0x030600 #include /* gnutls_certificate_set_openpgp_key_file(), GNUTLS_OPENPGP_FMT_BASE64 */ #elif GNUTLS_VERSION_NUMBER >= 0x030606 -#include /* gnutls_pkcs_encrypt_flags_t, - GNUTLS_PKCS_PLAIN, - GNUTLS_PKCS_NULL_PASSWORD */ +#include /* GNUTLS_PKCS_PLAIN, + GNUTLS_PKCS_NULL_PASSWORD */ #endif /* GPGME */ #include /* All GPGME types, constants and functions: - gpgme_* - GPGME_PROTOCOL_OpenPGP, - GPG_ERR_NO_* */ + gpgme_*, GPG_ERR_NO_*, + GPGME_IMPORT_* + GPGME_PROTOCOL_OpenPGP */ #define BUFFER_SIZE 256 @@ -396,9 +426,8 @@ fprintf_plus(stderr, "Setting system clock to key file mtime"); } - time_t keytime = keystat.st_mtim.tv_sec; - if(stime(&keytime) != 0){ - perror_plus("stime"); + if(clock_settime(CLOCK_REALTIME, &keystat.st_mtim) != 0){ + perror_plus("clock_settime"); } ret = lower_privileges(); if(ret != 0){ @@ -2716,9 +2745,6 @@ } { - /* Work around Debian bug #633582: - */ - /* Re-raise privileges */ ret = raise_privileges(); if(ret != 0){ @@ -2727,6 +2753,9 @@ } else { struct stat st; + /* Work around Debian bug #633582: + */ + if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){ int seckey_fd = open(seckey, O_RDONLY); if(seckey_fd == -1){ @@ -2791,6 +2820,15 @@ } } + /* Work around Debian bug #981302 + */ + if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){ + ret = symlink("/proc/self/fd", "/dev/fd"); + if(ret == -1){ + perror_plus("Failed to create /dev/fd symlink"); + } + } + /* Lower privileges */ ret = lower_privileges(); if(ret != 0){ === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2019-07-27 10:11:45 +0000 +++ plugins.d/password-prompt.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * Password-prompt - Read a password from the terminal and print it * - * Copyright © 2008-2019 Teddy Hogeborn - * Copyright © 2008-2019 Björn Påhlsson + * Copyright © 2008-2019, 2021-2022 Teddy Hogeborn + * Copyright © 2008-2019, 2021-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,47 +23,52 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* getline(), asprintf() */ - -#include /* struct termios, tcsetattr(), - TCSAFLUSH, tcgetattr(), ECHO */ -#include /* access(), struct termios, - tcsetattr(), STDIN_FILENO, - TCSAFLUSH, tcgetattr(), ECHO, - readlink() */ -#include /* sig_atomic_t, raise(), struct - sigaction, sigemptyset(), - sigaction(), sigaddset(), SIGINT, - SIGQUIT, SIGHUP, SIGTERM, +#define _GNU_SOURCE /* vasprintf(), + program_invocation_short_name, + asprintf(), getline() */ +#include /* sig_atomic_t, pid_t */ +#include /* bool, false, true */ +#include /* argp_program_version, + argp_program_bug_address, + struct argp_option, + struct argp_state, argp_state_help, + ARGP_HELP_STD_HELP, + ARGP_HELP_EXIT_ERR, + ARGP_HELP_EXIT_OK, ARGP_HELP_USAGE, + argp_err_exit_status, + ARGP_ERR_UNKNOWN, argp_parse(), + ARGP_IN_ORDER, ARGP_NO_HELP */ +#include /* va_list, va_start(), vfprintf() */ +#include /* vasprintf(), fprintf(), stderr, + vfprintf(), asprintf(), getline(), + stdin, feof(), clearerr(), + fputc() */ +#include /* program_invocation_short_name, + errno, ENOENT, error_t, ENOMEM, + EINVAL, EBADF, ENOTTY, EFAULT, + EFBIG, EIO, ENOSPC, EINTR */ +#include /* strerror(), strrchr(), strcmp() */ +#include /* error() */ +#include /* free(), realloc(), EXIT_SUCCESS, + EXIT_FAILURE, getenv() */ +#include /* access(), R_OK, ssize_t, close(), + read(), STDIN_FILENO, write(), + STDOUT_FILENO */ +#include /* struct dirent, scandir(), + alphasort() */ +#include /* uintmax_t, strtoumax() */ +#include /* or, and, not */ +#include /* open(), O_RDONLY */ +#include /* NULL, size_t */ +#include /* struct termios, tcgetattr(), + tcflag_t, ECHO, tcsetattr(), + TCSAFLUSH */ +#include /* struct sigaction, sigemptyset(), + sigaddset(), SIGINT, SIGHUP, + SIGTERM, SIG_IGN, SIG_DFL, raise() */ -#include /* NULL, size_t, ssize_t */ -#include /* ssize_t, struct dirent, pid_t, - ssize_t, open() */ -#include /* EXIT_SUCCESS, EXIT_FAILURE, - getenv(), free() */ -#include /* scandir(), alphasort() */ -#include /* fprintf(), stderr, getline(), - stdin, feof(), fputc(), vfprintf(), - vasprintf() */ -#include /* errno, EBADF, ENOTTY, EINVAL, - EFAULT, EFBIG, EIO, ENOSPC, EINTR - */ -#include /* error() */ -#include /* or, not */ -#include /* bool, false, true */ -#include /* strtoumax() */ -#include /* struct stat, lstat(), open() */ -#include /* strlen, rindex, memcmp, strerror() - */ -#include /* struct argp_option, struct - argp_state, struct argp, - argp_parse(), error_t, - ARGP_KEY_ARG, ARGP_KEY_END, - ARGP_ERR_UNKNOWN */ -#include /* EX_SOFTWARE, EX_OSERR, - EX_UNAVAILABLE, EX_IOERR, EX_OK */ -#include /* open() */ -#include /* va_list, va_start(), ... */ +#include /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE, + EX_IOERR, EX_OSFILE, EX_OK */ volatile sig_atomic_t quit_now = 0; int signal_received; === modified file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2020-02-09 03:38:33 +0000 +++ plugins.d/plymouth.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * Plymouth - Read a password from Plymouth and output it * - * Copyright © 2010-2019 Teddy Hogeborn - * Copyright © 2010-2019 Björn Påhlsson + * Copyright © 2010-2022 Teddy Hogeborn + * Copyright © 2010-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,37 +23,55 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - kill(), SIG_IGN */ +#define _GNU_SOURCE /* program_invocation_short_name, + vasprintf(), asprintf(), + TEMP_FAILURE_RETRY() */ +#include /* sig_atomic_t, pid_t, setuid(), + geteuid(), setsid() */ +#include /* argp_program_version, + argp_program_bug_address, + struct argp_option, + struct argp_state, + ARGP_ERR_UNKNOWN, struct argp, + argp_parse(), ARGP_IN_ORDER */ +#include /* NULL, size_t */ #include /* bool, false, true */ +#include /* FILE, fprintf(), vfprintf(), + vasprintf(), stderr, asprintf(), + fopen(), fscanf(), fclose(), + sscanf() */ +#include /* va_list, va_start(), vfprintf() */ +#include /* program_invocation_short_name, + errno, ENOMEM, EINTR, ENOENT, + error_t, EINVAL */ +#include /* strerror(), strdup(), memcmp() */ +#include /* error() */ +#include /* free(), getenv(), malloc(), + reallocarray(), realloc(), + EXIT_FAILURE, EXIT_SUCCESS */ +#include /* TEMP_FAILURE_RETRY(), setuid(), + geteuid(), setsid(), chdir(), + dup2(), STDERR_FILENO, + STDOUT_FILENO, fork(), _exit(), + execv(), ssize_t, readlink(), + close(), read(), access(), X_OK */ +#include /* kill(), SIGTERM, struct sigaction, + sigemptyset(), SIGINT, SIGHUP, + sigaddset(), SIG_IGN */ +#include /* waitpid(), WIFEXITED(), + WEXITSTATUS(), WIFSIGNALED(), + WTERMSIG() */ +#include /* not, and, or */ +#include /* EX_OSERR, EX_USAGE, + EX_UNAVAILABLE */ +#include /* SIZE_MAX */ +#include /* struct dirent, scandir(), + alphasort() */ +#include /* uintmax_t, strtoumax(), SCNuMAX, + PRIuMAX */ +#include /* struct stat, lstat(), S_ISLNK() */ #include /* open(), O_RDONLY */ -#include /* and, or, not*/ -#include /* size_t, ssize_t, pid_t, struct - dirent, waitpid() */ -#include /* waitpid() */ -#include /* NULL */ -#include /* strchr(), memcmp() */ -#include /* asprintf(), perror(), fopen(), - fscanf(), vasprintf(), fprintf(), - vfprintf() */ -#include /* close(), readlink(), read(), - fork(), setsid(), chdir(), dup2(), - STDERR_FILENO, execv(), access() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv(), reallocarray() */ -#include /* scandir(), alphasort() */ -#include /* intmax_t, strtoumax(), SCNuMAX */ -#include /* struct stat, lstat() */ -#include /* EX_OSERR, EX_UNAVAILABLE */ -#include /* error() */ -#include /* TEMP_FAILURE_RETRY */ #include /* argz_count(), argz_extract() */ -#include /* va_list, va_start(), ... */ -#include sig_atomic_t interrupted_by_signal = 0; const char *argp_program_version = "plymouth " VERSION; === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2018-02-08 10:23:55 +0000 +++ plugins.d/splashy.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * Splashy - Read a password from splashy and output it * - * Copyright © 2008-2018 Teddy Hogeborn - * Copyright © 2008-2018 Björn Påhlsson + * Copyright © 2008-2018, 2021-2022 Teddy Hogeborn + * Copyright © 2008-2018, 2021-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,40 +23,45 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction, - SIG_IGN, kill(), SIGKILL */ -#include /* NULL */ -#include /* getenv() */ -#include /* asprintf(), vasprintf(), vprintf(), - fprintf() */ -#include /* EXIT_FAILURE, free(), - EXIT_SUCCESS */ -#include /* pid_t, DIR, struct dirent, - ssize_t */ -#include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, strtoimax() */ -#include /* struct stat, lstat(), S_ISLNK */ -#include /* not, or, and */ -#include /* readlink(), fork(), execl(), - sleep(), dup2() STDERR_FILENO, - STDOUT_FILENO, _exit(), - pause() */ -#include /* memcmp(), strerror() */ -#include /* errno, EACCES, ENOTDIR, ELOOP, +#define _GNU_SOURCE /* vasprintf(), + program_invocation_short_name, + asprintf(), TEMP_FAILURE_RETRY() */ +#include /* sig_atomic_t, pid_t, setuid(), + geteuid(), setsid() */ +#include /* va_list, va_start(), vfprintf() */ +#include /* vasprintf(), fprintf(), stderr, + vfprintf(), asprintf() */ +#include /* program_invocation_short_name, + errno, EACCES, ENOTDIR, ELOOP, ENOENT, ENAMETOOLONG, EMFILE, ENFILE, ENOMEM, ENOEXEC, EINVAL, E2BIG, EFAULT, EIO, ETXTBSY, EISDIR, ELIBBAD, EPERM, EINTR, ECHILD */ +#include /* strerror(), memcmp() */ #include /* error() */ +#include /* free(), EXIT_FAILURE, getenv(), + EXIT_SUCCESS, abort() */ +#include /* NULL */ +#include /* DIR, opendir(), struct dirent, + readdir(), closedir() */ +#include /* EX_OSERR, EX_OSFILE, + EX_UNAVAILABLE */ +#include /* intmax_t, strtoimax() */ +#include /* or, not, and */ +#include /* ssize_t, readlink(), fork(), + execl(), _exit(), + TEMP_FAILURE_RETRY(), sleep(), + setuid(), geteuid(), setsid(), + chdir(), dup2(), STDERR_FILENO, + STDOUT_FILENO, pause() */ +#include /* struct stat, lstat(), S_ISLNK() */ +#include /* struct sigaction, sigemptyset(), + sigaddset(), SIGINT, SIGHUP, + SIGTERM, SIG_IGN, kill(), SIGKILL, + SIG_DFL, raise() */ #include /* waitpid(), WIFEXITED(), WEXITSTATUS() */ -#include /* EX_OSERR, EX_OSFILE, - EX_UNAVAILABLE */ -#include /* va_list, va_start(), ... */ sig_atomic_t interrupted_by_signal = 0; int signal_received; === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2018-02-08 10:23:55 +0000 +++ plugins.d/usplash.c 2022-04-24 16:54:30 +0000 @@ -2,8 +2,8 @@ /* * Usplash - Read a password from usplash and output it * - * Copyright © 2008-2018 Teddy Hogeborn - * Copyright © 2008-2018 Björn Påhlsson + * Copyright © 2008-2018, 2021-2022 Teddy Hogeborn + * Copyright © 2008-2018, 2021-2022 Björn Påhlsson * * This file is part of Mandos. * @@ -23,36 +23,42 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - SIG_IGN, kill(), SIGKILL */ +#define _GNU_SOURCE /* vasprintf(), + program_invocation_short_name, + asprintf(), TEMP_FAILURE_RETRY() */ +#include /* sig_atomic_t, pid_t, setuid(), + geteuid(), setsid() */ +#include /* va_list, va_start(), vfprintf() */ +#include /* vasprintf(), fprintf(), stderr, + vfprintf(), asprintf() */ +#include /* program_invocation_short_name, + errno, ENOENT, EINTR */ +#include /* strerror(), strlen(), memcmp() */ +#include /* error() */ +#include /* free(), getenv(), realloc(), + EXIT_FAILURE, EXIT_SUCCESS, + malloc(), abort() */ #include /* bool, false, true */ #include /* open(), O_WRONLY, O_RDONLY */ -#include /* and, or, not*/ -#include /* errno, EINTR */ -#include -#include /* size_t, ssize_t, pid_t, DIR, struct - dirent */ -#include /* NULL */ -#include /* strlen(), memcmp(), strerror() */ -#include /* asprintf(), vasprintf(), vprintf(), - fprintf() */ -#include /* close(), write(), readlink(), - read(), STDOUT_FILENO, sleep(), - fork(), setuid(), geteuid(), - setsid(), chdir(), dup2(), - STDERR_FILENO, execv() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv() */ -#include /* opendir(), readdir(), closedir() */ +#include /* size_t, NULL */ +#include /* close(), ssize_t, write(), + readlink(), read(), STDOUT_FILENO, + sleep(), fork(), setuid(), + geteuid(), setsid(), chdir(), + _exit(), dup2(), STDERR_FILENO, + execv(), TEMP_FAILURE_RETRY(), + pause() */ +#include /* DIR, opendir(), struct dirent, + readdir(), closedir() */ #include /* intmax_t, strtoimax() */ -#include /* struct stat, lstat(), S_ISLNK */ +#include /* or, not, and */ +#include /* struct stat, lstat(), S_ISLNK() */ #include /* EX_OSERR, EX_UNAVAILABLE */ +#include /* struct sigaction, sigemptyset(), + sigaddset(), SIGINT, SIGHUP, + SIGTERM, SIG_IGN, kill(), SIGKILL, + SIG_DFL, raise() */ #include /* argz_count(), argz_extract() */ -#include /* va_list, va_start(), ... */ sig_atomic_t interrupted_by_signal = 0; int signal_received;