=== modified file 'dbus-mandos.conf' --- dbus-mandos.conf 2011-10-02 19:18:24 +0000 +++ dbus-mandos.conf 2026-05-17 23:56:03 +0000 @@ -11,7 +11,7 @@ - + === modified file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2024-09-08 05:08:20 +0000 +++ debian/mandos-client.postinst 2026-05-17 23:56:03 +0000 @@ -55,7 +55,7 @@ fi done fi - + if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then # Make old initrd.img files unreadable too, in case they were # created with mandos-client 1.0.8 or older. === modified file 'debian/mandos-client.templates' --- debian/mandos-client.templates 2020-11-30 16:19:48 +0000 +++ debian/mandos-client.templates 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ Type: note _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. + file, otherwise this computer most likely will not reboot unattended. This option: . ${key_id} === modified file 'debian/mandos.postinst' --- debian/mandos.postinst 2019-08-18 00:05:36 +0000 +++ debian/mandos.postinst 2026-05-17 23:56:03 +0000 @@ -93,10 +93,10 @@ db_stop fi ;; - + abort-upgrade|abort-deconfigure|abort-remove) ;; - + *) echo "$0 called with unknown argument '$1'" 1>&2 exit 1 === modified file 'dracut-module/password-agent.c' --- dracut-module/password-agent.c 2025-06-27 19:49:05 +0000 +++ dracut-module/password-agent.c 2026-05-17 23:56:03 +0000 @@ -146,7 +146,7 @@ struct task_struct *tasks; /* Tasks in this queue */ size_t length; /* Number of tasks */ /* Memory allocated for "tasks", in bytes */ - size_t allocated; + size_t allocated; /* Time when this queue should be run, at the latest */ mono_microsecs next_run; } __attribute__((designated_init)) task_queue; === modified file 'dracut-module/password-agent.xml' --- dracut-module/password-agent.xml 2022-04-24 16:54:30 +0000 +++ dracut-module/password-agent.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -384,10 +384,10 @@ the normal file system: - + &COMMANDNAME; --helper-directory=/usr/lib/x86_64-linux-gnu/mandos/plugin-helpers -- /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --tls-pubkey=/etc/keys/mandos/tls-pubkey.pem --tls-privkey=/etc/keys/mandos/tls-privkey.pem - + === modified file 'intro.xml' --- intro.xml 2022-04-24 16:54:30 +0000 +++ intro.xml 2026-05-17 23:56:03 +0000 @@ -1,7 +1,7 @@ + %common; ]> @@ -45,19 +45,19 @@ - + intro 8mandos - + intro Introduction to the Mandos system - + DESCRIPTION @@ -77,7 +77,7 @@ continue booting normally. - + INTRODUCTION @@ -154,7 +154,7 @@ that server is encrypted.) - + FREQUENTLY ASKED QUESTIONS @@ -189,7 +189,7 @@ and reading it right off the memory bus. - + Replay attacks? @@ -197,7 +197,7 @@ protection against that. - + Man-in-the-middle? @@ -207,7 +207,7 @@ client. - + How about sniffing the network traffic and decrypting it later by physically grabbing the Mandos client and using its @@ -217,7 +217,7 @@ key exchange algorithms in TLS, which protects against this. </para> </refsect2> - + <refsect2 id="physgrab"> <title>Physically grabbing the Mandos server computer? @@ -235,7 +235,7 @@ on one of them. - + Faking checker results? @@ -255,7 +255,7 @@ - + SECURITY @@ -296,7 +296,7 @@ Which it does nicely. - + PLUGINS @@ -419,7 +419,7 @@ BUGS - + SEE ALSO === modified file 'legalnotice.xml' --- legalnotice.xml 2017-08-20 16:20:54 +0000 +++ legalnotice.xml 2026-05-17 23:56:03 +0000 @@ -5,21 +5,21 @@ This manual page is part of Mandos. - + Mandos is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + Mandos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with Mandos. If not, see /etc/mandos/clients.conf"> - + %common; ]> @@ -53,23 +53,23 @@ - + &CONFNAME; 5 - + &CONFNAME; Configuration file for the Mandos server - + &CONFPATH; - + DESCRIPTION @@ -105,7 +105,7 @@ comments. - + OPTIONS @@ -115,9 +115,9 @@ Unknown options are ignored. The used options are as follows: - + - + @@ -136,7 +136,7 @@ - + @@ -154,7 +154,7 @@ - + - + @@ -203,7 +203,7 @@ - + @@ -226,7 +226,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -264,7 +264,7 @@ - + @@ -282,7 +282,7 @@ - + @@ -305,7 +305,7 @@ - + @@ -328,7 +328,7 @@ - + @@ -358,7 +358,7 @@ - + @@ -383,7 +383,7 @@ - + - + - + EXPANSION @@ -471,16 +471,16 @@ mode is needed to expose an error of this kind. - + - + FILES The file described here is &CONFPATH; - + BUGS @@ -495,7 +495,7 @@ - + EXAMPLE @@ -539,7 +539,7 @@ - + SEE ALSO === modified file 'mandos-ctl.xml' --- mandos-ctl.xml 2019-07-29 16:35:53 +0000 +++ mandos-ctl.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -46,19 +46,19 @@ - + &COMMANDNAME; 8 - + &COMMANDNAME; Control or query the operation of the Mandos server - + &COMMANDNAME; @@ -220,7 +220,7 @@ - + DESCRIPTION @@ -234,7 +234,7 @@ deny client requests, and to remove clients from the server. - + PURPOSE @@ -244,10 +244,10 @@ linkend="overview"/> for details. - + OPTIONS - + @@ -258,7 +258,7 @@ - + @@ -269,7 +269,7 @@ - + @@ -281,7 +281,7 @@ - + @@ -291,7 +291,7 @@ - + @@ -300,7 +300,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -319,7 +319,7 @@ - + @@ -334,7 +334,7 @@ - + @@ -362,7 +362,7 @@ - + @@ -377,7 +377,7 @@ - + @@ -392,7 +392,7 @@ - + @@ -405,7 +405,7 @@ - + @@ -418,7 +418,7 @@ - + @@ -433,7 +433,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -458,7 +458,7 @@ - + @@ -468,7 +468,7 @@ - + @@ -479,7 +479,7 @@ - + @@ -489,7 +489,7 @@ - + @@ -499,7 +499,7 @@ - + @@ -510,7 +510,7 @@ - + @@ -519,7 +519,7 @@ - + @@ -528,10 +528,10 @@ - + - + OVERVIEW @@ -541,7 +541,7 @@ clients.conf on the server. - + EXIT STATUS @@ -549,12 +549,12 @@ status will be 0 only if the specified client is enabled. - + BUGS - + EXAMPLE @@ -584,7 +584,7 @@ - + @@ -594,7 +594,7 @@ &COMMANDNAME; --enable --all - + @@ -609,7 +609,7 @@ - + @@ -620,7 +620,7 @@ - + SECURITY @@ -629,7 +629,7 @@ could be configured otherwise by reconfiguring the D-Bus server. - + SEE ALSO @@ -643,7 +643,7 @@ 8 - + === modified file 'mandos-keygen' --- mandos-keygen 2025-11-05 00:32:25 +0000 +++ mandos-keygen 2026-05-17 23:56:03 +0000 @@ -248,7 +248,7 @@ if certtool --generate-privkey --password='' \ --outfile "$TLS_PRIVKEYTMP" --sec-param ultra \ --key-type="$TLS_KEYTYPE" --pkcs8 --no-text 2>/dev/null; then - + # Backup any old key files if cp --backup=numbered --force "$TLS_PRIVKEYFILE" "$TLS_PRIVKEYFILE" \ 2>/dev/null; then === modified file 'mandos-keygen.xml' --- mandos-keygen.xml 2025-11-05 00:32:25 +0000 +++ mandos-keygen.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -48,19 +48,19 @@ - + &COMMANDNAME; 8 - + &COMMANDNAME; Generate key and password for Mandos client and server. - + &COMMANDNAME; @@ -183,7 +183,7 @@ - + DESCRIPTION @@ -204,7 +204,7 @@ 5). - + PURPOSE @@ -214,10 +214,10 @@ linkend="overview"/> for details. - + OPTIONS - + @@ -228,7 +228,7 @@ - + @@ -241,7 +241,7 @@ - + @@ -253,7 +253,7 @@ - + @@ -265,7 +265,7 @@ - + @@ -277,7 +277,7 @@ - + @@ -289,7 +289,7 @@ - + @@ -301,7 +301,7 @@ - + @@ -313,7 +313,7 @@ - + @@ -327,7 +327,7 @@ - + @@ -339,7 +339,7 @@ - + @@ -398,7 +398,7 @@ - + OVERVIEW @@ -408,7 +408,7 @@ inclusion in clients.conf on the server. - + EXIT STATUS @@ -417,7 +417,7 @@ created, otherwise not. - + ENVIRONMENT @@ -433,7 +433,7 @@ - + FILES @@ -487,12 +487,12 @@ - + BUGS - + EXAMPLE @@ -539,7 +539,7 @@ - + SECURITY @@ -554,7 +554,7 @@ 8. - + SEE ALSO @@ -572,7 +572,7 @@ 1 - + === modified file 'mandos-monitor.xml' --- mandos-monitor.xml 2019-02-10 04:20:26 +0000 +++ mandos-monitor.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -46,25 +46,25 @@ - + &COMMANDNAME; 8 - + &COMMANDNAME; Text-based GUI to control the Mandos server. - + &COMMANDNAME; - + DESCRIPTION @@ -74,7 +74,7 @@ >8). - + PURPOSE @@ -84,7 +84,7 @@ linkend="overview"/> for details. - + OVERVIEW @@ -95,7 +95,7 @@ significant events reported by the Mandos server. - + KEYS @@ -197,7 +197,7 @@ - + BUGS @@ -207,7 +207,7 @@ - + EXAMPLE @@ -219,7 +219,7 @@ - + SECURITY @@ -228,7 +228,7 @@ could be configured otherwise by reconfiguring the D-Bus server. - + SEE ALSO @@ -240,7 +240,7 @@ 8 - + === modified file 'mandos-options.xml' --- mandos-options.xml 2019-07-25 21:42:40 +0000 +++ mandos-options.xml 2026-05-17 23:56:03 +0000 @@ -11,7 +11,7 @@
- + <para id="interface"> If this is specified, the server will only announce the service and listen to requests on the specified network interface. @@ -20,7 +20,7 @@ interface is not considered critical, and the server will not exit, but instead continue normally. </para> - + <para id="address"> If this option is used, the server will only listen to the specified IPv6 address. If a link-local address is specified, an @@ -33,19 +33,19 @@ <emphasis>disabled</emphasis> (see below) must this be an IPv4 address.) </para> - + <para id="port"> If this option is used, the server will bind to that port. By default, the server will listen to an arbitrary port given by the operating system. </para> - + <para id="debug"> If the server is run in debug mode, it will run in the foreground and print a lot of debugging information. The default is to <emphasis>not</emphasis> run in debug mode. </para> - + <para id="priority"> GnuTLS priority string for the <acronym>TLS</acronym> handshake. The default is @@ -61,7 +61,7 @@ communication impossible. Changing this option may also make the network traffic decryptable by an attacker. </para> - + <para id="servicename"> Zeroconf service name. The default is <quote><literal>Mandos</literal></quote>. This only needs to be @@ -72,13 +72,13 @@ rename itself to <quote><literal>Mandos #2</literal></quote>, and so on; therefore, this option is not needed in that case. </para> - + <para id="dbus"> This option controls whether the server will provide a D-Bus system bus interface. The default is to provide such an interface. </para> - + <para id="ipv6"> This option controls whether the server will use IPv6 sockets and addresses. The default is to use IPv6. This option should @@ -90,36 +90,36 @@ to the server if this option is turned off. <emphasis>Only advanced users should consider changing this option</emphasis>. </para> - + <para id="restore"> This option controls whether the server will restore its state from the last time it ran. Default is to restore last state. </para> - + <para id="statedir"> Directory to save (and restore) state in. Default is <quote><filename class="directory">/var/lib/mandos</filename></quote>. </para> - + <para id="socket"> If this option is used, the server will not create a new network socket, but will instead use the supplied file descriptor. By default, the server will create a new network socket. </para> - + <para id="foreground"> This option will make the server run in the foreground and not write a PID file. The default is to <emphasis>not</emphasis> run in the foreground, except in <option>debug</option> mode, which implies this option. </para> - + <para id="zeroconf"> This option controls whether the server will announce its existence using Zeroconf. Default is to use Zeroconf. If Zeroconf is not used, a <option>port</option> number or a <option>socket</option> is required. </para> - + </section> === modified file 'mandos.conf.xml' --- mandos.conf.xml 2026-05-17 20:45:21 +0000 +++ mandos.conf.xml 2026-05-17 23:56:03 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ <!ENTITY CONFNAME "mandos.conf"> <!ENTITY CONFPATH "<filename>/etc/mandos/mandos.conf</filename>"> -<!ENTITY TIMESTAMP "2026-05-17"> +<!ENTITY TIMESTAMP "2026-05-18"> <!ENTITY % common SYSTEM "common.ent"> %common; ]> @@ -54,23 +54,23 @@ </copyright> <xi:include href="legalnotice.xml"/> </refentryinfo> - + <refmeta> <refentrytitle>&CONFNAME;</refentrytitle> <manvolnum>5</manvolnum> </refmeta> - + <refnamediv> <refname><filename>&CONFNAME;</filename></refname> <refpurpose> Configuration file for the Mandos server </refpurpose> </refnamediv> - + <refsynopsisdiv> <synopsis>&CONFPATH;</synopsis> </refsynopsisdiv> - + <refsect1 id="description"> <title>DESCRIPTION @@ -88,11 +88,11 @@ # or ; are ignored and may be used to provide comments. - + OPTIONS - + - + @@ -109,7 +109,7 @@ - + @@ -117,7 +117,7 @@ - + - + @@ -136,7 +136,7 @@ - + @@ -145,7 +145,7 @@ xpointer="servicename"/> - + - + - + - + @@ -186,7 +186,7 @@ - + - + @@ -206,7 +206,7 @@ - + - + - + FILES The file described here is &CONFPATH; - + BUGS @@ -237,7 +237,7 @@ - + EXAMPLE @@ -268,7 +268,7 @@ - + SEE ALSO @@ -281,7 +281,7 @@ mandos-clients.conf 5 - + === modified file 'mandos.xml' --- mandos.xml 2025-06-27 19:49:05 +0000 +++ mandos.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -51,19 +51,19 @@ - + &COMMANDNAME; 8 - + &COMMANDNAME; Gives encrypted passwords to authenticated Mandos clients - + &COMMANDNAME; @@ -134,7 +134,7 @@ - + DESCRIPTION @@ -152,7 +152,7 @@ the stored pre-encrypted password for that specific client. - + PURPOSE @@ -162,7 +162,7 @@ linkend="overview"/> for details. - + OPTIONS @@ -175,7 +175,7 @@ - + NAME @@ -185,7 +185,7 @@ - + @@ -195,7 +195,7 @@ - + @@ -205,7 +205,7 @@ - + @@ -215,14 +215,14 @@ - + - + @@ -240,7 +240,7 @@ - + @@ -248,7 +248,7 @@ - + @@ -257,7 +257,7 @@ xpointer="servicename"/> - + @@ -272,7 +272,7 @@ - + @@ -281,7 +281,7 @@ - + @@ -291,14 +291,14 @@ - + - + @@ -308,7 +308,7 @@ - + @@ -316,7 +316,7 @@ - + @@ -324,7 +324,7 @@ - + @@ -332,17 +332,17 @@ xpointer="foreground"/> - + - + - + OVERVIEW @@ -352,7 +352,7 @@ RAM disk environment. - + NETWORK PROTOCOL @@ -410,7 +410,7 @@ - + CHECKING @@ -425,7 +425,7 @@ 5. - + APPROVAL @@ -443,9 +443,9 @@ the server delay before giving a client its secret, allowing optional manual denying of this specific client. - + - + LOGGING @@ -455,7 +455,7 @@ and also show them on the console. - + PERSISTENT STATE @@ -467,7 +467,7 @@ clients.conf, this will take precedence. - + D-BUS INTERFACE @@ -477,7 +477,7 @@ of the D-Bus API, see the file DBUS-API. - + EXIT STATUS @@ -485,7 +485,7 @@ critical error is encountered. - + ENVIRONMENT @@ -505,7 +505,7 @@ - + FILES @@ -580,7 +580,7 @@ - + BUGS @@ -592,7 +592,7 @@ - + EXAMPLE @@ -630,7 +630,7 @@ - + SECURITY @@ -669,7 +669,7 @@ - + SEE ALSO === modified file 'network-hooks.d/wireless' --- network-hooks.d/wireless 2018-02-08 10:23:55 +0000 +++ network-hooks.d/wireless 2026-05-17 23:56:03 +0000 @@ -43,7 +43,7 @@ for KEY in $ifkeys; do ADDRESS=`eval 'echo "$ADDRESS_'"$KEY"\"` INTERFACE=`addrtoif "$ADDRESS"` - + case "$DEVICE" in *,"$INTERFACE"|*,"$INTERFACE",*|"$INTERFACE",*|"$INTERFACE") break 2;; === modified file 'plugin-helpers/mandos-client-iprouteadddel.c' --- plugin-helpers/mandos-client-iprouteadddel.c 2022-04-24 16:54:30 +0000 +++ plugin-helpers/mandos-client-iprouteadddel.c 2026-05-17 23:56:03 +0000 @@ -94,7 +94,7 @@ int fprintf_plus(FILE *stream, const char *format, ...){ va_list ap; va_start(ap, format); - + fprintf(stream, "Mandos plugin helper %s: ", program_invocation_short_name); return vfprintf(stream, format, ap); @@ -117,7 +117,7 @@ struct rtnl_route *route = NULL; struct rtnl_nexthop *nexthop = NULL; struct nl_sock *sk = NULL; - + error_t parse_opt(int key, char *arg, struct argp_state *state){ int lret; errno = 0; @@ -165,12 +165,12 @@ } return errno; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "[ add | delete ] ADDRESS INTERFACE", .doc = "Mandos client helper -- Add or delete" " local route to IP address on interface" }; - + ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments); switch(ret){ case 0: === modified file 'plugin-runner.c' --- plugin-runner.c 2022-04-24 16:54:30 +0000 +++ plugin-runner.c 2026-05-17 23:56:03 +0000 @@ -103,7 +103,7 @@ char **environ; int envc; bool disabled; - + /* Variables used for running processes*/ pid_t pid; int fd; @@ -149,12 +149,12 @@ return NULL; } } - + *new_plugin = (plugin){ .name = copy_name, .argc = 1, .disabled = false, .next = plugin_list }; - + do { new_plugin->argv = malloc(sizeof(char *) * 2); } while(new_plugin->argv == NULL and errno == EINTR); @@ -167,7 +167,7 @@ } new_plugin->argv[0] = copy_name; new_plugin->argv[1] = NULL; - + do { new_plugin->environ = malloc(sizeof(char *)); } while(new_plugin->environ == NULL and errno == EINTR); @@ -180,7 +180,7 @@ return NULL; } new_plugin->environ[0] = NULL; - + /* Append the new plugin to the list */ plugin_list = new_plugin; return new_plugin; @@ -305,7 +305,7 @@ } error(0, errno, "waitpid"); } - + /* A child exited, find it in process_list */ while(proc != NULL and proc->pid != pid){ proc = proc->next; @@ -337,7 +337,7 @@ /* Removes and free a plugin from the plugin list */ __attribute__((nonnull)) static void free_plugin(plugin *plugin_node){ - + for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){ free(*arg); } @@ -348,7 +348,7 @@ } free(plugin_node->environ); free(plugin_node->buffer); - + /* Removes the plugin from the singly-linked list */ if(plugin_node == plugin_list){ /* First one - simple */ @@ -362,7 +362,7 @@ } } } - + free(plugin_node); } @@ -392,7 +392,7 @@ char **custom_argv = NULL; int custom_argc = 0; int dir_fd = -1; - + /* Establish a signal handler */ sigemptyset(&sigchld_action.sa_mask); ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD); @@ -407,7 +407,7 @@ exitstatus = EX_OSERR; goto fallback; } - + /* The options we understand. */ struct argp_option options[] = { { .name = "global-options", .key = 'g', @@ -457,7 +457,7 @@ .doc = "Print program version", .group = -1 }, { .name = NULL } }; - + __attribute__((nonnull(3))) error_t parse_opt(int key, char *arg, struct argp_state *state){ errno = 0; @@ -621,7 +621,7 @@ } return errno; /* Set to 0 at start */ } - + /* This option parser is the same as parse_opt() above, except it ignores everything but the --config-file option. */ error_t parse_opt_config_file(int key, char *arg, @@ -658,12 +658,12 @@ } return errno; } - + struct argp argp = { .options = options, .parser = parse_opt_config_file, .args_doc = "", .doc = "Mandos plugin runner -- Run plugins" }; - + /* Parse using parse_opt_config_file() in order to get the custom config file location, if any. */ ret = argp_parse(&argp, argc, argv, @@ -682,10 +682,10 @@ exitstatus = EX_USAGE; goto fallback; } - + /* Reset to the normal argument parser */ argp.parser = parse_opt; - + /* Open the configfile if available */ if(argfile == NULL){ conffp = fopen(AFILE, "r"); @@ -698,7 +698,7 @@ size_t size = 0; const char whitespace_delims[] = " \r\t\f\v\n"; const char comment_delim[] = "#"; - + custom_argc = 1; custom_argv = malloc(sizeof(char*) * 2); if(custom_argv == NULL){ @@ -708,7 +708,7 @@ } custom_argv[0] = argv[0]; custom_argv[1] = NULL; - + /* for each line in the config file, strip whitespace and ignore commented text */ while(true){ @@ -716,7 +716,7 @@ if(sret == -1){ break; } - + line = org_line; arg = strsep(&line, comment_delim); while((p = strsep(&arg, whitespace_delims)) != NULL){ @@ -730,7 +730,7 @@ free(org_line); goto fallback; } - + custom_argc += 1; { #if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26) @@ -799,7 +799,7 @@ goto fallback; } } - + /* Parse actual command line arguments, to let them override the config file */ ret = argp_parse(&argp, argc, argv, @@ -818,7 +818,7 @@ exitstatus = EX_USAGE; goto fallback; } - + { char *pluginhelperenv; bool bret = true; @@ -836,7 +836,7 @@ free(pluginhelperenv); } } - + if(debug){ for(plugin *p = plugin_list; p != NULL; p = p->next){ fprintf(stderr, "Plugin: %s has %d arguments\n", @@ -850,7 +850,7 @@ } } } - + if(getuid() == 0){ /* Work around Debian bug #633582: */ @@ -883,7 +883,7 @@ } } } - + /* Lower permissions */ ret = setgid(gid); if(ret == -1){ @@ -893,7 +893,7 @@ if(ret == -1){ error(0, errno, "setuid"); } - + /* Open plugin directory with close_on_exec flag */ { dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY | @@ -908,7 +908,7 @@ exitstatus = EX_UNAVAILABLE; goto fallback; } - + #ifndef O_CLOEXEC /* Set the FD_CLOEXEC flag on the directory */ ret = set_cloexec_flag(dir_fd); @@ -919,7 +919,7 @@ } #endif /* O_CLOEXEC */ } - + int good_name(const struct dirent * const dirent){ const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new", "*.dpkg-old", "*.dpkg-bak", @@ -944,7 +944,7 @@ } return 1; } - + int numplugins = scandirat(dir_fd, ".", &direntries, good_name, alphasort); if(numplugins == -1){ @@ -953,12 +953,12 @@ exitstatus = EX_OSERR; goto fallback; } - + FD_ZERO(&rfds_all); - + /* Read and execute any executable in the plugin directory*/ for(int i = 0; i < numplugins; i++){ - + int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY); if(plugin_fd == -1){ error(0, errno, "Could not open plugin"); @@ -972,7 +972,7 @@ free(direntries[i]); continue; } - + /* Ignore non-executable files */ if(not S_ISREG(st.st_mode) or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name, @@ -987,7 +987,7 @@ free(direntries[i]); continue; } - + plugin *p = getplugin(direntries[i]->d_name); if(p == NULL){ error(0, errno, "getplugin"); @@ -1030,7 +1030,7 @@ } } } - + int pipefd[2]; #ifndef O_CLOEXEC ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd)); @@ -1110,13 +1110,13 @@ error(0, errno, "sigprocmask"); _exit(EX_OSERR); } - + ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ if(ret == -1){ error(0, errno, "dup2"); _exit(EX_OSERR); } - + if(fexecve(plugin_fd, p->argv, (p->environ[0] != NULL) ? p->environ : environ) < 0){ error(0, errno, "fexecve for %s/%s", @@ -1143,7 +1143,7 @@ goto fallback; } free(direntries[i]); - + new_plugin->pid = pid; new_plugin->fd = pipefd[0]; @@ -1162,20 +1162,20 @@ exitstatus = EX_OSERR; goto fallback; } - + FD_SET(new_plugin->fd, &rfds_all); - + if(maxfd < new_plugin->fd){ maxfd = new_plugin->fd; } } - + free(direntries); direntries = NULL; close(dir_fd); dir_fd = -1; free_plugin(getplugin(NULL)); - + for(plugin *p = plugin_list; p != NULL; p = p->next){ if(p->pid != 0){ break; @@ -1186,7 +1186,7 @@ free_plugin_list(); } } - + /* Main loop while running plugins exist */ while(plugin_list){ fd_set rfds = rfds_all; @@ -1205,7 +1205,7 @@ if(not WIFEXITED(proc->status) or WEXITSTATUS(proc->status) != 0){ /* Bad exit by plugin */ - + if(debug){ if(WIFEXITED(proc->status)){ fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with" @@ -1220,10 +1220,10 @@ strsignal(WTERMSIG(proc->status))); } } - + /* Remove the plugin */ FD_CLR(proc->fd, &rfds_all); - + /* Block signal while modifying process_list */ ret = (int)TEMP_FAILURE_RETRY(sigprocmask (SIG_BLOCK, @@ -1234,11 +1234,11 @@ exitstatus = EX_OSERR; goto fallback; } - + plugin *next_plugin = proc->next; free_plugin(proc); proc = next_plugin; - + /* We are done modifying process list, so unblock signal */ ret = (int)(TEMP_FAILURE_RETRY (sigprocmask(SIG_UNBLOCK, @@ -1248,16 +1248,16 @@ exitstatus = EX_OSERR; goto fallback; } - + if(plugin_list == NULL){ break; } - + continue; } - + /* This process exited nicely, so print its buffer */ - + bool bret = print_out_password(proc->buffer, proc->buffer_length); if(not bret){ @@ -1266,7 +1266,7 @@ } goto fallback; } - + /* This process has not completed. Does it have any output? */ if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* This process had nothing to say at this time */ @@ -1303,10 +1303,10 @@ } } } - - + + fallback: - + if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS and exitstatus != EX_OK)){ /* Fallback if all plugins failed, none are found or an error @@ -1326,27 +1326,27 @@ exitstatus = EX_IOERR; } } - + /* Restore old signal handler */ ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); if(ret == -1){ error(0, errno, "sigaction"); exitstatus = EX_OSERR; } - + if(custom_argv != NULL){ for(char **arg = custom_argv+1; *arg != NULL; arg++){ free(*arg); } free(custom_argv); } - + free(direntries); - + if(dir_fd != -1){ close(dir_fd); } - + /* Kill the processes */ for(plugin *p = plugin_list; p != NULL; p = p->next){ if(p->pid != 0){ @@ -1358,7 +1358,7 @@ } } } - + /* Wait for any remaining child processes to terminate */ do { ret = wait(NULL); @@ -1366,12 +1366,12 @@ if(errno != ECHILD){ error(0, errno, "wait"); } - + free_plugin_list(); - + free(plugindir); free(pluginhelperdir); free(argfile); - + return exitstatus; } === modified file 'plugin-runner.xml' --- plugin-runner.xml 2025-06-27 19:49:05 +0000 +++ plugin-runner.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -53,19 +53,19 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Run Mandos plugins, pass data from first to succeed. - + &COMMANDNAME; @@ -155,7 +155,7 @@ - + DESCRIPTION @@ -174,7 +174,7 @@ be output on standard output. - + PURPOSE @@ -184,7 +184,7 @@ linkend="overview"/> for details. - + OPTIONS @@ -203,7 +203,7 @@ - + - + @@ -239,7 +239,7 @@ - + - + @@ -281,7 +281,7 @@ - + @@ -296,7 +296,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -322,7 +322,7 @@ - + @@ -335,7 +335,7 @@ - + @@ -350,7 +350,7 @@ - + @@ -362,7 +362,7 @@ - + @@ -381,7 +381,7 @@ - + @@ -391,7 +391,7 @@ - + @@ -400,7 +400,7 @@ - + @@ -412,7 +412,7 @@ - + OVERVIEW @@ -438,7 +438,7 @@ code will make this plugin-runner output the password from that plugin, stop any other plugins, and exit. - + WRITING PLUGINS @@ -481,7 +481,7 @@ - + FALLBACK @@ -493,7 +493,7 @@ from the console. - + EXIT STATUS @@ -503,7 +503,7 @@ case. - + ENVIRONMENT @@ -516,7 +516,7 @@ MANDOSPLUGINHELPERDIR for the plugins. - + FILES @@ -575,7 +575,7 @@ - + BUGS @@ -584,7 +584,7 @@ - + EXAMPLE @@ -600,10 +600,10 @@ Run the program, but not the plugins, in debug mode: - + &COMMANDNAME; --debug - + @@ -612,10 +612,10 @@ debug mode: - + &COMMANDNAME; --options-for=foo:--debug - + @@ -623,10 +623,10 @@ Run all plugins, but not the program, in debug mode: - + &COMMANDNAME; --global-options=--debug - + @@ -674,7 +674,7 @@ linkend="fallback"/>). - + SEE ALSO @@ -694,7 +694,7 @@ 8mandos - + === modified file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2021-02-03 08:33:43 +0000 +++ plugins.d/askpass-fifo.c 2026-05-17 23:56:03 +0000 @@ -59,7 +59,7 @@ va_list ap; char *text; int ret; - + va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); if(ret == -1){ @@ -86,10 +86,10 @@ __attribute__((unused))char **argv){ int ret = 0; ssize_t sret; - + uid = getuid(); gid = getgid(); - + /* Create FIFO */ const char passfifo[] = "/lib/cryptsetup/passfifo"; ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); @@ -115,7 +115,7 @@ break; /* not an error */ } } - + /* Open FIFO */ int fifo_fd = open(passfifo, O_RDONLY); if(fifo_fd == -1){ @@ -137,17 +137,17 @@ return EX_OSFILE; } } - + /* Lower group privileges */ if(setgid(gid) == -1){ error_plus(0, errno, "setgid"); } - + /* Lower user privileges */ if(setuid(uid) == -1){ error_plus(0, errno, "setuid"); } - + /* Read from FIFO */ char *buf = NULL; size_t buf_len = 0; @@ -186,10 +186,10 @@ buf_len += (size_t)sret; } while(sret != 0); } - + /* Close FIFO */ close(fifo_fd); - + /* Print password to stdout */ size_t written = 0; while(written < buf_len){ @@ -214,7 +214,7 @@ written += (size_t)sret; } free(buf); - + ret = close(STDOUT_FILENO); if(ret == -1){ int e = errno; === modified file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2019-02-10 04:20:26 +0000 +++ plugins.d/askpass-fifo.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -47,24 +47,24 @@ Björn Påhlsson - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Mandos plugin to get a password from a FIFO. - + &COMMANDNAME; - + DESCRIPTION @@ -85,14 +85,14 @@ Mandos system. - + OPTIONS This program takes no options. - + EXIT STATUS @@ -103,7 +103,7 @@ be ignored. - + FILES @@ -119,12 +119,12 @@ - + BUGS - + EXAMPLE @@ -142,7 +142,7 @@ - + SECURITY @@ -158,7 +158,7 @@ immediately be shown as output. - + SEE ALSO === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2026-05-17 19:54:20 +0000 +++ plugins.d/mandos-client.c 2026-05-17 23:56:03 +0000 @@ -233,7 +233,7 @@ int fprintf_plus(FILE *stream, const char *format, ...){ va_list ap; va_start (ap, format); - + TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name)); return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap)); @@ -372,7 +372,7 @@ mandos_context *mc){ gpgme_error_t rc; gpgme_engine_info_t engine_info; - + /* * Helper function to insert pub and seckey to the engine keyring. */ @@ -380,13 +380,13 @@ int ret; int fd; gpgme_data_t pgp_data; - + fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); if(fd == -1){ perror_plus("open"); return false; } - + /* Workaround for systems without a real-time clock; see also Debian bug #894495: */ do { @@ -442,7 +442,7 @@ gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - + rc = gpgme_op_import(mc->ctx, pgp_data); if(rc != GPG_ERR_NO_ERROR){ fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n", @@ -524,7 +524,7 @@ return false; } } - + ret = close(fd); if(ret == -1){ perror_plus("close"); @@ -532,11 +532,11 @@ gpgme_data_release(pgp_data); return true; } - + if(debug){ fprintf_plus(stderr, "Initializing GPGME\n"); } - + /* Init GPGME */ gpgme_check_version(NULL); rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); @@ -545,7 +545,7 @@ gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - + /* Set GPGME home directory for the OpenPGP engine only */ rc = gpgme_get_engine_info(&engine_info); if(rc != GPG_ERR_NO_ERROR){ @@ -566,7 +566,7 @@ tempdir); return false; } - + /* Create new GPGME "context" */ rc = gpgme_new(&(mc->ctx)); if(rc != GPG_ERR_NO_ERROR){ @@ -574,11 +574,11 @@ gpgme_strsource(rc), gpgme_strerror(rc)); return false; } - + if(not import_key(pubkey) or not import_key(seckey)){ return false; } - + return true; } @@ -596,11 +596,11 @@ ssize_t ret; size_t plaintext_capacity = 0; ssize_t plaintext_length = 0; - + if(debug){ fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n"); } - + /* Create new GPGME data buffer from memory cryptotext */ rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size, 0); @@ -609,7 +609,7 @@ gpgme_strsource(rc), gpgme_strerror(rc)); return -1; } - + /* Create new empty GPGME data buffer for the plaintext */ rc = gpgme_data_new(&dh_plain); if(rc != GPG_ERR_NO_ERROR){ @@ -618,7 +618,7 @@ gpgme_data_release(dh_crypto); return -1; } - + /* Decrypt data from the cryptotext data buffer to the plaintext data buffer */ rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain); @@ -654,18 +654,18 @@ } goto decrypt_end; } - + if(debug){ fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n"); } - + /* Seek back to the beginning of the GPGME plaintext data buffer */ if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){ perror_plus("gpgme_data_seek"); plaintext_length = -1; goto decrypt_end; } - + *plaintext = NULL; while(true){ plaintext_capacity = incbuffer(plaintext, @@ -676,7 +676,7 @@ plaintext_length = -1; goto decrypt_end; } - + ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length, BUFFER_SIZE); /* Print the data, if any */ @@ -691,7 +691,7 @@ } plaintext_length += ret; } - + if(debug){ fprintf_plus(stderr, "Decrypted password is: "); for(ssize_t i = 0; i < plaintext_length; i++){ @@ -699,12 +699,12 @@ } fprintf(stderr, "\n"); } - + decrypt_end: - + /* Delete the GPGME cryptotext data buffer */ gpgme_data_release(dh_crypto); - + /* Delete the GPGME plaintext data buffer */ gpgme_data_release(dh_plain); return plaintext_length; @@ -736,11 +736,11 @@ const char *dhparamsfilename, mandos_context *mc){ int ret; - + if(debug){ fprintf_plus(stderr, "Initializing GnuTLS\n"); } - + if(debug){ /* "Use a log level over 10 to enable all debugging options." * - GnuTLS manual @@ -748,7 +748,7 @@ gnutls_global_set_log_level(11); gnutls_global_set_log_function(debuggnutls); } - + /* OpenPGP credentials */ ret = gnutls_certificate_allocate_credentials(&mc->cred); if(ret != GNUTLS_E_SUCCESS){ @@ -756,14 +756,14 @@ safer_gnutls_strerror(ret)); return -1; } - + if(debug){ fprintf_plus(stderr, "Attempting to use public key %s and" " private key %s as GnuTLS credentials\n", pubkeyfilename, seckeyfilename); } - + #if GNUTLS_VERSION_NUMBER >= 0x030606 ret = gnutls_certificate_set_rawpk_key_file (mc->cred, pubkeyfilename, seckeyfilename, @@ -791,7 +791,7 @@ safer_gnutls_strerror(ret)); goto globalfail; } - + /* GnuTLS server initialization */ ret = gnutls_dh_params_init(&mc->dh_params); if(ret != GNUTLS_E_SUCCESS){ @@ -982,11 +982,11 @@ gnutls_certificate_set_dh_params(mc->cred, mc->dh_params); } } - + return 0; - + globalfail: - + gnutls_certificate_free_credentials(mc->cred); gnutls_dh_params_deinit(mc->dh_params); return -1; @@ -1015,7 +1015,7 @@ "Error in GnuTLS session initialization: %s\n", safer_gnutls_strerror(ret)); } - + { const char *err; do { @@ -1033,7 +1033,7 @@ return -1; } } - + do { ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, mc->cred); @@ -1048,10 +1048,10 @@ gnutls_deinit(*session); return -1; } - + /* ignore client certificate if any. */ gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE); - + return 0; } @@ -1077,13 +1077,13 @@ } return false; } - + char interface[IF_NAMESIZE]; if(if_indextoname((unsigned int)if_index, interface) == NULL){ perror_plus("if_indextoname"); return false; } - + int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY)); if(devnull == -1){ perror_plus("open(\"/dev/null\", O_RDONLY)"); @@ -1249,14 +1249,14 @@ gnutls_session_t session; int pf; /* Protocol family */ bool route_added = false; - + errno = 0; - + if(quit_now){ errno = EINTR; return -1; } - + switch(af){ case AF_INET6: pf = PF_INET6; @@ -1269,7 +1269,7 @@ errno = EINVAL; return -1; } - + /* If the interface is specified and we have a list of interfaces */ if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){ /* Check if the interface is one of the interfaces we are using */ @@ -1302,17 +1302,17 @@ return -1; } } - + ret = init_gnutls_session(&session, mc); if(ret != 0){ return -1; } - + if(debug){ fprintf_plus(stderr, "Setting up a TCP connection to %s, port %" PRIuMAX "\n", ip, (uintmax_t)port); } - + tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0); if(tcp_sd < 0){ int e = errno; @@ -1320,12 +1320,12 @@ errno = e; goto mandos_end; } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + if(af == AF_INET6){ struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to; *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af }; @@ -1363,12 +1363,12 @@ } else { ((struct sockaddr_in *)&to)->sin_port = htons(port); } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + if(debug){ if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){ char interface[IF_NAMESIZE]; @@ -1403,12 +1403,12 @@ fprintf_plus(stderr, "Canonical address form: %s\n", addrstr); } } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + while(true){ if(af == AF_INET6){ ret = connect(tcp_sd, (struct sockaddr *)&to, @@ -1435,7 +1435,7 @@ connect to a Mandos server announced by Avahi on a server host with a global address. Work around this by retrying with an explicit route added with the server's address. - + Avahi bug reference: https://lists.freedesktop.org/archives/avahi/2010-February/001833.html https://bugs.debian.org/587961 @@ -1458,14 +1458,14 @@ } goto mandos_end; } - + if(quit_now){ errno = EINTR; goto mandos_end; } break; } - + const char *out = mandos_protocol_version; written = 0; while(true){ @@ -1489,33 +1489,33 @@ break; } } - + if(quit_now){ errno = EINTR; goto mandos_end; } } - + if(debug){ fprintf_plus(stderr, "Establishing TLS session with %s\n", ip); } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + /* This casting via intptr_t is to eliminate warning about casting an int to a pointer type. This is exactly how the Guile-GnuTLS function "set-session-transport-fd!" does it. */ gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t)tcp_sd); - + if(quit_now){ errno = EINTR; goto mandos_end; } - + do { ret = gnutls_handshake(session); if(quit_now){ @@ -1523,7 +1523,7 @@ goto mandos_end; } } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - + if(ret != GNUTLS_E_SUCCESS){ if(debug){ fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n"); @@ -1532,21 +1532,21 @@ errno = EPROTO; goto mandos_end; } - + /* Read OpenPGP packet that contains the wanted password */ - + if(debug){ fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from" " %s\n", ip); } - + while(true){ - + if(quit_now){ errno = EINTR; goto mandos_end; } - + buffer_capacity = incbuffer(&buffer, buffer_length, buffer_capacity); if(buffer_capacity == 0){ @@ -1555,12 +1555,12 @@ errno = e; goto mandos_end; } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + sret = gnutls_record_recv(session, buffer+buffer_length, BUFFER_SIZE); if(sret == 0){ @@ -1574,7 +1574,7 @@ case GNUTLS_E_REHANDSHAKE: do { ret = gnutls_handshake(session); - + if(quit_now){ errno = EINTR; goto mandos_end; @@ -1599,16 +1599,16 @@ buffer_length += (size_t) sret; } } - + if(debug){ fprintf_plus(stderr, "Closing TLS session\n"); } - + if(quit_now){ errno = EINTR; goto mandos_end; } - + do { ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); if(quit_now){ @@ -1616,13 +1616,13 @@ goto mandos_end; } } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - + if(buffer_length > 0){ ssize_t decrypted_buffer_size; decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length, &decrypted_buffer, mc); if(decrypted_buffer_size >= 0){ - + clearerr(stdout); written = 0; while(written < (size_t) decrypted_buffer_size){ @@ -1630,7 +1630,7 @@ errno = EINTR; goto mandos_end; } - + ret = (int)fwrite(decrypted_buffer + written, 1, (size_t)decrypted_buffer_size - written, stdout); @@ -1658,9 +1658,9 @@ retval = 0; } } - + /* Shutdown procedure */ - + mandos_end: { if(route_added){ @@ -1708,15 +1708,15 @@ if(r == NULL){ return; } - + /* Called whenever a service has been resolved successfully or timed out */ - + if(quit_now){ avahi_s_service_resolver_free(r); return; } - + switch(event){ default: case AVAHI_RESOLVER_FAILURE: @@ -1726,7 +1726,7 @@ avahi_strerror(avahi_server_errno (((mandos_context*)mc)->server))); break; - + case AVAHI_RESOLVER_FOUND: { char ip[AVAHI_ADDRESS_STR_MAX]; @@ -1768,30 +1768,30 @@ if(b == NULL){ return; } - + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - + if(quit_now){ return; } - + switch(event){ default: case AVAHI_BROWSER_FAILURE: - + fprintf_plus(stderr, "(Avahi browser) %s\n", avahi_strerror(avahi_server_errno (((mandos_context*)mc)->server))); avahi_simple_poll_quit(simple_poll); return; - + case AVAHI_BROWSER_NEW: /* We ignore the returned Avahi resolver object. In the callback function we free it. If the Avahi server is terminated before the callback function is called the Avahi server will free the resolver for us. */ - + if(avahi_s_service_resolver_new(((mandos_context*)mc)->server, interface, protocol, name, type, domain, protocol, 0, @@ -1801,10 +1801,10 @@ avahi_strerror(avahi_server_errno (((mandos_context*)mc)->server))); break; - + case AVAHI_BROWSER_REMOVE: break; - + case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: if(debug){ @@ -1834,7 +1834,7 @@ bool get_flags(const char *ifname, struct ifreq *ifr){ int ret; int old_errno; - + int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); if(s < 0){ old_errno = errno; @@ -1868,7 +1868,7 @@ __attribute__((nonnull, warn_unused_result)) bool good_flags(const char *ifname, const struct ifreq *ifr){ - + /* Reject the loopback device */ if(ifr->ifr_flags & IFF_LOOPBACK){ if(debug){ @@ -1901,7 +1901,7 @@ } return false; } - + /* Accept this device */ if(debug){ fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname); @@ -1919,7 +1919,7 @@ if(if_entry->d_name[0] == '.'){ return 0; } - + struct ifreq ifr; if(not get_flags(if_entry->d_name, &ifr)){ if(debug){ @@ -1928,7 +1928,7 @@ } return 0; } - + if(not good_flags(if_entry->d_name, &ifr)){ return 0; } @@ -1948,7 +1948,7 @@ } return false; } - + return (bool)(ifr.ifr_flags & IFF_UP); } @@ -1965,7 +1965,7 @@ } return false; } - + return (bool)(ifr.ifr_flags & IFF_RUNNING); } @@ -1987,12 +1987,12 @@ int ret; size_t sret; struct stat st; - + if((direntry->d_name)[0] == '\0'){ /* Empty name? */ return 0; } - + sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" @@ -2005,7 +2005,7 @@ } return 0; } - + ret = fstatat(hookdir_fd, direntry->d_name, &st, 0); if(ret == -1){ if(debug){ @@ -2043,7 +2043,7 @@ struct timespec now; struct timespec waited_time; intmax_t block_time; - + while(true){ if(mc->current_server == NULL){ if(debug){ @@ -2074,12 +2074,12 @@ block_time = ((retry_interval - ((intmax_t)waited_time.tv_sec * 1000)) - ((intmax_t)waited_time.tv_nsec / 1000000)); - + if(debug){ fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n", block_time); } - + if(block_time <= 0){ ret = start_mandos_communication(mc->current_server->ip, mc->current_server->port, @@ -2099,7 +2099,7 @@ block_time = 0; /* Call avahi to find new Mandos servers, but don't block */ } - + ret = avahi_simple_poll_iterate(s, (int)block_time); } if(ret != 0){ @@ -2303,12 +2303,12 @@ errno = old_errno; return ENXIO; } - + if(quit_now){ errno = old_errno; return EINTR; } - + if(not interface_is_up(interface)){ int ret_errno = 0; int ioctl_errno = 0; @@ -2320,7 +2320,7 @@ return ret_errno; } network.ifr_flags |= IFF_UP; /* set flag */ - + int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); if(sd == -1){ ret_errno = errno; @@ -2328,7 +2328,7 @@ errno = old_errno; return ret_errno; } - + if(quit_now){ ret = close(sd); if(ret == -1){ @@ -2337,19 +2337,19 @@ errno = old_errno; return EINTR; } - + if(debug){ fprintf_plus(stderr, "Bringing up interface \"%s\"\n", interface); } - + /* Raise privileges */ ret_errno = raise_privileges(); if(ret_errno != 0){ errno = ret_errno; perror_plus("Failed to raise privileges"); } - + #ifdef __linux__ int ret_linux; bool restore_loglevel = false; @@ -2374,7 +2374,7 @@ } } #endif /* __linux__ */ - + /* If raise_privileges() succeeded above */ if(ret_errno == 0){ /* Lower privileges */ @@ -2384,13 +2384,13 @@ perror_plus("Failed to lower privileges"); } } - + /* Close the socket */ ret = close(sd); if(ret == -1){ perror_plus("close"); } - + if(ret_setflags == -1){ errno = ioctl_errno; perror_plus("ioctl SIOCSIFFLAGS +IFF_UP"); @@ -2401,7 +2401,7 @@ fprintf_plus(stderr, "Interface \"%s\" is already up; good\n", interface); } - + /* Sleep checking until interface is running. Check every 0.25s, up to total time of delay */ for(int i = 0; i < delay * 4; i++){ @@ -2414,7 +2414,7 @@ perror_plus("nanosleep"); } } - + errno = old_errno; return 0; } @@ -2440,7 +2440,7 @@ return ret_errno; } network.ifr_flags &= ~(short)IFF_UP; /* clear flag */ - + int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); if(sd == -1){ ret_errno = errno; @@ -2448,22 +2448,22 @@ errno = old_errno; return ret_errno; } - + if(debug){ fprintf_plus(stderr, "Taking down interface \"%s\"\n", interface); } - + /* Raise privileges */ ret_errno = raise_privileges(); if(ret_errno != 0){ errno = ret_errno; perror_plus("Failed to raise privileges"); } - + int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network); ioctl_errno = errno; - + /* If raise_privileges() succeeded above */ if(ret_errno == 0){ /* Lower privileges */ @@ -2473,13 +2473,13 @@ perror_plus("Failed to lower privileges"); } } - + /* Close the socket */ int ret = close(sd); if(ret == -1){ perror_plus("close"); } - + if(ret_setflags == -1){ errno = ioctl_errno; perror_plus("ioctl SIOCSIFFLAGS -IFF_UP"); @@ -2490,7 +2490,7 @@ fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n", interface); } - + errno = old_errno; return 0; } @@ -2529,37 +2529,37 @@ #endif const char *dh_params_file = NULL; char *interfaces_hooks = NULL; - + bool gnutls_initialized = false; bool gpgme_initialized = false; float delay = 2.5f; double retry_interval = 10; /* 10s between trying a server and retrying the same server again */ - + struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL }; struct sigaction sigterm_action = { .sa_handler = handle_sigterm }; - + uid = getuid(); gid = getgid(); - + /* Lower any group privileges we might have, just to be safe */ errno = 0; ret = setgid(gid); if(ret == -1){ perror_plus("setgid"); } - + /* Lower user privileges (temporarily) */ errno = 0; ret = seteuid(uid); if(ret == -1){ perror_plus("seteuid"); } - + if(quit_now){ goto end; } - + { struct argp_option options[] = { { .name = "debug", .key = 128, @@ -2634,7 +2634,7 @@ .doc = "Print program version", .group = -1 }, { .name = NULL } }; - + error_t parse_opt(int key, char *arg, struct argp_state *state){ errno = 0; @@ -2722,7 +2722,7 @@ } return errno; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos client -- Get and decrypt" @@ -2743,7 +2743,7 @@ goto end; } } - + { /* Re-raise privileges */ ret = raise_privileges(); @@ -2752,7 +2752,7 @@ perror_plus("Failed to raise privileges"); } else { struct stat st; - + /* Work around Debian bug #633582: */ @@ -2776,7 +2776,7 @@ close(seckey_fd); } } - + if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){ int pubkey_fd = open(pubkey, O_RDONLY); if(pubkey_fd == -1){ @@ -2797,7 +2797,7 @@ close(pubkey_fd); } } - + if(dh_params_file != NULL and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){ int dhparams_fd = open(dh_params_file, O_RDONLY); @@ -2819,7 +2819,7 @@ close(dhparams_fd); } } - + /* Work around Debian bug #981302 */ if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){ @@ -2837,7 +2837,7 @@ } } } - + /* Remove invalid interface names (except "none") */ { char *interface = NULL; @@ -2854,7 +2854,7 @@ } } } - + /* Run network hooks */ { if(mc.interfaces != NULL){ @@ -2869,11 +2869,11 @@ run_network_hooks("start", interfaces_hooks != NULL ? interfaces_hooks : "", delay); } - + if(not debug){ avahi_set_log_function(empty_log); } - + /* Initialize Avahi early so avahi_simple_poll_quit() can be called from the signal handler */ /* Initialize the pseudo-RNG for Avahi */ @@ -2885,7 +2885,7 @@ exitcode = EX_UNAVAILABLE; goto end; } - + sigemptyset(&sigterm_action.sa_mask); ret = sigaddset(&sigterm_action.sa_mask, SIGINT); if(ret == -1){ @@ -2948,7 +2948,7 @@ goto end; } } - + /* If no interfaces were specified, make a list */ if(mc.interfaces == NULL){ struct dirent **direntries = NULL; @@ -2982,7 +2982,7 @@ goto end; } } - + /* Bring up interfaces which are down, and remove any "none"s */ { char *interface = NULL; @@ -3024,7 +3024,7 @@ fprintf_plus(stderr, "No interfaces were brought up\n"); } } - + /* If we only got one interface, explicitly use only that one */ if(argz_count(mc.interfaces, mc.interfaces_size) == 1){ if(debug){ @@ -3033,11 +3033,11 @@ } if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces); } - + if(quit_now){ goto end; } - + #if GNUTLS_VERSION_NUMBER >= 0x030606 ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc); #elif GNUTLS_VERSION_NUMBER < 0x030600 @@ -3052,11 +3052,11 @@ } else { gnutls_initialized = true; } - + if(quit_now){ goto end; } - + /* Try /run/tmp before /tmp */ tempdir = mkdtemp(run_tempdir); if(tempdir == NULL and errno == ENOENT){ @@ -3070,11 +3070,11 @@ perror_plus("mkdtemp"); goto end; } - + if(quit_now){ goto end; } - + if(not init_gpgme(pubkey, seckey, tempdir, &mc)){ fprintf_plus(stderr, "init_gpgme failed\n"); exitcode = EX_UNAVAILABLE; @@ -3082,26 +3082,26 @@ } else { gpgme_initialized = true; } - + if(quit_now){ goto end; } - + if(connect_to != NULL){ /* Connect directly, do not use Zeroconf */ /* (Mainly meant for debugging) */ char *address = strrchr(connect_to, ':'); - + if(address == NULL){ fprintf_plus(stderr, "No colon in address\n"); exitcode = EX_USAGE; goto end; } - + if(quit_now){ goto end; } - + in_port_t port; errno = 0; tmpmax = strtoimax(address+1, &tmp, 10); @@ -3111,11 +3111,11 @@ exitcode = EX_USAGE; goto end; } - + if(quit_now){ goto end; } - + port = (in_port_t)tmpmax; *address = '\0'; /* Colon in address indicates IPv6 */ @@ -3132,11 +3132,11 @@ af = AF_INET; } address = connect_to; - + if(quit_now){ goto end; } - + while(not quit_now){ ret = start_mandos_communication(address, port, if_index, af, &mc); @@ -3149,18 +3149,18 @@ } sleep((unsigned int)retry_interval); } - + if(not quit_now){ exitcode = EXIT_SUCCESS; } - + goto end; } - + if(quit_now){ goto end; } - + { AvahiServerConfig config; /* Do not publish any local Zeroconf records */ @@ -3169,15 +3169,15 @@ config.publish_addresses = 0; config.publish_workstation = 0; config.publish_domain = 0; - + /* Allocate a new server */ mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &ret); - + /* Free the Avahi configuration data */ avahi_server_config_free(&config); } - + /* Check if creating the Avahi server object succeeded */ if(mc.server == NULL){ fprintf_plus(stderr, "Failed to create Avahi server: %s\n", @@ -3185,11 +3185,11 @@ exitcode = EX_UNAVAILABLE; goto end; } - + if(quit_now){ goto end; } - + /* Create the Avahi service browser */ sb = avahi_s_service_browser_new(mc.server, if_index, AVAHI_PROTO_UNSPEC, "_mandos._tcp", @@ -3201,26 +3201,26 @@ exitcode = EX_UNAVAILABLE; goto end; } - + if(quit_now){ goto end; } - + /* Run the main loop */ - + if(debug){ fprintf_plus(stderr, "Starting Avahi loop search\n"); } - + ret = avahi_loop_with_timeout(simple_poll, (int)(retry_interval * 1000), &mc); if(debug){ fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n", (ret == 0) ? "successfully" : "with error"); } - + end: - + if(debug){ if(signal_received){ fprintf_plus(stderr, "%s exiting due to signal %d: %s\n", @@ -3230,28 +3230,28 @@ fprintf_plus(stderr, "%s exiting\n", argv[0]); } } - + /* Cleanup things */ free(mc.interfaces); - + if(sb != NULL) avahi_s_service_browser_free(sb); - + if(mc.server != NULL) avahi_server_free(mc.server); - + if(simple_poll != NULL) avahi_simple_poll_free(simple_poll); - + if(gnutls_initialized){ gnutls_certificate_free_credentials(mc.cred); gnutls_dh_params_deinit(mc.dh_params); } - + if(gpgme_initialized){ gpgme_release(mc.ctx); } - + /* Cleans up the circular linked list of Mandos servers the client has seen */ if(mc.current_server != NULL){ @@ -3270,7 +3270,7 @@ mc.current_server = next; } } - + /* Re-raise privileges */ { ret = raise_privileges(); @@ -3278,11 +3278,11 @@ errno = ret; perror_plus("Failed to raise privileges"); } else { - + /* Run network hooks */ run_network_hooks("stop", interfaces_hooks != NULL ? interfaces_hooks : "", delay); - + /* Take down the network interfaces which were brought up */ { char *interface = NULL; @@ -3301,17 +3301,17 @@ } } } - + ret = lower_privileges_permanently(); if(ret != 0){ errno = ret; perror_plus("Failed to lower privileges permanently"); } } - + free(interfaces_to_take_down); free(interfaces_hooks); - + void clean_dir_at(int base, const char * const dirname, uintmax_t level){ struct dirent **direntries = NULL; @@ -3338,7 +3338,7 @@ if(errno == EISDIR){ dret = unlinkat(dir_fd, direntries[i]->d_name, AT_REMOVEDIR); - } + } if((dret == -1) and (errno == ENOTEMPTY) and (strcmp(direntries[i]->d_name, "private-keys-v1.d") == 0) and (level == 0)){ @@ -3353,7 +3353,7 @@ } free(direntries[i]); } - + /* need to clean even if 0 because man page doesn't specify */ free(direntries); dret = unlinkat(base, dirname, AT_REMOVEDIR); @@ -3365,12 +3365,12 @@ } close(dir_fd); } - + /* Removes the GPGME temp directory and all files inside */ if(tempdir != NULL){ clean_dir_at(-1, tempdir, 0); } - + if(quit_now){ sigemptyset(&old_sigterm_action.sa_mask); old_sigterm_action.sa_handler = SIG_DFL; @@ -3389,6 +3389,6 @@ } TEMP_FAILURE_RETRY(pause()); } - + return exitcode; } === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2025-06-27 19:49:05 +0000 +++ plugins.d/mandos-client.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -53,19 +53,19 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Client for Mandos - + &COMMANDNAME; @@ -163,7 +163,7 @@ - + DESCRIPTION @@ -206,7 +206,7 @@ linkend="overview"/>. - + PURPOSE @@ -216,7 +216,7 @@ linkend="overview"/> for details. - + OPTIONS @@ -226,7 +226,7 @@ accepts are therefore normally provided by the invoking program, and not directly. - + - + - + @@ -305,7 +305,7 @@ - + @@ -319,7 +319,7 @@ - + @@ -356,7 +356,7 @@ xpointer="priority"/> - + @@ -371,7 +371,7 @@ - + @@ -429,7 +429,7 @@ - + @@ -445,7 +445,7 @@ - + @@ -455,7 +455,7 @@ - + @@ -464,7 +464,7 @@ - + @@ -476,7 +476,7 @@ - + OVERVIEW @@ -515,7 +515,7 @@ at all. - + EXIT STATUS @@ -528,7 +528,7 @@ get a decryptable password and print it. - + ENVIRONMENT @@ -551,7 +551,7 @@ . - + NETWORK HOOKS @@ -720,7 +720,7 @@ - + FILES @@ -765,12 +765,12 @@ - + BUGS - + EXAMPLE @@ -824,7 +824,7 @@ - + SECURITY @@ -872,7 +872,7 @@ confidential. - + SEE ALSO === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2022-04-24 16:54:30 +0000 +++ plugins.d/password-prompt.c 2026-05-17 23:56:03 +0000 @@ -86,7 +86,7 @@ va_list ap; char *text; int ret; - + va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); if(ret == -1){ @@ -119,7 +119,7 @@ if(access("/run/plymouth/pid", R_OK) == 0){ return true; } - + __attribute__((nonnull)) int is_plymouth(const struct dirent *proc_entry){ int ret; @@ -129,13 +129,13 @@ char *tmp; errno = 0; proc_id = strtoumax(proc_entry->d_name, &tmp, 10); - + if(errno != 0 or *tmp != '\0' or proc_id != (uintmax_t)((pid_t)proc_id)){ return 0; } } - + char *cmdline_filename; ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_entry->d_name); @@ -143,7 +143,7 @@ error_plus(0, errno, "asprintf"); return 0; } - + /* Open /proc//cmdline */ cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); @@ -153,7 +153,7 @@ } return 0; } - + char *cmdline = NULL; { size_t cmdline_len = 0; @@ -174,7 +174,7 @@ cmdline = tmp; cmdline_allocated += blocksize; } - + /* Read data */ sret = read(cl_fd, cmdline + cmdline_len, cmdline_allocated - cmdline_len); @@ -195,7 +195,7 @@ cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */ } /* we now have cmdline */ - + /* get basename */ char *cmdline_base = strrchr(cmdline, '/'); if(cmdline_base != NULL){ @@ -203,7 +203,7 @@ } else { cmdline_base = cmdline; } - + if(strcmp(cmdline_base, plymouth_name) != 0){ if(debug){ fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base, @@ -219,7 +219,7 @@ free(cmdline); return 1; } - + struct dirent **direntries = NULL; int ret; ret = scandir("/proc", &direntries, is_plymouth, alphasort); @@ -270,7 +270,7 @@ .doc = "Print program version", .group = -1 }, { .name = NULL } }; - + __attribute__((nonnull(3))) error_t parse_opt (int key, char *arg, struct argp_state *state){ errno = 0; @@ -305,7 +305,7 @@ } return errno; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos password-prompt -- Read and" @@ -324,7 +324,7 @@ return EX_USAGE; } } - + if(debug){ fprintf(stderr, "Starting %s\n", argv[0]); } @@ -335,11 +335,11 @@ } return EXIT_FAILURE; } - + if(debug){ fprintf(stderr, "Storing current terminal attributes\n"); } - + if(tcgetattr(STDIN_FILENO, &t_old) != 0){ int e = errno; error_plus(0, errno, "tcgetattr"); @@ -351,7 +351,7 @@ return EX_OSERR; } } - + sigemptyset(&new_action.sa_mask); ret = sigaddset(&new_action.sa_mask, SIGINT); if(ret == -1){ @@ -408,12 +408,12 @@ return EX_OSERR; } } - - + + if(debug){ fprintf(stderr, "Removing echo flag from terminal attributes\n"); } - + t_new = t_old; t_new.c_lflag &= ~(tcflag_t)ECHO; if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ @@ -428,7 +428,7 @@ return EX_OSERR; } } - + if(debug){ fprintf(stderr, "Waiting for input from stdin \n"); } @@ -555,16 +555,16 @@ fprintf(stderr, "getline() returned 0, retrying.\n"); } } - + free(buffer); - + if(debug){ fprintf(stderr, "Restoring terminal attributes\n"); } if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ error_plus(0, errno, "tcsetattr+echo"); } - + if(quit_now){ sigemptyset(&old_action.sa_mask); old_action.sa_handler = SIG_DFL; @@ -574,7 +574,7 @@ } raise(signal_received); } - + if(debug){ fprintf(stderr, "%s is exiting with status %d\n", argv[0], status); @@ -582,6 +582,6 @@ if(status == EXIT_SUCCESS or status == EX_OK){ fputc('\n', stderr); } - + return status; } === modified file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2019-07-27 10:11:45 +0000 +++ plugins.d/password-prompt.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -48,17 +48,17 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Prompt for a password and output it. - + &COMMANDNAME; @@ -93,7 +93,7 @@ - + DESCRIPTION @@ -123,7 +123,7 @@ this process will immediately exit without doing anything. - + OPTIONS @@ -135,7 +135,7 @@ are therefore normally provided by the plugin runner, and not directly. - + - + @@ -160,7 +160,7 @@ - + @@ -171,7 +171,7 @@ - + @@ -181,7 +181,7 @@ - + @@ -190,7 +190,7 @@ - + @@ -202,7 +202,7 @@ - + EXIT STATUS @@ -212,7 +212,7 @@ corrupt and/or truncated, and should therefore be ignored. - + ENVIRONMENT @@ -242,12 +242,12 @@ - + BUGS - + EXAMPLE @@ -287,7 +287,7 @@ - + SECURITY @@ -312,7 +312,7 @@ features. - + SEE ALSO === modified file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2025-06-27 19:49:05 +0000 +++ plugins.d/plymouth.c 2026-05-17 23:56:03 +0000 @@ -114,7 +114,7 @@ va_list ap; char *text; int ret; - + va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); if(ret == -1){ @@ -139,7 +139,7 @@ const char *const crypttarget = getenv("crypttarget"); const char prompt_start[] = "Unlocking the disk"; const char prompt_end[] = "Enter passphrase"; - + if(cryptsource == NULL){ if(crypttarget == NULL){ ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end); @@ -172,7 +172,7 @@ if(ret == -1){ error_plus(0, errno, "setuid"); } - + setsid(); ret = chdir("/"); if(ret == -1){ @@ -213,7 +213,7 @@ _exit(EX_OSERR); } } - + char **new_argv = malloc(sizeof(const char *)); if(new_argv == NULL){ error_plus(0, errno, "malloc"); @@ -243,7 +243,7 @@ new_argv[i] = strdup(argv[i]); } new_argv[i] = NULL; - + execv(path, (char *const *)new_argv); error_plus(0, errno, "execv"); _exit(EXIT_FAILURE); @@ -302,7 +302,7 @@ error_plus(0, errno, "asprintf"); return 0; } - + struct stat exe_stat; ret = lstat(exe_link, &exe_stat); if(ret == -1){ @@ -312,14 +312,14 @@ } return 0; } - + if(not S_ISLNK(exe_stat.st_mode) or exe_stat.st_uid != 0 or exe_stat.st_gid != 0){ free(exe_link); return 0; } - + ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target)); free(exe_link); if((sret != (ssize_t)sizeof(plymouthd_path)-1) or @@ -392,7 +392,7 @@ if((uintmax_t)pid == proc_id){ return pid; } - + return 0; } @@ -401,14 +401,14 @@ char *cmdline_filename; ssize_t sret; int ret; - + ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline", (uintmax_t)pid); if(ret == -1){ error_plus(0, errno, "asprintf"); return NULL; } - + /* Open /proc//cmdline */ cl_fd = open(cmdline_filename, O_RDONLY); free(cmdline_filename); @@ -416,7 +416,7 @@ error_plus(0, errno, "open"); return NULL; } - + size_t cmdline_allocated = 0; size_t cmdline_len = 0; char *cmdline = NULL; @@ -435,7 +435,7 @@ cmdline = tmp; cmdline_allocated += blocksize; } - + /* Read data */ sret = read(cl_fd, cmdline + cmdline_len, cmdline_allocated - cmdline_len); @@ -453,7 +453,7 @@ free(cmdline); return NULL; } - + /* we got cmdline and cmdline_len, ignore rest... */ char **argv = malloc((argz_count(cmdline, cmdline_len) + 1) * sizeof(char *)); /* Get number of args */ @@ -482,7 +482,7 @@ .doc = "Debug mode" }, { .name = NULL } }; - + __attribute__((nonnull(3))) error_t parse_opt (int key, char *arg, __attribute__((unused)) struct argp_state *state){ @@ -502,7 +502,7 @@ } return errno; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos plymouth -- Read and" @@ -521,7 +521,7 @@ return EX_USAGE; } } - + /* test -x /bin/plymouth */ ret = access(plymouth_path, X_OK); if(ret == -1){ @@ -533,7 +533,7 @@ } exit(EX_UNAVAILABLE); } - + { /* Add signal handlers */ struct sigaction old_action, new_action = { .sa_handler = termination_handler, @@ -557,7 +557,7 @@ } } } - + /* plymouth --ping */ bret = exec_and_wait(&plymouth_command_pid, plymouth_path, (const char *[]) @@ -575,7 +575,7 @@ } exit(EX_UNAVAILABLE); } - + if(prompt != NULL){ ret = asprintf(&prompt_arg, "--prompt=%s", prompt); } else { @@ -586,7 +586,7 @@ if(ret == -1){ error_plus(EX_OSERR, errno, "asprintf"); } - + /* plymouth ask-for-password --prompt="$prompt" */ if(debug){ fprintf_plus(stderr, "Prompting for password via Plymouth\n"); @@ -605,7 +605,7 @@ exit(EXIT_FAILURE); } kill_and_wait(plymouth_command_pid); - + char **plymouthd_argv = NULL; pid_t pid = get_pid(); if(pid == 0){ @@ -613,7 +613,7 @@ } else { plymouthd_argv = getargv(pid); } - + bret = exec_and_wait(NULL, plymouth_path, (const char *[]) { plymouth_path, "quit", NULL }, false, false); === modified file 'plugins.d/plymouth.xml' --- plugins.d/plymouth.xml 2019-07-27 10:11:45 +0000 +++ plugins.d/plymouth.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -46,18 +46,18 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Mandos plugin to use plymouth to get a password. - + &COMMANDNAME; @@ -85,7 +85,7 @@ - + DESCRIPTION @@ -120,7 +120,7 @@ arguments as the old one was using. - + OPTIONS @@ -132,7 +132,7 @@ are therefore normally provided by the plugin runner, and not directly. - + - + @@ -156,7 +156,7 @@ - + @@ -166,7 +166,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -187,7 +187,7 @@ - + EXIT STATUS @@ -198,7 +198,7 @@ be ignored. - + ENVIRONMENT @@ -228,7 +228,7 @@ - + FILES @@ -273,7 +273,7 @@ - + BUGS @@ -284,7 +284,7 @@ - + EXAMPLE @@ -310,7 +310,7 @@ - + SECURITY @@ -348,7 +348,7 @@ immediately be shown as output. - + SEE ALSO === modified file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2022-04-24 16:54:30 +0000 +++ plugins.d/splashy.c 2026-05-17 23:56:03 +0000 @@ -73,7 +73,7 @@ va_list ap; char *text; int ret; - + va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); if(ret == -1){ @@ -107,14 +107,14 @@ pid_t splashy_pid = 0; pid_t splashy_command_pid = 0; int exitstatus = EXIT_FAILURE; - + /* Create prompt string */ { const char *const cryptsource = getenv("cryptsource"); const char *const crypttarget = getenv("crypttarget"); const char *const prompt_start = "getpass " "Enter passphrase to unlock the disk"; - + if(cryptsource == NULL){ if(crypttarget == NULL){ ret = asprintf(&prompt, "%s: ", prompt_start); @@ -136,7 +136,7 @@ goto failure; } } - + /* Find splashy process */ { const char splashy_name[] = "/sbin/splashy"; @@ -189,7 +189,7 @@ exitstatus = EX_OSERR; goto failure; } - + /* Check that it refers to a symlink owned by root:root */ struct stat exe_stat; ret = lstat(exe_link, &exe_stat); @@ -220,7 +220,7 @@ free(exe_link); continue; } - + sret = readlink(exe_link, exe_target, sizeof(exe_target)); free(exe_link); } @@ -238,7 +238,7 @@ exitstatus = EX_UNAVAILABLE; goto failure; } - + /* Set up the signal handler */ { struct sigaction old_action, @@ -306,11 +306,11 @@ } } } - + if(interrupted_by_signal){ goto failure; } - + /* Fork off the splashy command to prompt for password */ splashy_command_pid = fork(); if(splashy_command_pid != 0 and interrupted_by_signal){ @@ -357,15 +357,15 @@ free(prompt); _exit(EXIT_FAILURE); } - + /* Parent */ free(prompt); prompt = NULL; - + if(interrupted_by_signal){ goto failure; } - + /* Wait for command to complete */ { int status; @@ -389,18 +389,18 @@ } } } - + failure: - + free(prompt); - + if(proc_dir != NULL){ TEMP_FAILURE_RETRY(closedir(proc_dir)); } - + if(splashy_command_pid != 0){ TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM)); - + TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM)); sleep(2); while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){ @@ -410,14 +410,14 @@ pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork()); if(new_splashy_pid == 0){ /* Child; will become new splashy process */ - + /* Make the effective user ID (root) the only user ID instead of the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ error_plus(0, errno, "setuid"); } - + setsid(); ret = chdir("/"); if(ret == -1){ @@ -431,7 +431,7 @@ error_plus(0, errno, "dup2"); _exit(EX_OSERR); } - + execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); { int e = errno; @@ -453,7 +453,7 @@ } } } - + if(interrupted_by_signal){ struct sigaction signal_action; sigemptyset(&signal_action.sa_mask); @@ -472,6 +472,6 @@ } TEMP_FAILURE_RETRY(pause()); } - + return exitstatus; } === modified file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2019-07-27 10:11:45 +0000 +++ plugins.d/splashy.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -48,24 +48,24 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Mandos plugin to use splashy to get a password. - + &COMMANDNAME; - + DESCRIPTION @@ -100,14 +100,14 @@ >boot as the only argument. - + OPTIONS This program takes no options. - + EXIT STATUS @@ -118,7 +118,7 @@ be ignored. - + ENVIRONMENT @@ -146,7 +146,7 @@ - + FILES @@ -193,7 +193,7 @@ - + BUGS @@ -204,7 +204,7 @@ - + EXAMPLE @@ -222,7 +222,7 @@ - + SECURITY @@ -260,7 +260,7 @@ immediately be shown as output. - + SEE ALSO === modified file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2022-04-24 16:54:30 +0000 +++ plugins.d/usplash.c 2026-05-17 23:56:03 +0000 @@ -71,7 +71,7 @@ va_list ap; char *text; int ret; - + va_start(ap, formatstring); ret = vasprintf(&text, formatstring, ap); if(ret == -1){ @@ -112,7 +112,7 @@ } *fifo_fd_r = ret; } - + const char *cmd_line; size_t cmd_line_len; char *cmd_line_alloc = NULL; @@ -132,7 +132,7 @@ cmd_line = cmd_line_alloc; cmd_line_len = (size_t)ret + 1; } - + size_t written = 0; ssize_t sret = 0; while(written < cmd_line_len){ @@ -148,7 +148,7 @@ written += (size_t)sret; } free(cmd_line_alloc); - + return true; } @@ -159,7 +159,7 @@ const char *const cryptsource = getenv("cryptsource"); const char *const crypttarget = getenv("crypttarget"); const char prompt_start[] = "Enter passphrase to unlock the disk"; - + if(cryptsource == NULL){ if(crypttarget == NULL){ ret = asprintf(&prompt, "%s: ", prompt_start); @@ -219,7 +219,7 @@ error_plus(0, errno, "asprintf"); goto fail_find_usplash; } - + /* Check that it refers to a symlink owned by root:root */ struct stat exe_stat; ret = lstat(exe_link, &exe_stat); @@ -238,7 +238,7 @@ free(exe_link); continue; } - + sret = readlink(exe_link, exe_target, sizeof(exe_target)); free(exe_link); } @@ -311,9 +311,9 @@ *cmdline_len_r = cmdline_len; return pid; } - + fail_find_usplash: - + free(cmdline); if(proc_dir != NULL){ int e = errno; @@ -334,13 +334,13 @@ pid_t usplash_pid = -1; bool usplash_accessed = false; int status = EXIT_FAILURE; /* Default failure exit status */ - + char *prompt = makeprompt(); if(prompt == NULL){ status = EX_OSERR; goto failure; } - + /* Find usplash process */ char *cmdline = NULL; size_t cmdline_len = 0; @@ -349,7 +349,7 @@ status = EX_UNAVAILABLE; goto failure; } - + /* Set up the signal handler */ { struct sigaction old_action, @@ -429,7 +429,7 @@ } } } - + usplash_accessed = true; /* Write command to FIFO */ if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){ @@ -439,11 +439,11 @@ } goto failure; } - + if(interrupted_by_signal){ goto failure; } - + if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){ if(errno != EINTR){ error_plus(0, errno, "usplash_write"); @@ -451,14 +451,14 @@ } goto failure; } - + if(interrupted_by_signal){ goto failure; } - + free(prompt); prompt = NULL; - + /* Read reply from usplash */ /* Open FIFO */ outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); @@ -469,11 +469,11 @@ } goto failure; } - + if(interrupted_by_signal){ goto failure; } - + /* Read from FIFO */ size_t buf_allocated = 0; const size_t blocksize = 1024; @@ -504,7 +504,7 @@ if(interrupted_by_signal){ break; } - + buf_len += (size_t)sret; } while(sret != 0); ret = close(outfifo_fd); @@ -516,11 +516,11 @@ goto failure; } outfifo_fd = -1; - + if(interrupted_by_signal){ goto failure; } - + if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){ if(errno != EINTR){ error_plus(0, errno, "usplash_write"); @@ -528,11 +528,11 @@ } goto failure; } - + if(interrupted_by_signal){ goto failure; } - + ret = close(fifo_fd); if(ret == -1){ if(errno != EINTR){ @@ -542,7 +542,7 @@ goto failure; } fifo_fd = -1; - + /* Print password to stdout */ size_t written = 0; while(written < buf_len){ @@ -556,7 +556,7 @@ goto failure; } } while(sret == -1); - + if(interrupted_by_signal){ goto failure; } @@ -564,25 +564,25 @@ } free(buf); buf = NULL; - + if(interrupted_by_signal){ goto failure; } - + free(cmdline); return EXIT_SUCCESS; - + failure: - + free(buf); - + free(prompt); - + /* If usplash was never accessed, we can stop now */ if(not usplash_accessed){ return status; } - + /* Close FIFO */ if(fifo_fd != -1){ ret = close(fifo_fd); @@ -591,7 +591,7 @@ } fifo_fd = -1; } - + /* Close output FIFO */ if(outfifo_fd != -1){ ret = close(outfifo_fd); @@ -599,7 +599,7 @@ error_plus(0, errno, "close"); } } - + /* Create argv for new usplash*/ char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1) * sizeof(char *)); /* Count args */ @@ -608,7 +608,7 @@ return status; } argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */ - + /* Kill old usplash */ kill(usplash_pid, SIGTERM); sleep(2); @@ -616,18 +616,18 @@ kill(usplash_pid, SIGKILL); sleep(1); } - + pid_t new_usplash_pid = fork(); if(new_usplash_pid == 0){ /* Child; will become new usplash process */ - + /* Make the effective user ID (root) the only user ID instead of the real user ID (_mandos) */ ret = setuid(geteuid()); if(ret == -1){ error_plus(0, errno, "setuid"); } - + setsid(); ret = chdir("/"); if(ret == -1){ @@ -642,7 +642,7 @@ error_plus(0, errno, "dup2"); _exit(EX_OSERR); } - + execv(usplash_name, cmdline_argv); if(not interrupted_by_signal){ error_plus(0, errno, "execv"); @@ -659,7 +659,7 @@ error_plus(0, errno, "usplash_write"); } } - + /* Close FIFO (again) */ if(fifo_fd != -1){ ret = close(fifo_fd); @@ -668,7 +668,7 @@ } fifo_fd = -1; } - + if(interrupted_by_signal){ struct sigaction signal_action = { .sa_handler = SIG_DFL }; sigemptyset(&signal_action.sa_mask); @@ -686,6 +686,6 @@ } TEMP_FAILURE_RETRY(pause()); } - + return status; } === modified file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2019-07-27 10:11:45 +0000 +++ plugins.d/usplash.xml 2026-05-17 23:56:03 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -48,24 +48,24 @@ - + &COMMANDNAME; 8mandos - + &COMMANDNAME; Mandos plugin to use usplash to get a password. - + &COMMANDNAME; - + DESCRIPTION @@ -100,14 +100,14 @@ arguments as the old one was using. - + OPTIONS This program takes no options. - + EXIT STATUS @@ -118,7 +118,7 @@ be ignored. - + ENVIRONMENT @@ -146,7 +146,7 @@ - + FILES @@ -207,7 +207,7 @@ - + BUGS @@ -218,7 +218,7 @@ - + EXAMPLE @@ -236,7 +236,7 @@ - + SECURITY @@ -274,7 +274,7 @@ immediately be shown as output. - + SEE ALSO