tty-ask-password-agent.c revision 092c4c437f30bb030deae89cc81109a624898b36
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik/***
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik This file is part of systemd.
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik Copyright 2010 Lennart Poettering
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik systemd is free software; you can redistribute it and/or modify it
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik under the terms of the GNU Lesser General Public License as published by
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik the Free Software Foundation; either version 2.1 of the License, or
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik (at your option) any later version.
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik systemd is distributed in the hope that it will be useful, but
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik WITHOUT ANY WARRANTY; without even the implied warranty of
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik Lesser General Public License for more details.
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik You should have received a copy of the GNU Lesser General Public License
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik along with systemd; If not, see <http://www.gnu.org/licenses/>.
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik***/
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <stdbool.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <errno.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <string.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <sys/socket.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <sys/un.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <stddef.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <sys/poll.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <sys/inotify.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <unistd.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <getopt.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <sys/signalfd.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include <fcntl.h>
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "util.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "mkdir.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "path-util.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "conf-parser.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "utmp-wtmp.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "socket-util.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "ask-password-api.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "strv.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik#include "build.h"
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvikstatic enum {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik ACTION_LIST,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik ACTION_QUERY,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik ACTION_WATCH,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik ACTION_WALL
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik} arg_action = ACTION_QUERY;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvikstatic bool arg_plymouth = false;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvikstatic bool arg_console = false;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvikstatic int ask_password_plymouth(
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik const char *message,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik usec_t until,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik const char *flag_file,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik bool accept_cached,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik char ***_passphrases) {
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik int fd = -1, notify = -1;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik union sockaddr_union sa;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik char *packet = NULL;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik ssize_t k;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik int r, n;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik struct pollfd pollfd[2];
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik char buffer[LINE_MAX];
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik size_t p = 0;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik enum {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik POLL_SOCKET,
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik POLL_INOTIFY
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik };
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik assert(_passphrases);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (flag_file) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik zero(sa);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik sa.sa.sa_family = AF_UNIX;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik log_error("Failed to connect to Plymouth: %m");
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (accept_cached) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik packet = strdup("c");
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik n = 1;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik } else
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (!packet) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -ENOMEM;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = k < 0 ? (int) k : -EIO;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik zero(pollfd);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik pollfd[POLL_SOCKET].fd = fd;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik pollfd[POLL_SOCKET].events = POLLIN;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik pollfd[POLL_INOTIFY].fd = notify;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik pollfd[POLL_INOTIFY].events = POLLIN;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik for (;;) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik int sleep_for = -1, j;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (until > 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik usec_t y;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik y = now(CLOCK_MONOTONIC);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (y > until) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -ETIME;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik sleep_for = (int) ((until - y) / USEC_PER_MSEC);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (flag_file)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (access(flag_file, F_OK) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (errno == EINTR)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik continue;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -errno;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik } else if (j == 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -ETIME;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik flush_fd(notify);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (pollfd[POLL_SOCKET].revents == 0)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik continue;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = k < 0 ? -errno : -EIO;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik p += k;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (p < 1)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik continue;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (buffer[0] == 5) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (accept_cached) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik /* Hmm, first try with cached
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik * passwords failed, so let's retry
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik * with a normal password request */
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik free(packet);
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik packet = NULL;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -ENOMEM;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = k < 0 ? (int) k : -EIO;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik accept_cached = false;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik p = 0;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik continue;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik }
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik /* No password, because UI not shown */
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = -ENOENT;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik goto finish;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik } else if (buffer[0] == 2 || buffer[0] == 9) {
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik uint32_t size;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik char **l;
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik /* One ore more answers */
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik if (p < 5)
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik continue;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen memcpy(&size, buffer+1, sizeof(size));
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik size = le32toh(size);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (size+5 > sizeof(buffer)) {
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik r = -EIO;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik goto finish;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik }
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (p-5 < size)
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik continue;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (!(l = strv_parse_nulstr(buffer + 5, size))) {
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik r = -ENOMEM;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik goto finish;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik }
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik *_passphrases = l;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik break;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik } else {
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen /* Unknown packet */
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen r = -EIO;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik goto finish;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik }
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik }
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik r = 0;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvikfinish:
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen if (notify >= 0)
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik close_nointr_nofail(notify);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (fd >= 0)
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik close_nointr_nofail(fd);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik free(packet);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik return r;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik}
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvikstatic int parse_password(const char *filename, char **wall) {
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik char *socket_name = NULL, *message = NULL, *packet = NULL;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik uint64_t not_after = 0;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik unsigned pid = 0;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik int socket_fd = -1;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik bool accept_cached = false;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik const ConfigTableItem items[] = {
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik { "Ask", "Socket", config_parse_string, 0, &socket_name },
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik { "Ask", "NotAfter", config_parse_uint64, 0, &not_after },
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen { "Ask", "Message", config_parse_string, 0, &message },
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik { "Ask", "PID", config_parse_unsigned, 0, &pid },
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik { NULL, NULL, NULL, 0, NULL }
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik };
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik FILE *f;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik int r;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik assert(filename);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik f = fopen(filename, "re");
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (!f) {
71e31fc573ba527f0d2be7929c2cb98037c860ecKnut Anders Hatlen if (errno == ENOENT)
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik return 0;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik log_error("open(%s): %m", filename);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik return -errno;
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik }
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik
8d22687fbdc540bd0b4d05fd90d87fb6037f4b9fJorgen Austvik r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
381a9b85cf2c73401fc1bff06c2e0d86389a5e88Jorgen Austvik if (r < 0) {
log_error("Failed to parse password file %s: %s", filename, strerror(-r));
goto finish;
}
if (!socket_name) {
log_error("Invalid password file %s", filename);
r = -EBADMSG;
goto finish;
}
if (not_after > 0) {
if (now(CLOCK_MONOTONIC) > not_after) {
r = 0;
goto finish;
}
}
if (pid > 0 &&
kill(pid, 0) < 0 &&
errno == ESRCH) {
r = 0;
goto finish;
}
if (arg_action == ACTION_LIST)
printf("'%s' (PID %u)\n", message, pid);
else if (arg_action == ACTION_WALL) {
char *_wall;
if (asprintf(&_wall,
"%s%sPassword entry required for \'%s\' (PID %u).\r\n"
"Please enter password with the systemd-tty-ask-password-agent tool!",
*wall ? *wall : "",
*wall ? "\r\n\r\n" : "",
message,
pid) < 0) {
r = log_oom();
goto finish;
}
free(*wall);
*wall = _wall;
} else {
union {
struct sockaddr sa;
struct sockaddr_un un;
} sa;
size_t packet_length = 0;
assert(arg_action == ACTION_QUERY ||
arg_action == ACTION_WATCH);
if (access(socket_name, W_OK) < 0) {
if (arg_action == ACTION_QUERY)
log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
r = 0;
goto finish;
}
if (arg_plymouth) {
char **passwords = NULL;
if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
char **p;
packet_length = 1;
STRV_FOREACH(p, passwords)
packet_length += strlen(*p) + 1;
if (!(packet = new(char, packet_length)))
r = -ENOMEM;
else {
char *d;
packet[0] = '+';
d = packet+1;
STRV_FOREACH(p, passwords)
d = stpcpy(d, *p) + 1;
}
}
} else {
int tty_fd = -1;
char *password;
if (arg_console)
if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
r = tty_fd;
goto finish;
}
r = ask_password_tty(message, not_after, filename, &password);
if (arg_console) {
close_nointr_nofail(tty_fd);
release_terminal();
}
if (r >= 0) {
packet_length = 1+strlen(password)+1;
if (!(packet = new(char, packet_length)))
r = -ENOMEM;
else {
packet[0] = '+';
strcpy(packet+1, password);
}
free(password);
}
}
if (r == -ETIME || r == -ENOENT) {
/* If the query went away, that's OK */
r = 0;
goto finish;
}
if (r < 0) {
log_error("Failed to query password: %s", strerror(-r));
goto finish;
}
if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
log_error("socket(): %m");
r = -errno;
goto finish;
}
zero(sa);
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
log_error("Failed to send: %m");
r = -errno;
goto finish;
}
}
finish:
fclose(f);
if (socket_fd >= 0)
close_nointr_nofail(socket_fd);
free(packet);
free(socket_name);
free(message);
return r;
}
static int wall_tty_block(void) {
char *p;
int fd, r;
dev_t devnr;
r = get_ctty_devnr(0, &devnr);
if (r < 0)
return -r;
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
return -ENOMEM;
mkdir_parents_label(p, 0700);
mkfifo(p, 0600);
fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
free(p);
if (fd < 0)
return -errno;
return fd;
}
static bool wall_tty_match(const char *path) {
int fd, k;
char *p;
struct stat st;
if (path_is_absolute(path))
k = lstat(path, &st);
else {
if (asprintf(&p, "/dev/%s", path) < 0)
return true;
k = lstat(p, &st);
free(p);
}
if (k < 0)
return true;
if (!S_ISCHR(st.st_mode))
return true;
/* 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. */
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
return true;
fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
free(p);
if (fd < 0)
return true;
/* What, we managed to open the pipe? Then this tty is filtered. */
close_nointr_nofail(fd);
return false;
}
static int show_passwords(void) {
DIR *d;
struct dirent *de;
int r = 0;
if (!(d = opendir("/run/systemd/ask-password"))) {
if (errno == ENOENT)
return 0;
log_error("opendir(): %m");
return -errno;
}
while ((de = readdir(d))) {
char *p;
int q;
char *wall;
/* We only support /dev on tmpfs, hence we can rely on
* d_type to be reliable */
if (de->d_type != DT_REG)
continue;
if (ignore_file(de->d_name))
continue;
if (!startswith(de->d_name, "ask."))
continue;
if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
r = log_oom();
goto finish;
}
wall = NULL;
if ((q = parse_password(p, &wall)) < 0)
r = q;
free(p);
if (wall) {
utmp_wall(wall, wall_tty_match);
free(wall);
}
}
finish:
if (d)
closedir(d);
return r;
}
static int watch_passwords(void) {
enum {
FD_INOTIFY,
FD_SIGNAL,
_FD_MAX
};
int notify = -1, signal_fd = -1, tty_block_fd = -1;
struct pollfd pollfd[_FD_MAX];
sigset_t mask;
int r;
tty_block_fd = wall_tty_block();
mkdir_p_label("/run/systemd/ask-password", 0755);
if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
r = -errno;
goto finish;
}
if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
r = -errno;
goto finish;
}
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGINT, SIGTERM, -1);
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
log_error("signalfd(): %m");
r = -errno;
goto finish;
}
zero(pollfd);
pollfd[FD_INOTIFY].fd = notify;
pollfd[FD_INOTIFY].events = POLLIN;
pollfd[FD_SIGNAL].fd = signal_fd;
pollfd[FD_SIGNAL].events = POLLIN;
for (;;) {
if ((r = show_passwords()) < 0)
log_error("Failed to show password: %s", strerror(-r));
if (poll(pollfd, _FD_MAX, -1) < 0) {
if (errno == EINTR)
continue;
r = -errno;
goto finish;
}
if (pollfd[FD_INOTIFY].revents != 0)
flush_fd(notify);
if (pollfd[FD_SIGNAL].revents != 0)
break;
}
r = 0;
finish:
if (notify >= 0)
close_nointr_nofail(notify);
if (signal_fd >= 0)
close_nointr_nofail(signal_fd);
if (tty_block_fd >= 0)
close_nointr_nofail(tty_block_fd);
return r;
}
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Process system password requests.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --list Show pending password requests\n"
" --query Process pending password requests\n"
" --watch Continuously process password requests\n"
" --wall Continuously forward password requests to wall\n"
" --plymouth Ask question with Plymouth instead of on TTY\n"
" --console Ask question on /dev/console instead of current TTY\n",
program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_LIST = 0x100,
ARG_QUERY,
ARG_WATCH,
ARG_WALL,
ARG_PLYMOUTH,
ARG_CONSOLE,
ARG_VERSION
};
static const struct option options[] = {
{ "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", no_argument, NULL, ARG_CONSOLE },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
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;
case ARG_PLYMOUTH:
arg_plymouth = true;
break;
case ARG_CONSOLE:
arg_console = true;
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
if (optind != argc) {
help();
return -EINVAL;
}
return 1;
}
int main(int argc, char *argv[]) {
int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
if ((r = parse_argv(argc, argv)) <= 0)
goto finish;
if (arg_console) {
setsid();
release_terminal();
}
if (arg_action == ACTION_WATCH ||
arg_action == ACTION_WALL)
r = watch_passwords();
else
r = show_passwords();
if (r < 0)
log_error("Error: %s", strerror(-r));
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}