=== 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 'plugins.d/mandos-client.c'
--- plugins.d/mandos-client.c 2014-08-20 21:46:38 +0000
+++ plugins.d/mandos-client.c 2015-07-01 20:01:26 +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
@@ -650,6 +650,207 @@
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 remove_local_route() */
+__attribute__((nonnull, warn_unused_result))
+static bool add_remove_local_route(const bool add,
+ const char *address,
+ AvahiIfIndex if_index){
+ int ret;
+ char helper[] = "mandos-client-iprouteadddel";
+ char add_arg[] = "add";
+ char remove_arg[] = "remove";
+ 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 = 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));
+ int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
+ helper, O_RDONLY));
+ 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 : remove_arg, (char *)address,
+ interface, 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){
+ return add_remove_local_route(true, address, if_index);
+}
+
+__attribute__((nonnull, warn_unused_result))
+static bool remove_local_route(const char *address,
+ AvahiIfIndex if_index){
+ return add_remove_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,
@@ -666,6 +867,7 @@
int retval = -1;
gnutls_session_t session;
int pf; /* Protocol family */
+ bool route_added = false;
errno = 0;
@@ -729,7 +931,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");
@@ -824,25 +1026,57 @@
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
+ */
+ 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;
@@ -1031,6 +1265,12 @@
mandos_end:
{
+ if(route_added){
+ if(not remove_local_route(ip, if_index)){
+ fprintf_plus(stderr, "Failed to remove local route to %s on"
+ " interface %d", ip, if_index);
+ }
+ }
int e = errno;
free(decrypted_buffer);
free(buffer);
@@ -1462,64 +1702,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){
@@ -1550,7 +1739,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){
@@ -1580,21 +1773,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");
@@ -1635,7 +1813,9 @@
_exit(EX_OSERR);
}
}
- int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
+ int hook_fd = TEMP_FAILURE_RETRY(openat(hookdir_fd,
+ direntry->d_name,
+ O_RDONLY));
if(hook_fd == -1){
perror_plus("openat");
_exit(EXIT_FAILURE);
@@ -1644,6 +1824,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 = 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");
@@ -2097,7 +2292,7 @@
goto end;
}
}
-
+
{
/* Work around Debian bug #633582:
*/
@@ -2130,7 +2325,7 @@
TEMP_FAILURE_RETRY(close(seckey_fd));
}
}
-
+
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
int pubkey_fd = open(pubkey, O_RDONLY);
if(pubkey_fd == -1){
@@ -2151,7 +2346,7 @@
TEMP_FAILURE_RETRY(close(pubkey_fd));
}
}
-
+
/* Lower privileges */
ret_errno = lower_privileges();
if(ret_errno != 0){
@@ -2626,8 +2821,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 2014-06-22 02:19:30 +0000
+++ plugins.d/mandos-client.xml 2015-07-01 20:01:26 +0000
@@ -2,7 +2,7 @@
-
+
%common;
]>
@@ -36,6 +36,7 @@
2012
2013
2014
+ 2015
Teddy Hogeborn
Björn Påhlsson
@@ -444,9 +445,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
.