=== modified file 'Makefile'
--- Makefile 2014-10-05 20:08:58 +0000
+++ Makefile 2015-07-06 20:09:47 +0000
@@ -69,6 +69,8 @@
GPGME_CFLAGS=$(shell gpgme-config --cflags; getconf LFS_CFLAGS)
GPGME_LIBS=$(shell 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)
# Do not change these two
CFLAGS+=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \
@@ -106,7 +108,8 @@
PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \
plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo \
plugins.d/plymouth
-CPROGS=plugin-runner $(PLUGINS)
+PLUGIN_HELPERS=plugin-helpers/mandos-client-iprouteadddel
+CPROGS=plugin-runner $(PLUGINS) $(PLUGIN_HELPERS)
PROGS=mandos mandos-keygen mandos-ctl mandos-monitor $(CPROGS)
DOCS=mandos.8 mandos-keygen.8 mandos-monitor.8 mandos-ctl.8 \
mandos.conf.5 mandos-clients.conf.5 plugin-runner.8mandos \
@@ -239,6 +242,10 @@
$(LINK.c) $^ -lrt $(GNUTLS_LIBS) $(AVAHI_LIBS) $(strip\
) $(GPGME_LIBS) $(LOADLIBES) $(LDLIBS) -o $@
+plugin-helpers/mandos-client-iprouteadddel: plugin-helpers/mandos-client-iprouteadddel.c
+ $(LINK.c) $(LIBNL3_CFLAGS) $^ $(LIBNL3_LIBS) $(strip\
+ ) $(LOADLIBES) $(LDLIBS) -o $@
+
.PHONY : all doc html clean distclean mostlyclean maintainer-clean \
check run-client run-server install install-html \
install-server install-client-nokey install-client uninstall \
@@ -273,6 +280,7 @@
@echo "###################################################################"
# We set GNOME_KEYRING_CONTROL to block pam_gnome_keyring
./plugin-runner --plugin-dir=plugins.d \
+ --plugin-helper-dir=plugin-helpers \
--config-file=plugin-runner.conf \
--options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt,--network-hook-dir=network-hooks.d \
--env-for=mandos-client:GNOME_KEYRING_CONTROL= \
@@ -352,10 +360,12 @@
install-client-nokey: all doc
install --directory $(LIBDIR)/mandos $(CONFDIR)
install --directory --mode=u=rwx $(KEYDIR) \
- $(LIBDIR)/mandos/plugins.d
+ $(LIBDIR)/mandos/plugins.d \
+ $(LIBDIR)/mandos/plugin-helpers
if [ "$(CONFDIR)" != "$(LIBDIR)/mandos" ]; then \
install --mode=u=rwx \
--directory "$(CONFDIR)/plugins.d"; \
+ install --directory "$(CONFDIR)/plugin-helpers"; \
fi
install --mode=u=rwx,go=rx --directory \
"$(CONFDIR)/network-hooks.d"
@@ -381,6 +391,9 @@
install --mode=u=rwxs,go=rx \
--target-directory=$(LIBDIR)/mandos/plugins.d \
plugins.d/plymouth
+ install --mode=u=rwxs,go=rx \
+ --target-directory=$(LIBDIR)/mandos/plugin-helpers \
+ plugin-helpers/mandos-client-iprouteadddel
install initramfs-tools-hook \
$(INITRAMFSTOOLS)/hooks/mandos
install --mode=u=rw,go=r initramfs-tools-hook-conf \
=== modified file 'TODO'
--- TODO 2015-07-06 20:14:45 +0000
+++ TODO 2015-07-06 20:29:34 +0000
@@ -52,9 +52,6 @@
** TODO [#C] Remove code for GNU libc < 2.15
* mandos (server)
-** TODO [#B] Work around Avahi issue
- Avahi does not announce link-local addresses if any global
- addresses exist: http://lists.freedesktop.org/archives/avahi/2010-March/001863.html
** TODO [#B] --notify-command
This would allow the mandos.service to use
--notify-command="systemd-notify --pid READY=1"
@@ -72,7 +69,7 @@
+ Approve(False) -> Close client connection immediately
** TODO [#C] python-parsedatetime
** TODO Separate logging logic to own object
-** TODO [#A] Limit approval_delay to max gnutls/tls timeout value
+** TODO [#B] Limit approval_delay to max gnutls/tls timeout value
** TODO [#B] break the wait on approval_delay if connection dies
** TODO Generate Client.runtime_expansions from client options + extra
** TODO Allow %%(checker)s as a runtime expansion
=== modified file 'debian/mandos-client.README.Debian'
--- debian/mandos-client.README.Debian 2013-10-28 10:04:05 +0000
+++ debian/mandos-client.README.Debian 2015-07-01 20:01:26 +0000
@@ -16,6 +16,8 @@
is possible to verify that the correct password will be received by
this client by running the command, on the client:
+ MANDOSPLUGINHELPERDIR=/usr/lib/$(dpkg-architecture \
+ -qDEB_HOST_MULTIARCH)/mandos/plugin-helpers \
/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH \
)/mandos/plugins.d/mandos-client \
--pubkey=/etc/keys/mandos/pubkey.txt \
@@ -91,4 +93,4 @@
work, "--options-for=mandos-client:--connect=
:" needs
to be manually added to the file "/etc/mandos/plugin-runner.conf".
- -- Teddy Hogeborn , Mon, 28 Oct 2013 11:02:26 +0100
+ -- Teddy Hogeborn , Mon, 29 Jun 2015 18:17:41 +0200
=== modified file 'initramfs-tools-hook'
--- initramfs-tools-hook 2014-07-16 23:44:54 +0000
+++ initramfs-tools-hook 2015-07-06 20:09:47 +0000
@@ -70,11 +70,13 @@
CONFDIR="/conf/conf.d/mandos"
MANDOSDIR="/lib/mandos"
PLUGINDIR="${MANDOSDIR}/plugins.d"
+PLUGINHELPERDIR="${MANDOSDIR}/plugin-helpers"
HOOKDIR="${MANDOSDIR}/network-hooks.d"
# Make directories
install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \
- "${DESTDIR}${MANDOSDIR}" "${DESTDIR}${HOOKDIR}"
+ "${DESTDIR}${MANDOSDIR}" "${DESTDIR}${HOOKDIR}" \
+ "${DESTDIR}${PLUGINHELPERDIR}"
install --owner=${mandos_user} --group=${mandos_group} --directory \
--mode=u=rwx "${DESTDIR}${PLUGINDIR}"
@@ -98,6 +100,21 @@
esac
done
+# Copy the packaged plugin helpers
+for file in "$libdir"/mandos/plugin-helpers/*; do
+ base="`basename \"$file\"`"
+ # Is this plugin overridden?
+ if [ -e "/etc/mandos/plugin-helpers/$base" ]; then
+ continue
+ fi
+ case "$base" in
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
+ "*") : ;;
+ *) copy_exec "$file" "${PLUGINHELPERDIR}" ;;
+ esac
+done
+
# Copy any user-supplied plugins
for file in /etc/mandos/plugins.d/*; do
base="`basename \"$file\"`"
@@ -109,6 +126,17 @@
esac
done
+# Copy any user-supplied plugin helpers
+for file in /etc/mandos/plugin-helpers/*; do
+ base="`basename \"$file\"`"
+ case "$base" in
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
+ "*") : ;;
+ *) copy_exec "$file" "${PLUGINHELPERDIR}" ;;
+ esac
+done
+
# Get DEVICE from initramfs.conf and other files
. /etc/initramfs-tools/initramfs.conf
for conf in /etc/initramfs-tools/conf.d/*; do
=== added directory 'plugin-helpers'
=== added file 'plugin-helpers/mandos-client-iprouteadddel.c'
--- plugin-helpers/mandos-client-iprouteadddel.c 1970-01-01 00:00:00 +0000
+++ plugin-helpers/mandos-client-iprouteadddel.c 2015-07-05 21:38:01 +0000
@@ -0,0 +1,282 @@
+/* -*- coding: utf-8 -*- */
+/*
+ * iprouteadddel - Add or delete direct route to a local IP address
+ *
+ * Copyright © 2015 Teddy Hogeborn
+ * Copyright © 2015 Björn Påhlsson
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * .
+ *
+ * Contact the authors at .
+ */
+
+#define _GNU_SOURCE /* asprintf(),
+ 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,
+ 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 */
+
+#include /* struct nl_addr, nl_addr_parse(),
+ nl_geterror(),
+ nl_addr_get_family(),
+ 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 /* struct nl_sock, nl_socket_alloc(),
+ nl_connect(), nl_socket_free() */
+#include /* rtnl_link_get_kernel(),
+ rtnl_link_get_ifindex(),
+ rtnl_link_put() */
+
+bool debug = false;
+const char *argp_program_version = "mandos-client-iprouteadddel " VERSION;
+const char *argp_program_bug_address = "";
+
+/* Function to use when printing errors */
+void perror_plus(const char *print_text){
+ int e = errno;
+ fprintf(stderr, "Mandos plugin helper %s: ",
+ program_invocation_short_name);
+ errno = e;
+ perror(print_text);
+}
+
+__attribute__((format (gnu_printf, 2, 3), nonnull))
+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);
+}
+
+int main(int argc, char *argv[]){
+ int ret;
+ int exitcode = EXIT_SUCCESS;
+ struct arguments {
+ bool add; /* true: add, false: delete */
+ char *address; /* IP address as string */
+ struct nl_addr *nl_addr; /* Netlink IP address */
+ char *interface; /* interface name */
+ } arguments = { .add = true, .address = NULL, .interface = NULL };
+ struct argp_option options[] = {
+ { .name = "debug", .key = 128,
+ .doc = "Debug mode" },
+ { .name = NULL }
+ };
+ 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;
+ switch(key){
+ case 128: /* --debug */
+ debug = true;
+ break;
+ case ARGP_KEY_ARG:
+ switch(state->arg_num){
+ case 0:
+ if(strcasecmp(arg, "add") == 0){
+ ((struct arguments *)(state->input))->add = true;
+ } else if(strcasecmp(arg, "delete") == 0){
+ ((struct arguments *)(state->input))->add = false;
+ } else {
+ fprintf_plus(stderr, "Unrecognized command: %s\n", arg);
+ argp_usage(state);
+ }
+ break;
+ case 1:
+ ((struct arguments *)(state->input))->address = arg;
+ lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *)
+ (state->input))
+ ->nl_addr));
+ if(lret != 0){
+ fprintf_plus(stderr, "Failed to parse address %s: %s\n",
+ arg, nl_geterror(lret));
+ argp_usage(state);
+ }
+ break;
+ case 2:
+ ((struct arguments *)(state->input))->interface = arg;
+ break;
+ default:
+ argp_usage(state);
+ }
+ break;
+ case ARGP_KEY_END:
+ if(state->arg_num < 3){
+ argp_usage(state);
+ }
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ 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:
+ break;
+ case EINVAL:
+ exit(EX_USAGE);
+ case ENOMEM:
+ default:
+ errno = ret;
+ perror_plus("argp_parse");
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Get netlink socket */
+ sk = nl_socket_alloc();
+ if(sk == NULL){
+ fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n",
+ nl_geterror(ret));
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Connect socket to netlink */
+ ret = nl_connect(sk, NETLINK_ROUTE);
+ if(ret < 0){
+ fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n",
+ nl_geterror(ret));
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Get link object of specified interface */
+ struct rtnl_link *link = NULL;
+ ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link);
+ if(ret < 0){
+ fprintf_plus(stderr, "Failed to use interface %s: %s\n",
+ arguments.interface, nl_geterror(ret));
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Get netlink route object */
+ route = rtnl_route_alloc();
+ if(route == NULL){
+ fprintf_plus(stderr, "Failed to get netlink route:\n");
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Get address family of specified address */
+ sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr);
+ if(debug){
+ fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX
+ ")\n", arguments.address,
+ af == AF_INET6 ? "AF_INET6" :
+ ( af == AF_INET ? "AF_INET" : "UNKNOWN"),
+ (intmax_t)af);
+ }
+ /* Set route parameters: */
+ rtnl_route_set_family(route, (uint8_t)af); /* Address family */
+ rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see
+ ip-route(8) */
+ rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */
+ rtnl_route_set_type(route, RTN_UNICAST); /* normal unicast
+ address route */
+ rtnl_route_set_dst(route, arguments.nl_addr); /* Destination
+ address */
+ rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing
+ table */
+ /* Create nexthop */
+ nexthop = rtnl_route_nh_alloc();
+ if(nexthop == NULL){
+ fprintf_plus(stderr, "Failed to get netlink route nexthop\n");
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ /* Get index number of specified interface */
+ int ifindex = rtnl_link_get_ifindex(link);
+ if(debug){
+ fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface,
+ ifindex);
+ }
+ /* Set interface index number on nexthop object */
+ rtnl_route_nh_set_ifindex(nexthop, ifindex);
+ /* Set route tu use nexthop object */
+ rtnl_route_add_nexthop(route, nexthop);
+ /* Add or delete route? */
+ if(arguments.add){
+ ret = rtnl_route_add(sk, route, NLM_F_EXCL);
+ } else {
+ ret = rtnl_route_delete(sk, route, 0);
+ }
+ if(ret < 0){
+ fprintf_plus(stderr, "Failed to %s route: %s\n",
+ arguments.add ? "add" : "delete",
+ nl_geterror(ret));
+ exitcode = EX_OSERR;
+ goto end;
+ }
+ end:
+ /* Deallocate route */
+ if(route){
+ rtnl_route_put(route);
+ } else if(nexthop) {
+ /* Deallocate route nexthop */
+ rtnl_route_nh_free(nexthop);
+ }
+ /* Deallocate parsed address */
+ if(arguments.nl_addr){
+ nl_addr_put(arguments.nl_addr);
+ }
+ /* Deallocate link struct */
+ if(link){
+ rtnl_link_put(link);
+ }
+ /* Deallocate netlink socket struct */
+ if(sk){
+ nl_socket_free(sk);
+ }
+ return exitcode;
+}
=== modified file 'plugin-runner.c'
--- plugin-runner.c 2014-07-16 01:41:23 +0000
+++ plugin-runner.c 2015-06-28 16:35:27 +0000
@@ -76,6 +76,7 @@
#define BUFFER_SIZE 256
#define PDIR "/lib/mandos/plugins.d"
+#define PHDIR "/lib/mandos/plugin-helpers"
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
const char *argp_program_version = "plugin-runner " VERSION;
@@ -347,6 +348,7 @@
int main(int argc, char *argv[]){
char *plugindir = NULL;
+ char *pluginhelperdir = NULL;
char *argfile = NULL;
FILE *conffp;
struct dirent **direntries = NULL;
@@ -414,6 +416,10 @@
.doc = "Group ID the plugins will run as", .group = 3 },
{ .name = "debug", .key = 132,
.doc = "Debug mode", .group = 4 },
+ { .name = "plugin-helper-dir", .key = 133,
+ .arg = "DIRECTORY",
+ .doc = "Specify a different plugin helper directory",
+ .group = 2 },
/*
* These reproduce what we would get without ARGP_NO_HELP
*/
@@ -545,6 +551,13 @@
case 132: /* --debug */
debug = true;
break;
+ case 133: /* --plugin-helper-dir */
+ free(pluginhelperdir);
+ pluginhelperdir = strdup(arg);
+ if(pluginhelperdir != NULL){
+ errno = 0;
+ }
+ break;
/*
* These reproduce what we would get without ARGP_NO_HELP
*/
@@ -601,6 +614,7 @@
case 130: /* --userid */
case 131: /* --groupid */
case 132: /* --debug */
+ case 133: /* --plugin-helper-dir */
case '?': /* --help */
case -3: /* --usage */
case 'V': /* --version */
@@ -761,6 +775,24 @@
goto fallback;
}
+ {
+ char *pluginhelperenv;
+ bool bret = true;
+ ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
+ pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
+ if(ret != -1){
+ bret = add_environment(getplugin(NULL), pluginhelperenv, true);
+ }
+ if(ret == -1 or not bret){
+ error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
+ " environment variable to \"%s\" for all plugins\n",
+ pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
+ }
+ if(ret != -1){
+ free(pluginhelperenv);
+ }
+ }
+
if(debug){
for(plugin *p = plugin_list; p != NULL; p=p->next){
fprintf(stderr, "Plugin: %s has %d arguments\n",
@@ -1336,6 +1368,7 @@
free_plugin_list();
free(plugindir);
+ free(pluginhelperdir);
free(argfile);
return exitstatus;
=== modified file 'plugin-runner.xml'
--- plugin-runner.xml 2015-07-06 20:14:45 +0000
+++ plugin-runner.xml 2015-07-06 20:29:34 +0000
@@ -114,6 +114,9 @@
+
+
@@ -320,6 +323,21 @@
+
+
+
+ Specify a different plugin helper directory. The default
+ is /lib/mandos/plugin-helpers, which
+ will exist in the initial RAM disk
+ environment. (This will simply be passed to all plugins
+ via the MANDOSPLUGINHELPERDIR environment
+ variable. See )
+
+
+
+
+
@@ -426,7 +444,11 @@
The plugin will run in the initial RAM disk environment, so
care must be taken not to depend on any files or running
- services not available there.
+ services not available there. Any helper executables required
+ by the plugin (which are not in the PATH) can
+ be placed in the plugin helper directory, the name of which
+ will be made available to the plugin via the
+ MANDOSPLUGINHELPERDIR environment variable.
The plugin must exit cleanly and free all allocated resources
@@ -475,7 +497,9 @@
only passes on its environment to all the plugins. The
environment passed to plugins can be modified using the
and
- options.
+ options. Also, the option
+ will affect the environment variable
+ MANDOSPLUGINHELPERDIR for the plugins.
=== modified file 'plugins.d/mandos-client.c'
--- plugins.d/mandos-client.c 2015-03-10 18:52:09 +0000
+++ plugins.d/mandos-client.c 2015-07-06 20:29:34 +0000
@@ -9,8 +9,8 @@
* "browse_callback", and parts of "main".
*
* Everything else is
- * Copyright © 2008-2014 Teddy Hogeborn
- * Copyright © 2008-2014 Björn Påhlsson
+ * Copyright © 2008-2015 Teddy Hogeborn
+ * Copyright © 2008-2015 Björn Påhlsson
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -757,6 +757,223 @@
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
__attribute__((unused)) const char *txt){}
+/* Set effective uid to 0, return errno */
+__attribute__((warn_unused_result))
+error_t raise_privileges(void){
+ error_t old_errno = errno;
+ error_t ret_errno = 0;
+ if(seteuid(0) == -1){
+ ret_errno = errno;
+ }
+ errno = old_errno;
+ return ret_errno;
+}
+
+/* Set effective and real user ID to 0. Return errno. */
+__attribute__((warn_unused_result))
+error_t raise_privileges_permanently(void){
+ error_t old_errno = errno;
+ error_t ret_errno = raise_privileges();
+ if(ret_errno != 0){
+ errno = old_errno;
+ return ret_errno;
+ }
+ if(setuid(0) == -1){
+ ret_errno = errno;
+ }
+ errno = old_errno;
+ return ret_errno;
+}
+
+/* Set effective user ID to unprivileged saved user ID */
+__attribute__((warn_unused_result))
+error_t lower_privileges(void){
+ error_t old_errno = errno;
+ error_t ret_errno = 0;
+ if(seteuid(uid) == -1){
+ ret_errno = errno;
+ }
+ errno = old_errno;
+ return ret_errno;
+}
+
+/* Lower privileges permanently */
+__attribute__((warn_unused_result))
+error_t lower_privileges_permanently(void){
+ error_t old_errno = errno;
+ error_t ret_errno = 0;
+ if(setuid(uid) == -1){
+ ret_errno = errno;
+ }
+ errno = old_errno;
+ return ret_errno;
+}
+
+/* Helper function to add_local_route() and delete_local_route() */
+__attribute__((nonnull, warn_unused_result))
+static bool add_delete_local_route(const bool add,
+ const char *address,
+ AvahiIfIndex if_index){
+ int ret;
+ char helper[] = "mandos-client-iprouteadddel";
+ char add_arg[] = "add";
+ char delete_arg[] = "delete";
+ char debug_flag[] = "--debug";
+ char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
+ if(pluginhelperdir == NULL){
+ if(debug){
+ fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
+ " variable not set; cannot run helper\n");
+ }
+ 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)");
+ return false;
+ }
+ pid_t pid = fork();
+ if(pid == 0){
+ /* Child */
+ /* Raise privileges */
+ errno = raise_privileges_permanently();
+ if(errno != 0){
+ perror_plus("Failed to raise privileges");
+ /* _exit(EX_NOPERM); */
+ } else {
+ /* Set group */
+ errno = 0;
+ ret = setgid(0);
+ if(ret == -1){
+ perror_plus("setgid");
+ _exit(EX_NOPERM);
+ }
+ /* Reset supplementary groups */
+ errno = 0;
+ ret = setgroups(0, NULL);
+ if(ret == -1){
+ perror_plus("setgroups");
+ _exit(EX_NOPERM);
+ }
+ }
+ ret = dup2(devnull, STDIN_FILENO);
+ if(ret == -1){
+ perror_plus("dup2(devnull, STDIN_FILENO)");
+ _exit(EX_OSERR);
+ }
+ ret = (int)TEMP_FAILURE_RETRY(close(devnull));
+ if(ret == -1){
+ perror_plus("close");
+ _exit(EX_OSERR);
+ }
+ ret = dup2(STDERR_FILENO, STDOUT_FILENO);
+ if(ret == -1){
+ perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
+ _exit(EX_OSERR);
+ }
+ int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
+ O_RDONLY
+ | O_DIRECTORY
+ | O_PATH
+ | O_CLOEXEC));
+ if(helperdir_fd == -1){
+ perror_plus("open");
+ _exit(EX_UNAVAILABLE);
+ }
+ int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
+ helper, O_RDONLY));
+ if(helper_fd == -1){
+ perror_plus("openat");
+ _exit(EX_UNAVAILABLE);
+ }
+ TEMP_FAILURE_RETRY(close(helperdir_fd));
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+ if(fexecve(helper_fd, (char *const [])
+ { helper, add ? add_arg : delete_arg, (char *)address,
+ interface, debug ? debug_flag : NULL, NULL },
+ environ) == -1){
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ perror_plus("fexecve");
+ _exit(EXIT_FAILURE);
+ }
+ }
+ if(pid == -1){
+ perror_plus("fork");
+ return false;
+ }
+ int status;
+ pid_t pret = -1;
+ errno = 0;
+ do {
+ pret = waitpid(pid, &status, 0);
+ if(pret == -1 and errno == EINTR and quit_now){
+ int errno_raising = 0;
+ if((errno = raise_privileges()) != 0){
+ errno_raising = errno;
+ perror_plus("Failed to raise privileges in order to"
+ " kill helper program");
+ }
+ if(kill(pid, SIGTERM) == -1){
+ perror_plus("kill");
+ }
+ if((errno_raising == 0) and (errno = lower_privileges()) != 0){
+ perror_plus("Failed to lower privileges after killing"
+ " helper program");
+ }
+ return false;
+ }
+ } while(pret == -1 and errno == EINTR);
+ if(pret == -1){
+ perror_plus("waitpid");
+ return false;
+ }
+ if(WIFEXITED(status)){
+ if(WEXITSTATUS(status) != 0){
+ fprintf_plus(stderr, "Error: iprouteadddel exited"
+ " with status %d\n", WEXITSTATUS(status));
+ return false;
+ }
+ return true;
+ }
+ if(WIFSIGNALED(status)){
+ fprintf_plus(stderr, "Error: iprouteadddel died by"
+ " signal %d\n", WTERMSIG(status));
+ return false;
+ }
+ fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
+ return false;
+}
+
+__attribute__((nonnull, warn_unused_result))
+static bool add_local_route(const char *address,
+ AvahiIfIndex if_index){
+ if(debug){
+ fprintf_plus(stderr, "Adding route to %s\n", address);
+ }
+ return add_delete_local_route(true, address, if_index);
+}
+
+__attribute__((nonnull, warn_unused_result))
+static bool delete_local_route(const char *address,
+ AvahiIfIndex if_index){
+ if(debug){
+ fprintf_plus(stderr, "Removing route to %s\n", address);
+ }
+ return add_delete_local_route(false, address, if_index);
+}
+
/* Called when a Mandos server is found */
__attribute__((nonnull, warn_unused_result))
static int start_mandos_communication(const char *ip, in_port_t port,
@@ -773,6 +990,7 @@
int retval = -1;
gnutls_session_t session;
int pf; /* Protocol family */
+ bool route_added = false;
errno = 0;
@@ -836,7 +1054,7 @@
PRIuMAX "\n", ip, (uintmax_t)port);
}
- tcp_sd = socket(pf, SOCK_STREAM, 0);
+ tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
if(tcp_sd < 0){
int e = errno;
perror_plus("socket");
@@ -931,25 +1149,61 @@
goto mandos_end;
}
- if(af == AF_INET6){
- ret = connect(tcp_sd, (struct sockaddr *)&to,
- sizeof(struct sockaddr_in6));
- } else {
- ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
- sizeof(struct sockaddr_in));
- }
- if(ret < 0){
- if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
- int e = errno;
- perror_plus("connect");
- errno = e;
- }
- goto mandos_end;
- }
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
+ while(true){
+ if(af == AF_INET6){
+ ret = connect(tcp_sd, (struct sockaddr *)&to,
+ sizeof(struct sockaddr_in6));
+ } else {
+ ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
+ sizeof(struct sockaddr_in));
+ }
+ if(ret < 0){
+ if(errno == ENETUNREACH
+ and if_index != AVAHI_IF_UNSPEC
+ and connect_to == NULL
+ and not route_added and
+ ((af == AF_INET6 and not
+ IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
+ &to)->sin6_addr)))
+ or (af == AF_INET and
+ /* Not a a IPv4LL address */
+ (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
+ & 0xFFFF0000L) != 0xA9FE0000L))){
+ /* Work around Avahi bug - Avahi does not announce link-local
+ addresses if it has a global address, so local hosts with
+ *only* a link-local address (e.g. Mandos clients) cannot
+ 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:
+ http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
+ https://bugs.debian.org/587961
+ */
+ if(debug){
+ fprintf_plus(stderr, "Mandos server unreachable, trying"
+ " direct route\n");
+ }
+ int e = errno;
+ route_added = add_local_route(ip, if_index);
+ if(route_added){
+ continue;
+ }
+ errno = e;
+ }
+ if(errno != ECONNREFUSED or debug){
+ int e = errno;
+ perror_plus("connect");
+ errno = e;
+ }
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ errno = EINTR;
+ goto mandos_end;
+ }
+ break;
}
const char *out = mandos_protocol_version;
@@ -1138,6 +1392,12 @@
mandos_end:
{
+ if(route_added){
+ if(not delete_local_route(ip, if_index)){
+ fprintf_plus(stderr, "Failed to delete local route to %s on"
+ " interface %d", ip, if_index);
+ }
+ }
int e = errno;
free(decrypted_buffer);
free(buffer);
@@ -1569,64 +1829,13 @@
}
}
-/* Set effective uid to 0, return errno */
-__attribute__((warn_unused_result))
-error_t raise_privileges(void){
- error_t old_errno = errno;
- error_t ret_errno = 0;
- if(seteuid(0) == -1){
- ret_errno = errno;
- }
- errno = old_errno;
- return ret_errno;
-}
-
-/* Set effective and real user ID to 0. Return errno. */
-__attribute__((warn_unused_result))
-error_t raise_privileges_permanently(void){
- error_t old_errno = errno;
- error_t ret_errno = raise_privileges();
- if(ret_errno != 0){
- errno = old_errno;
- return ret_errno;
- }
- if(setuid(0) == -1){
- ret_errno = errno;
- }
- errno = old_errno;
- return ret_errno;
-}
-
-/* Set effective user ID to unprivileged saved user ID */
-__attribute__((warn_unused_result))
-error_t lower_privileges(void){
- error_t old_errno = errno;
- error_t ret_errno = 0;
- if(seteuid(uid) == -1){
- ret_errno = errno;
- }
- errno = old_errno;
- return ret_errno;
-}
-
-/* Lower privileges permanently */
-__attribute__((warn_unused_result))
-error_t lower_privileges_permanently(void){
- error_t old_errno = errno;
- error_t ret_errno = 0;
- if(setuid(uid) == -1){
- ret_errno = errno;
- }
- errno = old_errno;
- return ret_errno;
-}
-
__attribute__((nonnull))
void run_network_hooks(const char *mode, const char *interface,
const float delay){
struct dirent **direntries = NULL;
if(hookdir_fd == -1){
- hookdir_fd = open(hookdir, O_RDONLY);
+ hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
+ | O_CLOEXEC);
if(hookdir_fd == -1){
if(errno == ENOENT){
if(debug){
@@ -1657,7 +1866,11 @@
}
struct dirent *direntry;
int ret;
- int devnull = open("/dev/null", O_RDONLY);
+ int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
+ if(devnull == -1){
+ perror_plus("open(\"/dev/null\", O_RDONLY)");
+ return;
+ }
for(int i = 0; i < numhooks; i++){
direntry = direntries[i];
if(debug){
@@ -1687,21 +1900,6 @@
perror_plus("setgroups");
_exit(EX_NOPERM);
}
- ret = dup2(devnull, STDIN_FILENO);
- if(ret == -1){
- perror_plus("dup2(devnull, STDIN_FILENO)");
- _exit(EX_OSERR);
- }
- ret = close(devnull);
- if(ret == -1){
- perror_plus("close");
- _exit(EX_OSERR);
- }
- ret = dup2(STDERR_FILENO, STDOUT_FILENO);
- if(ret == -1){
- perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
- _exit(EX_OSERR);
- }
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
if(ret == -1){
perror_plus("setenv");
@@ -1742,7 +1940,9 @@
_exit(EX_OSERR);
}
}
- int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
+ int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
+ direntry->d_name,
+ O_RDONLY));
if(hook_fd == -1){
perror_plus("openat");
_exit(EXIT_FAILURE);
@@ -1751,6 +1951,21 @@
perror_plus("close");
_exit(EXIT_FAILURE);
}
+ ret = dup2(devnull, STDIN_FILENO);
+ if(ret == -1){
+ perror_plus("dup2(devnull, STDIN_FILENO)");
+ _exit(EX_OSERR);
+ }
+ ret = (int)TEMP_FAILURE_RETRY(close(devnull));
+ if(ret == -1){
+ perror_plus("close");
+ _exit(EX_OSERR);
+ }
+ ret = dup2(STDERR_FILENO, STDOUT_FILENO);
+ if(ret == -1){
+ perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
+ _exit(EX_OSERR);
+ }
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
environ) == -1){
perror_plus("fexecve");
@@ -2204,7 +2419,7 @@
goto end;
}
}
-
+
{
/* Work around Debian bug #633582:
*/
@@ -2237,7 +2452,7 @@
TEMP_FAILURE_RETRY(close(seckey_fd));
}
}
-
+
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
int pubkey_fd = open(pubkey, O_RDONLY);
if(pubkey_fd == -1){
@@ -2258,7 +2473,7 @@
TEMP_FAILURE_RETRY(close(pubkey_fd));
}
}
-
+
/* Lower privileges */
ret_errno = lower_privileges();
if(ret_errno != 0){
@@ -2733,8 +2948,10 @@
/* Removes the GPGME temp directory and all files inside */
if(tempdir != NULL){
struct dirent **direntries = NULL;
- int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
- O_NOFOLLOW));
+ int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
+ | O_NOFOLLOW
+ | O_DIRECTORY
+ | O_PATH));
if(tempdir_fd == -1){
perror_plus("open");
} else {
=== modified file 'plugins.d/mandos-client.xml'
--- plugins.d/mandos-client.xml 2015-03-10 18:52:09 +0000
+++ plugins.d/mandos-client.xml 2015-07-06 20:29:34 +0000
@@ -2,7 +2,7 @@
-
+
%common;
]>
@@ -36,6 +36,7 @@
2012
2013
2014
+ 2015
Teddy Hogeborn
Björn Påhlsson
@@ -445,9 +446,22 @@
ENVIRONMENT
+
+
+ MANDOSPLUGINHELPERDIR
+
+
+ This environment variable will be assumed to contain the
+ directory containing any helper executables. The use and
+ nature of these helper executables, if any, is
+ purposefully not documented.
+
+
+
+
- This program does not use any environment variables, not even
- the ones provided by cryptsetup8
.