2010-10-25 20:35:17 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2010 Lennart Poettering
|
2015-11-18 12:28:30 +01:00
|
|
|
Copyright 2015 Werner Fink
|
2010-10-25 20:35:17 +02:00
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2010-10-25 20:35:17 +02:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd 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
|
2012-04-12 00:20:58 +02:00
|
|
|
Lesser General Public License for more details.
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2010-10-25 20:35:17 +02:00
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <poll.h>
|
2015-11-18 12:28:30 +01:00
|
|
|
#include <signal.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
2010-10-25 20:35:17 +02:00
|
|
|
#include <string.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <sys/inotify.h>
|
2015-11-18 12:28:30 +01:00
|
|
|
#include <sys/prctl.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <sys/signalfd.h>
|
2010-10-25 20:35:17 +02:00
|
|
|
#include <sys/socket.h>
|
2015-11-18 12:28:30 +01:00
|
|
|
#include <sys/wait.h>
|
2010-10-25 20:35:17 +02:00
|
|
|
#include <sys/un.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "ask-password-api.h"
|
|
|
|
#include "conf-parser.h"
|
|
|
|
#include "def.h"
|
2015-10-26 20:07:55 +01:00
|
|
|
#include "dirent-util.h"
|
2015-11-18 12:28:30 +01:00
|
|
|
#include "exit-status.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-11-18 12:28:30 +01:00
|
|
|
#include "fileio.h"
|
|
|
|
#include "hashmap.h"
|
2015-10-25 14:08:25 +01:00
|
|
|
#include "io-util.h"
|
2015-11-18 12:28:30 +01:00
|
|
|
#include "macro.h"
|
2012-04-10 21:54:31 +02:00
|
|
|
#include "mkdir.h"
|
2012-05-07 21:36:12 +02:00
|
|
|
#include "path-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "process-util.h"
|
|
|
|
#include "signal-util.h"
|
2010-10-26 04:35:25 +02:00
|
|
|
#include "socket-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2011-02-23 01:12:07 +01:00
|
|
|
#include "strv.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "util.h"
|
|
|
|
#include "utmp-wtmp.h"
|
2010-10-25 20:35:17 +02:00
|
|
|
|
|
|
|
static enum {
|
|
|
|
ACTION_LIST,
|
|
|
|
ACTION_QUERY,
|
|
|
|
ACTION_WATCH,
|
|
|
|
ACTION_WALL
|
|
|
|
} arg_action = ACTION_QUERY;
|
|
|
|
|
2010-10-26 04:35:25 +02:00
|
|
|
static bool arg_plymouth = false;
|
2010-11-16 04:28:04 +01:00
|
|
|
static bool arg_console = false;
|
2015-11-18 12:28:30 +01:00
|
|
|
static const char *arg_device = NULL;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2011-02-23 01:12:07 +01:00
|
|
|
static int ask_password_plymouth(
|
|
|
|
const char *message,
|
|
|
|
usec_t until,
|
2015-10-07 11:26:10 +02:00
|
|
|
AskPasswordFlags flags,
|
2011-02-23 01:12:07 +01:00
|
|
|
const char *flag_file,
|
2015-10-07 11:26:10 +02:00
|
|
|
char ***ret) {
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2016-05-05 22:24:36 +02:00
|
|
|
static const union sockaddr_union sa = PLYMOUTH_SOCKET;
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_close_ int fd = -1, notify = -1;
|
|
|
|
_cleanup_free_ char *packet = NULL;
|
2010-10-26 04:35:25 +02:00
|
|
|
ssize_t k;
|
|
|
|
int r, n;
|
2013-03-25 00:59:00 +01:00
|
|
|
struct pollfd pollfd[2] = {};
|
2010-10-26 04:35:25 +02:00
|
|
|
char buffer[LINE_MAX];
|
|
|
|
size_t p = 0;
|
|
|
|
enum {
|
|
|
|
POLL_SOCKET,
|
|
|
|
POLL_INOTIFY
|
|
|
|
};
|
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
assert(ret);
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2010-10-26 04:35:25 +02:00
|
|
|
if (flag_file) {
|
2014-08-03 18:52:03 +02:00
|
|
|
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
|
|
|
|
if (notify < 0)
|
|
|
|
return -errno;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
|
|
|
|
if (r < 0)
|
|
|
|
return -errno;
|
2010-10-26 04:35:25 +02:00
|
|
|
}
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
|
|
|
if (fd < 0)
|
|
|
|
return -errno;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2016-05-05 22:24:36 +02:00
|
|
|
r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
|
2014-11-28 19:57:32 +01:00
|
|
|
if (r < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return -errno;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
|
2011-02-23 01:12:07 +01:00
|
|
|
packet = strdup("c");
|
|
|
|
n = 1;
|
2015-10-06 16:27:24 +02:00
|
|
|
} else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
|
2014-07-25 15:38:31 +02:00
|
|
|
packet = NULL;
|
2014-08-03 18:52:03 +02:00
|
|
|
if (!packet)
|
2015-10-06 16:27:24 +02:00
|
|
|
return -ENOMEM;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2014-12-02 02:43:19 +01:00
|
|
|
r = loop_write(fd, packet, n + 1, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
|
|
|
pollfd[POLL_SOCKET].fd = fd;
|
|
|
|
pollfd[POLL_SOCKET].events = POLLIN;
|
|
|
|
pollfd[POLL_INOTIFY].fd = notify;
|
|
|
|
pollfd[POLL_INOTIFY].events = POLLIN;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int sleep_for = -1, j;
|
|
|
|
|
|
|
|
if (until > 0) {
|
|
|
|
usec_t y;
|
|
|
|
|
|
|
|
y = now(CLOCK_MONOTONIC);
|
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
if (y > until) {
|
|
|
|
r = -ETIME;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
|
|
|
sleep_for = (int) ((until - y) / USEC_PER_MSEC);
|
|
|
|
}
|
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
if (flag_file && access(flag_file, F_OK) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
|
2014-08-03 18:52:03 +02:00
|
|
|
if (j < 0) {
|
2010-10-26 04:35:25 +02:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
r = -errno;
|
|
|
|
goto finish;
|
|
|
|
} else if (j == 0) {
|
|
|
|
r = -ETIME;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
|
2010-10-26 04:35:25 +02:00
|
|
|
flush_fd(notify);
|
|
|
|
|
|
|
|
if (pollfd[POLL_SOCKET].revents == 0)
|
|
|
|
continue;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
k = read(fd, buffer + p, sizeof(buffer) - p);
|
2015-10-07 11:26:10 +02:00
|
|
|
if (k < 0) {
|
2017-09-29 00:37:23 +02:00
|
|
|
if (IN_SET(errno, EINTR, EAGAIN))
|
2015-10-07 11:26:10 +02:00
|
|
|
continue;
|
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
r = -errno;
|
|
|
|
goto finish;
|
|
|
|
} else if (k == 0) {
|
|
|
|
r = -EIO;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
|
|
|
p += k;
|
|
|
|
|
|
|
|
if (p < 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (buffer[0] == 5) {
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
|
2011-02-23 01:12:07 +01:00
|
|
|
/* Hmm, first try with cached
|
|
|
|
* passwords failed, so let's retry
|
|
|
|
* with a normal password request */
|
2015-07-31 19:56:38 +02:00
|
|
|
packet = mfree(packet);
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto finish;
|
|
|
|
}
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2014-12-02 02:43:19 +01:00
|
|
|
r = loop_write(fd, packet, n+1, true);
|
|
|
|
if (r < 0)
|
2015-10-14 22:40:23 +02:00
|
|
|
goto finish;
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
|
2011-02-23 01:12:07 +01:00
|
|
|
p = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-10-26 04:35:25 +02:00
|
|
|
/* No password, because UI not shown */
|
2015-10-14 22:40:23 +02:00
|
|
|
r = -ENOENT;
|
|
|
|
goto finish;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2011-02-23 01:12:07 +01:00
|
|
|
} else if (buffer[0] == 2 || buffer[0] == 9) {
|
2010-10-26 04:35:25 +02:00
|
|
|
uint32_t size;
|
2011-02-23 01:12:07 +01:00
|
|
|
char **l;
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2014-09-30 15:25:47 +02:00
|
|
|
/* One or more answers */
|
2010-10-26 04:35:25 +02:00
|
|
|
if (p < 5)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
memcpy(&size, buffer+1, sizeof(size));
|
2011-11-01 14:20:31 +01:00
|
|
|
size = le32toh(size);
|
2015-10-14 22:40:23 +02:00
|
|
|
if (size + 5 > sizeof(buffer)) {
|
|
|
|
r = -EIO;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
|
|
|
if (p-5 < size)
|
|
|
|
continue;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
l = strv_parse_nulstr(buffer + 5, size);
|
2015-10-14 22:40:23 +02:00
|
|
|
if (!l) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
*ret = l;
|
2010-10-26 04:35:25 +02:00
|
|
|
break;
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
} else {
|
2010-10-26 04:35:25 +02:00
|
|
|
/* Unknown packet */
|
2015-10-14 22:40:23 +02:00
|
|
|
r = -EIO;
|
|
|
|
goto finish;
|
|
|
|
}
|
2010-10-26 04:35:25 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 22:40:23 +02:00
|
|
|
r = 0;
|
|
|
|
|
|
|
|
finish:
|
2017-02-06 02:05:27 +01:00
|
|
|
explicit_bzero(buffer, sizeof(buffer));
|
2015-10-14 22:40:23 +02:00
|
|
|
return r;
|
2010-10-26 04:35:25 +02:00
|
|
|
}
|
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
static int send_passwords(const char *socket_name, char **passwords) {
|
|
|
|
_cleanup_free_ char *packet = NULL;
|
|
|
|
_cleanup_close_ int socket_fd = -1;
|
|
|
|
union sockaddr_union sa = { .un.sun_family = AF_UNIX };
|
|
|
|
size_t packet_length = 1;
|
|
|
|
char **p, *d;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(socket_name);
|
|
|
|
|
|
|
|
STRV_FOREACH(p, passwords)
|
|
|
|
packet_length += strlen(*p) + 1;
|
|
|
|
|
|
|
|
packet = new(char, packet_length);
|
|
|
|
if (!packet)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
packet[0] = '+';
|
|
|
|
|
|
|
|
d = packet + 1;
|
|
|
|
STRV_FOREACH(p, passwords)
|
|
|
|
d = stpcpy(d, *p) + 1;
|
|
|
|
|
|
|
|
socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
|
|
|
if (socket_fd < 0) {
|
|
|
|
r = log_debug_errno(errno, "socket(): %m");
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
|
|
|
|
|
2016-05-05 22:24:36 +02:00
|
|
|
r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un));
|
2016-01-20 18:46:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
r = log_debug_errno(errno, "sendto(): %m");
|
|
|
|
|
|
|
|
finish:
|
2017-02-06 02:05:27 +01:00
|
|
|
explicit_bzero(packet, packet_length);
|
2016-01-20 18:46:51 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2010-10-26 04:45:27 +02:00
|
|
|
static int parse_password(const char *filename, char **wall) {
|
2016-01-20 18:46:51 +01:00
|
|
|
_cleanup_free_ char *socket_name = NULL, *message = NULL;
|
2015-10-14 22:40:23 +02:00
|
|
|
bool accept_cached = false, echo = false;
|
2010-10-25 20:35:17 +02:00
|
|
|
uint64_t not_after = 0;
|
|
|
|
unsigned pid = 0;
|
|
|
|
|
2011-08-01 00:43:05 +02:00
|
|
|
const ConfigTableItem items[] = {
|
|
|
|
{ "Ask", "Socket", config_parse_string, 0, &socket_name },
|
|
|
|
{ "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
|
|
|
|
{ "Ask", "Message", config_parse_string, 0, &message },
|
|
|
|
{ "Ask", "PID", config_parse_unsigned, 0, &pid },
|
|
|
|
{ "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
|
2014-10-03 15:53:45 +02:00
|
|
|
{ "Ask", "Echo", config_parse_bool, 0, &echo },
|
2014-08-03 18:52:03 +02:00
|
|
|
{}
|
2010-10-25 20:35:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
|
2014-07-17 00:27:12 +02:00
|
|
|
r = config_parse(NULL, filename, NULL,
|
|
|
|
NULL,
|
|
|
|
config_item_table_lookup, items,
|
|
|
|
true, false, true, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2011-04-13 21:42:46 +02:00
|
|
|
if (!socket_name) {
|
2010-10-25 20:35:17 +02:00
|
|
|
log_error("Invalid password file %s", filename);
|
2014-07-17 00:37:52 +02:00
|
|
|
return -EBADMSG;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-07-17 00:37:52 +02:00
|
|
|
if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
|
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2014-07-17 00:37:52 +02:00
|
|
|
if (pid > 0 && !pid_is_alive(pid))
|
|
|
|
return 0;
|
2011-04-13 21:43:36 +02:00
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
if (arg_action == ACTION_LIST)
|
|
|
|
printf("'%s' (PID %u)\n", message, pid);
|
2014-07-17 00:37:52 +02:00
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
else if (arg_action == ACTION_WALL) {
|
2010-10-26 04:45:27 +02:00
|
|
|
char *_wall;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2010-10-26 04:45:27 +02:00
|
|
|
if (asprintf(&_wall,
|
|
|
|
"%s%sPassword entry required for \'%s\' (PID %u).\r\n"
|
2010-11-12 03:05:20 +01:00
|
|
|
"Please enter password with the systemd-tty-ask-password-agent tool!",
|
2015-09-25 13:36:54 +02:00
|
|
|
strempty(*wall),
|
2010-10-26 04:45:27 +02:00
|
|
|
*wall ? "\r\n\r\n" : "",
|
2010-10-25 20:35:17 +02:00
|
|
|
message,
|
2014-07-17 00:37:52 +02:00
|
|
|
pid) < 0)
|
|
|
|
return log_oom();
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2010-10-26 04:45:27 +02:00
|
|
|
free(*wall);
|
|
|
|
*wall = _wall;
|
2014-07-17 00:37:52 +02:00
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
} else {
|
2016-01-20 18:46:51 +01:00
|
|
|
_cleanup_strv_free_erase_ char **passwords = NULL;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2017-09-29 00:37:23 +02:00
|
|
|
assert(IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH));
|
2010-10-25 20:35:17 +02:00
|
|
|
|
|
|
|
if (access(socket_name, W_OK) < 0) {
|
|
|
|
if (arg_action == ACTION_QUERY)
|
|
|
|
log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
|
|
|
|
|
2014-07-17 00:37:52 +02:00
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
if (arg_plymouth)
|
2015-10-07 11:26:10 +02:00
|
|
|
r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
|
2016-01-20 18:46:51 +01:00
|
|
|
else {
|
|
|
|
char *password = NULL;
|
2015-10-06 16:27:24 +02:00
|
|
|
int tty_fd = -1;
|
2010-11-16 04:28:04 +01:00
|
|
|
|
2014-07-17 00:37:52 +02:00
|
|
|
if (arg_console) {
|
2015-11-18 12:28:30 +01:00
|
|
|
const char *con = arg_device ? arg_device : "/dev/console";
|
|
|
|
|
|
|
|
tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY);
|
2014-07-17 00:37:52 +02:00
|
|
|
if (tty_fd < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_error_errno(tty_fd, "Failed to acquire /dev/console: %m");
|
2015-10-08 14:33:53 +02:00
|
|
|
|
|
|
|
r = reset_terminal_fd(tty_fd, true);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
|
2014-07-17 00:37:52 +02:00
|
|
|
}
|
2010-11-16 04:28:04 +01:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
r = ask_password_tty(message, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password);
|
2010-10-26 04:35:25 +02:00
|
|
|
|
2010-11-16 04:28:04 +01:00
|
|
|
if (arg_console) {
|
2015-10-06 16:27:24 +02:00
|
|
|
tty_fd = safe_close(tty_fd);
|
2010-11-16 04:28:04 +01:00
|
|
|
release_terminal();
|
|
|
|
}
|
2011-02-23 01:12:07 +01:00
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
if (r >= 0)
|
|
|
|
r = strv_push(&passwords, password);
|
2010-11-16 04:28:04 +01:00
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
string_free_erase(password);
|
2015-10-14 22:40:23 +02:00
|
|
|
}
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
/* If the query went away, that's OK */
|
|
|
|
if (IN_SET(r, -ETIME, -ENOENT))
|
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to query password: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2016-01-20 18:46:51 +01:00
|
|
|
r = send_passwords(socket_name, passwords);
|
2015-10-06 16:27:24 +02:00
|
|
|
if (r < 0)
|
2016-01-20 18:46:51 +01:00
|
|
|
return log_error_errno(r, "Failed to send: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-07-17 00:37:52 +02:00
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2010-11-16 04:28:04 +01:00
|
|
|
static int wall_tty_block(void) {
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_free_ char *p = NULL;
|
2011-02-17 16:29:04 +01:00
|
|
|
dev_t devnr;
|
2015-10-06 16:27:24 +02:00
|
|
|
int fd, r;
|
2010-11-12 03:33:08 +01:00
|
|
|
|
2011-06-27 22:44:12 +02:00
|
|
|
r = get_ctty_devnr(0, &devnr);
|
2015-10-07 11:26:10 +02:00
|
|
|
if (r == -ENXIO) /* We have no controlling tty */
|
|
|
|
return -ENOTTY;
|
2011-06-27 22:44:12 +02:00
|
|
|
if (r < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_error_errno(r, "Failed to get controlling TTY: %m");
|
2010-11-12 03:33:08 +01:00
|
|
|
|
2011-03-25 05:07:20 +01:00
|
|
|
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_oom();
|
2010-11-12 03:33:08 +01:00
|
|
|
|
2012-05-31 12:40:20 +02:00
|
|
|
mkdir_parents_label(p, 0700);
|
2010-11-12 03:33:08 +01:00
|
|
|
mkfifo(p, 0600);
|
|
|
|
|
|
|
|
fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
|
|
|
if (fd < 0)
|
2015-10-14 16:24:25 +02:00
|
|
|
return log_debug_errno(errno, "Failed to open %s: %m", p);
|
2010-11-12 03:33:08 +01:00
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2015-04-24 15:31:29 +02:00
|
|
|
static bool wall_tty_match(const char *path, void *userdata) {
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_free_ char *p = NULL;
|
2015-10-06 16:27:24 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
|
|
|
struct stat st;
|
2011-02-17 16:29:04 +01:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
if (!path_is_absolute(path))
|
2015-02-03 02:05:59 +01:00
|
|
|
path = strjoina("/dev/", path);
|
2011-02-17 16:29:04 +01:00
|
|
|
|
2015-10-06 16:27:24 +02:00
|
|
|
if (lstat(path, &st) < 0) {
|
|
|
|
log_debug_errno(errno, "Failed to stat %s: %m", path);
|
2011-02-17 16:29:04 +01:00
|
|
|
return true;
|
2015-10-06 16:27:24 +02:00
|
|
|
}
|
2011-02-17 16:29:04 +01:00
|
|
|
|
2015-10-06 16:27:24 +02:00
|
|
|
if (!S_ISCHR(st.st_mode)) {
|
|
|
|
log_debug("%s is not a character device.", path);
|
2011-02-17 16:29:04 +01:00
|
|
|
return true;
|
2015-10-06 16:27:24 +02:00
|
|
|
}
|
2010-11-12 03:33:08 +01:00
|
|
|
|
|
|
|
/* We use named pipes to ensure that wall messages suggesting
|
|
|
|
* password entry are not printed over password prompts
|
|
|
|
* already shown. We use the fact here that opening a pipe in
|
|
|
|
* non-blocking mode for write-only will succeed only if
|
|
|
|
* there's some writer behind it. Using pipes has the
|
|
|
|
* advantage that the block will automatically go away if the
|
|
|
|
* process dies. */
|
|
|
|
|
2015-10-06 16:27:24 +02:00
|
|
|
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
|
|
|
|
log_oom();
|
2010-11-12 03:33:08 +01:00
|
|
|
return true;
|
2015-10-06 16:27:24 +02:00
|
|
|
}
|
2010-11-12 03:33:08 +01:00
|
|
|
|
|
|
|
fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
2015-10-06 16:27:24 +02:00
|
|
|
if (fd < 0) {
|
|
|
|
log_debug_errno(errno, "Failed top open the wall pipe: %m");
|
|
|
|
return 1;
|
|
|
|
}
|
2010-11-12 03:33:08 +01:00
|
|
|
|
|
|
|
/* What, we managed to open the pipe? Then this tty is filtered. */
|
2015-10-06 16:27:24 +02:00
|
|
|
return 0;
|
2010-11-12 03:33:08 +01:00
|
|
|
}
|
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
static int show_passwords(void) {
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_closedir_ DIR *d;
|
2010-10-25 20:35:17 +02:00
|
|
|
struct dirent *de;
|
|
|
|
int r = 0;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
d = opendir("/run/systemd/ask-password");
|
|
|
|
if (!d) {
|
2010-10-25 20:35:17 +02:00
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
|
2015-11-05 13:44:01 +01:00
|
|
|
return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2015-10-06 16:27:24 +02:00
|
|
|
FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_free_ char *p = NULL, *wall = NULL;
|
2010-10-25 20:35:17 +02:00
|
|
|
int q;
|
|
|
|
|
2011-03-03 16:29:50 +01:00
|
|
|
/* We only support /dev on tmpfs, hence we can rely on
|
|
|
|
* d_type to be reliable */
|
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
if (de->d_type != DT_REG)
|
|
|
|
continue;
|
|
|
|
|
2016-04-27 15:24:59 +02:00
|
|
|
if (hidden_or_backup_file(de->d_name))
|
2010-10-25 20:35:17 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!startswith(de->d_name, "ask."))
|
|
|
|
continue;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
p = strappend("/run/systemd/ask-password/", de->d_name);
|
|
|
|
if (!p)
|
|
|
|
return log_oom();
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
q = parse_password(p, &wall);
|
|
|
|
if (q < 0 && r == 0)
|
2010-10-25 20:35:17 +02:00
|
|
|
r = q;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
if (wall)
|
2015-10-06 16:27:24 +02:00
|
|
|
(void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int watch_passwords(void) {
|
2010-10-26 15:15:00 +02:00
|
|
|
enum {
|
|
|
|
FD_INOTIFY,
|
|
|
|
FD_SIGNAL,
|
|
|
|
_FD_MAX
|
|
|
|
};
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
_cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
|
2013-03-25 00:59:00 +01:00
|
|
|
struct pollfd pollfd[_FD_MAX] = {};
|
2010-10-26 15:15:00 +02:00
|
|
|
sigset_t mask;
|
2010-10-25 20:35:17 +02:00
|
|
|
int r;
|
|
|
|
|
2010-11-16 04:28:04 +01:00
|
|
|
tty_block_fd = wall_tty_block();
|
2010-11-12 03:33:08 +01:00
|
|
|
|
2015-10-06 16:27:24 +02:00
|
|
|
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
notify = inotify_init1(IN_CLOEXEC);
|
|
|
|
if (notify < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_error_errno(errno, "Failed to allocate directory watch: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2015-06-15 20:13:23 +02:00
|
|
|
assert_se(sigemptyset(&mask) >= 0);
|
|
|
|
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
|
|
|
|
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
|
2010-10-26 15:15:00 +02:00
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
|
|
|
|
if (signal_fd < 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
|
2010-10-26 15:15:00 +02:00
|
|
|
|
|
|
|
pollfd[FD_INOTIFY].fd = notify;
|
|
|
|
pollfd[FD_INOTIFY].events = POLLIN;
|
|
|
|
pollfd[FD_SIGNAL].fd = signal_fd;
|
|
|
|
pollfd[FD_SIGNAL].events = POLLIN;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
|
|
|
for (;;) {
|
2014-08-03 18:52:03 +02:00
|
|
|
r = show_passwords();
|
|
|
|
if (r < 0)
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to show password: %m");
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2010-10-26 15:15:00 +02:00
|
|
|
if (poll(pollfd, _FD_MAX, -1) < 0) {
|
2010-10-25 20:35:17 +02:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
return -errno;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2010-10-26 15:15:00 +02:00
|
|
|
if (pollfd[FD_INOTIFY].revents != 0)
|
2015-10-06 16:27:24 +02:00
|
|
|
(void) flush_fd(notify);
|
2010-10-26 15:15:00 +02:00
|
|
|
|
|
|
|
if (pollfd[FD_SIGNAL].revents != 0)
|
|
|
|
break;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-02 17:12:21 +02:00
|
|
|
static void help(void) {
|
2010-10-25 20:35:17 +02:00
|
|
|
printf("%s [OPTIONS...]\n\n"
|
|
|
|
"Process system password requests.\n\n"
|
2010-10-26 04:35:25 +02:00
|
|
|
" -h --help Show this help\n"
|
2012-06-27 02:12:39 +02:00
|
|
|
" --version Show package version\n"
|
2010-10-26 04:35:25 +02:00
|
|
|
" --list Show pending password requests\n"
|
|
|
|
" --query Process pending password requests\n"
|
2011-02-21 15:32:17 +01:00
|
|
|
" --watch Continuously process password requests\n"
|
|
|
|
" --wall Continuously forward password requests to wall\n"
|
2010-11-16 04:28:04 +01:00
|
|
|
" --plymouth Ask question with Plymouth instead of on TTY\n"
|
|
|
|
" --console Ask question on /dev/console instead of current TTY\n",
|
2010-10-25 20:35:17 +02:00
|
|
|
program_invocation_short_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ARG_LIST = 0x100,
|
|
|
|
ARG_QUERY,
|
|
|
|
ARG_WATCH,
|
|
|
|
ARG_WALL,
|
2010-11-16 04:28:04 +01:00
|
|
|
ARG_PLYMOUTH,
|
2012-06-27 02:12:39 +02:00
|
|
|
ARG_CONSOLE,
|
|
|
|
ARG_VERSION
|
2010-10-25 20:35:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
2015-11-18 12:28:30 +01:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
|
|
{ "list", no_argument, NULL, ARG_LIST },
|
|
|
|
{ "query", no_argument, NULL, ARG_QUERY },
|
|
|
|
{ "watch", no_argument, NULL, ARG_WATCH },
|
|
|
|
{ "wall", no_argument, NULL, ARG_WALL },
|
|
|
|
{ "plymouth", no_argument, NULL, ARG_PLYMOUTH },
|
|
|
|
{ "console", optional_argument, NULL, ARG_CONSOLE },
|
2013-11-06 18:28:39 +01:00
|
|
|
{}
|
2010-10-25 20:35:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
assert(argc >= 0);
|
|
|
|
assert(argv);
|
|
|
|
|
2014-08-02 17:12:21 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
2010-10-25 20:35:17 +02:00
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
case 'h':
|
2014-08-02 17:12:21 +02:00
|
|
|
help();
|
|
|
|
return 0;
|
2010-10-25 20:35:17 +02:00
|
|
|
|
2012-06-27 02:12:39 +02:00
|
|
|
case ARG_VERSION:
|
2015-09-23 03:01:06 +02:00
|
|
|
return version();
|
2012-06-27 02:12:39 +02:00
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
case ARG_LIST:
|
|
|
|
arg_action = ACTION_LIST;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_QUERY:
|
|
|
|
arg_action = ACTION_QUERY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_WATCH:
|
|
|
|
arg_action = ACTION_WATCH;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_WALL:
|
|
|
|
arg_action = ACTION_WALL;
|
|
|
|
break;
|
|
|
|
|
2010-10-26 04:35:25 +02:00
|
|
|
case ARG_PLYMOUTH:
|
|
|
|
arg_plymouth = true;
|
|
|
|
break;
|
|
|
|
|
2010-11-16 04:28:04 +01:00
|
|
|
case ARG_CONSOLE:
|
|
|
|
arg_console = true;
|
2015-11-18 12:28:30 +01:00
|
|
|
if (optarg) {
|
|
|
|
|
|
|
|
if (isempty(optarg)) {
|
|
|
|
log_error("Empty console device path is not allowed.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg_device = optarg;
|
|
|
|
}
|
2010-11-16 04:28:04 +01:00
|
|
|
break;
|
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
2013-11-06 18:28:39 +01:00
|
|
|
assert_not_reached("Unhandled option");
|
2010-10-25 20:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (optind != argc) {
|
2014-08-02 17:12:21 +02:00
|
|
|
log_error("%s takes no arguments.", program_invocation_short_name);
|
2010-10-25 20:35:17 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-11-18 12:28:30 +01:00
|
|
|
if (arg_plymouth || arg_console) {
|
|
|
|
|
|
|
|
if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) {
|
|
|
|
log_error("Options --query and --watch conflict.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg_plymouth && arg_console) {
|
|
|
|
log_error("Options --plymouth and --console conflict.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-18 12:28:30 +01:00
|
|
|
/*
|
|
|
|
* To be able to ask on all terminal devices of /dev/console
|
|
|
|
* the devices are collected. If more than one device is found,
|
|
|
|
* then on each of the terminals a inquiring task is forked.
|
|
|
|
* Every task has its own session and its own controlling terminal.
|
|
|
|
* If one of the tasks does handle a password, the remaining tasks
|
|
|
|
* will be terminated.
|
|
|
|
*/
|
|
|
|
static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) {
|
|
|
|
struct sigaction sig = {
|
|
|
|
.sa_handler = nop_signal_handler,
|
|
|
|
.sa_flags = SA_NOCLDSTOP | SA_RESTART,
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
|
|
|
|
|
|
|
|
assert_se(sigemptyset(&sig.sa_mask) >= 0);
|
|
|
|
assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0);
|
|
|
|
|
|
|
|
sig.sa_handler = SIG_DFL;
|
|
|
|
assert_se(sigaction(SIGHUP, &sig, NULL) >= 0);
|
|
|
|
|
|
|
|
*pid = fork();
|
|
|
|
if (*pid < 0)
|
|
|
|
return log_error_errno(errno, "Failed to fork process: %m");
|
|
|
|
|
|
|
|
if (*pid == 0) {
|
|
|
|
int ac;
|
|
|
|
|
|
|
|
assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
|
|
|
|
|
|
|
|
reset_signal_mask();
|
|
|
|
reset_all_signal_handlers();
|
|
|
|
|
|
|
|
for (ac = 0; ac < argc; ac++) {
|
|
|
|
if (streq(argv[ac], "--console")) {
|
|
|
|
argv[ac] = strjoina("--console=", tty, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(ac < argc);
|
|
|
|
|
|
|
|
execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv);
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void terminate_agents(Set *pids) {
|
|
|
|
struct timespec ts;
|
|
|
|
siginfo_t status = {};
|
|
|
|
sigset_t set;
|
|
|
|
Iterator i;
|
|
|
|
void *p;
|
|
|
|
int r, signum;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Request termination of the remaining processes as those
|
|
|
|
* are not required anymore.
|
|
|
|
*/
|
|
|
|
SET_FOREACH(p, pids, i)
|
|
|
|
(void) kill(PTR_TO_PID(p), SIGTERM);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect the processes which have go away.
|
|
|
|
*/
|
|
|
|
assert_se(sigemptyset(&set) >= 0);
|
|
|
|
assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
|
|
|
timespec_store(&ts, 50 * USEC_PER_MSEC);
|
|
|
|
|
|
|
|
while (!set_isempty(pids)) {
|
|
|
|
|
|
|
|
zero(status);
|
|
|
|
r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
|
|
|
|
if (r < 0 && errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (r == 0 && status.si_pid > 0) {
|
|
|
|
set_remove(pids, PID_TO_PTR(status.si_pid));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
signum = sigtimedwait(&set, NULL, &ts);
|
|
|
|
if (signum < 0) {
|
|
|
|
if (errno != EAGAIN)
|
|
|
|
log_error_errno(errno, "sigtimedwait() failed: %m");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(signum == SIGCHLD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kill hanging processes.
|
|
|
|
*/
|
|
|
|
SET_FOREACH(p, pids, i) {
|
|
|
|
log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
|
|
|
|
(void) kill(PTR_TO_PID(p), SIGKILL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ask_on_consoles(int argc, char *argv[]) {
|
|
|
|
_cleanup_set_free_ Set *pids = NULL;
|
|
|
|
_cleanup_strv_free_ char **consoles = NULL;
|
|
|
|
siginfo_t status = {};
|
|
|
|
char **tty;
|
|
|
|
pid_t pid;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = get_kernel_consoles(&consoles);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
|
|
|
|
|
|
|
|
pids = set_new(NULL);
|
|
|
|
if (!pids)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
/* Start an agent on each console. */
|
|
|
|
STRV_FOREACH(tty, consoles) {
|
|
|
|
r = ask_on_this_console(*tty, &pid, argc, argv);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (set_put(pids, PID_TO_PTR(pid)) < 0)
|
|
|
|
return log_oom();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for an agent to exit. */
|
|
|
|
for (;;) {
|
|
|
|
zero(status);
|
|
|
|
|
|
|
|
if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return log_error_errno(errno, "waitid() failed: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
set_remove(pids, PID_TO_PTR(status.si_pid));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-10 22:07:30 +02:00
|
|
|
if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
|
2015-11-18 12:28:30 +01:00
|
|
|
log_error("Password agent failed with: %d", status.si_status);
|
|
|
|
|
|
|
|
terminate_agents(pids);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
int r;
|
|
|
|
|
2012-07-13 15:45:45 +02:00
|
|
|
log_set_target(LOG_TARGET_AUTO);
|
2010-10-25 20:35:17 +02:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
2011-08-01 20:52:18 +02:00
|
|
|
umask(0022);
|
|
|
|
|
2014-08-03 18:52:03 +02:00
|
|
|
r = parse_argv(argc, argv);
|
|
|
|
if (r <= 0)
|
2010-10-25 20:35:17 +02:00
|
|
|
goto finish;
|
|
|
|
|
2015-11-18 12:28:30 +01:00
|
|
|
if (arg_console && !arg_device)
|
|
|
|
/*
|
|
|
|
* Spawn for each console device a separate process.
|
|
|
|
*/
|
|
|
|
r = ask_on_consoles(argc, argv);
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (arg_device) {
|
|
|
|
/*
|
|
|
|
* Later on, a controlling terminal will be acquired,
|
|
|
|
* therefore the current process has to become a session
|
|
|
|
* leader and should not have a controlling terminal already.
|
|
|
|
*/
|
|
|
|
(void) setsid();
|
|
|
|
(void) release_terminal();
|
|
|
|
}
|
2010-11-16 04:28:04 +01:00
|
|
|
|
2015-11-18 12:28:30 +01:00
|
|
|
if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
|
|
|
|
r = watch_passwords();
|
|
|
|
else
|
|
|
|
r = show_passwords();
|
|
|
|
}
|
2010-11-16 03:12:54 +01:00
|
|
|
|
2010-10-25 20:35:17 +02:00
|
|
|
finish:
|
|
|
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
|
|
}
|