X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fask-password.c;h=5162f62eeec95cd52f8b60c93fbab200d3c39ad1;hp=bcee6863c996e85a22ed2ebfe572fcc2de71ef5d;hb=91f9dcaf9270fe465525638cc08bd94590273349;hpb=3e21c85da365f66582d77eb0a81016c7f43c7762 diff --git a/src/ask-password.c b/src/ask-password.c index bcee6863c..5162f62ee 100644 --- a/src/ask-password.c +++ b/src/ask-password.c @@ -33,71 +33,32 @@ #include #include #include +#include #include "log.h" #include "macro.h" #include "util.h" +#include "strv.h" +#include "ask-password-api.h" +#include "def.h" static const char *arg_icon = NULL; static const char *arg_message = NULL; static bool arg_use_tty = true; -static usec_t arg_timeout = 60 * USEC_PER_SEC; - -static int create_socket(char **name) { - int fd; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; - int one = 1, r; - char *c; - - assert(name); - - if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { - log_error("socket() failed: %m"); - return -errno; - } - - zero(sa); - sa.un.sun_family = AF_UNIX; - snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, "/org/freedesktop/systemd1/ask-password/%llu", random_ull()); - - if (bind(fd, &sa.sa, sizeof(sa_family_t) + 1 + strlen(sa.un.sun_path+1)) < 0) { - r = -errno; - log_error("bind() failed: %m"); - goto fail; - } - - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - r = -errno; - log_error("SO_PASSCRED failed: %m"); - goto fail; - } - - if (!(c = strdup(sa.un.sun_path+1))) { - r = -ENOMEM; - log_error("Out of memory"); - goto fail; - } - - *name = c; - return fd; - -fail: - close_nointr_nofail(fd); - - return r; -} +static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; +static bool arg_accept_cached = false; +static bool arg_multiple = false; static int help(void) { printf("%s [OPTIONS...] MESSAGE\n\n" "Query the user for a system passphrase, via the TTY or an UI agent.\n\n" - " -h --help Show this help\n" - " --icon=NAME Icon name\n" - " --timeout=USEC Timeout in usec\n" - " --no-tty Ask question via agent even on TTY\n", + " -h --help Show this help\n" + " --icon=NAME Icon name\n" + " --timeout=SEC Timeout in sec\n" + " --no-tty Ask question via agent even on TTY\n" + " --accept-cached Accept cached passwords\n" + " --multiple List multiple passwords if available\n", program_invocation_short_name); return 0; @@ -108,15 +69,19 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_ICON = 0x100, ARG_TIMEOUT, - ARG_NO_TTY + ARG_NO_TTY, + ARG_ACCEPT_CACHED, + ARG_MULTIPLE }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "icon", required_argument, NULL, ARG_ICON }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { "no-tty", no_argument, NULL, ARG_NO_TTY }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "icon", required_argument, NULL, ARG_ICON }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "no-tty", no_argument, NULL, ARG_NO_TTY }, + { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED }, + { "multiple", no_argument, NULL, ARG_MULTIPLE }, + { NULL, 0, NULL, 0 } }; int c; @@ -137,7 +102,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TIMEOUT: - if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) { + if (parse_usec(optarg, &arg_timeout) < 0) { log_error("Failed to parse --timeout parameter %s", optarg); return -EINVAL; } @@ -147,6 +112,14 @@ static int parse_argv(int argc, char *argv[]) { arg_use_tty = false; break; + case ARG_ACCEPT_CACHED: + arg_accept_cached = true; + break; + + case ARG_MULTIPLE: + arg_multiple = true; + break; + case '?': return -EINVAL; @@ -165,305 +138,47 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int ask_agent(void) { - char temp[] = "/dev/.systemd/ask-password/tmp.XXXXXX"; - char final[sizeof(temp)] = ""; - int fd = -1, r; - FILE *f = NULL; - char *socket_name = NULL; - int socket_fd = -1, signal_fd; - sigset_t mask; - usec_t not_after; - - if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) { - log_error("Failed to create password file: %m"); - r = -errno; - goto finish; - } - - fchmod(fd, 0644); - - if (!(f = fdopen(fd, "w"))) { - log_error("Failed to allocate FILE: %m"); - r = -errno; - goto finish; - } - - fd = -1; - - 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; - } - - if ((socket_fd = create_socket(&socket_name)) < 0) { - r = socket_fd; - goto finish; - } - - not_after = now(CLOCK_MONOTONIC) + arg_timeout; - - fprintf(f, - "[Ask]\n" - "Socket=%s\n" - "NotAfter=%llu\n", - socket_name, - (unsigned long long) not_after); - - if (arg_message) - fprintf(f, "Message=%s\n", arg_message); - - if (arg_icon) - fprintf(f, "Icon=%s\n", arg_icon); - - fflush(f); - - if (ferror(f)) { - log_error("Failed to write query file: %m"); - r = -errno; - goto finish; - } - - memcpy(final, temp, sizeof(temp)); +int main(int argc, char *argv[]) { + int r; + usec_t timeout; - final[sizeof(final)-11] = 'a'; - final[sizeof(final)-10] = 's'; - final[sizeof(final)-9] = 'k'; + log_parse_environment(); + log_open(); - if (rename(temp, final) < 0) { - log_error("Failed to rename query file: %m"); - r = -errno; + if ((r = parse_argv(argc, argv)) <= 0) goto finish; - } - - for (;;) { - enum { - FD_SOCKET, - FD_SIGNAL, - _FD_MAX - }; - - char passphrase[LINE_MAX+1]; - struct msghdr msghdr; - struct iovec iovec; - struct ucred *ucred; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - ssize_t n; - struct pollfd pollfd[_FD_MAX]; - int k; - - zero(pollfd); - pollfd[FD_SOCKET].fd = socket_fd; - pollfd[FD_SOCKET].events = POLLIN; - pollfd[FD_SIGNAL].fd = signal_fd; - pollfd[FD_SIGNAL].events = POLLIN; - - if ((k = poll(pollfd, 2, arg_timeout/USEC_PER_MSEC)) < 0) { - - if (errno == EINTR) - continue; - - log_error("poll() failed: %m"); - r = -errno; - goto finish; - } - - if (k <= 0) { - log_notice("Timed out"); - r = -ETIME; - goto finish; - } - - if (pollfd[FD_SIGNAL].revents & POLLIN) - break; - - if (pollfd[FD_SOCKET].revents != POLLIN) { - log_error("Unexpected poll() event."); - r = -EIO; - goto finish; - } - - zero(iovec); - iovec.iov_base = passphrase; - iovec.iov_len = sizeof(passphrase)-1; - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); - - if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) { - - if (errno == EAGAIN || - errno == EINTR) - continue; - - log_error("recvmsg() failed: %m"); - r = -errno; - goto finish; - } - - if (n <= 0) { - log_error("Message too short"); - continue; - } - - if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || - control.cmsghdr.cmsg_level != SOL_SOCKET || - control.cmsghdr.cmsg_type != SCM_CREDENTIALS || - control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_warning("Received message without credentials. Ignoring."); - continue; - } + if (arg_timeout > 0) + timeout = now(CLOCK_MONOTONIC) + arg_timeout; + else + timeout = 0; - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - if (ucred->uid != 0) { - log_warning("Got request from unprivileged user. Ignoring."); - continue; - } + if (arg_use_tty && isatty(STDIN_FILENO)) { + char *password = NULL; - if (passphrase[0] == '+') { - passphrase[n] = 0; - fputs(passphrase+1, stdout); - fflush(stdout); - } else if (passphrase[0] == '-') { - r = -ECANCELED; - goto finish; - } else { - log_error("Invalid packet"); - continue; + if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) { + puts(password); + free(password); } - break; - } - - r = 0; - -finish: - if (fd >= 0) - close_nointr_nofail(fd); - - if (socket_fd >= 0) - close_nointr_nofail(socket_fd); - - if (f) - fclose(f); - - unlink(temp); - - if (final[0]) - unlink(final); - - return r; -} - -static int ask_tty(void) { - struct termios old_termios, new_termios; - char passphrase[LINE_MAX]; - FILE *ttyf; - - if (!(ttyf = fopen("/dev/tty", "w"))) { - log_error("Failed to open /dev/tty: %m"); - return -errno; - } - - fputs("\x1B[1m", ttyf); - fprintf(ttyf, "%s: ", arg_message); - fputs("\x1B[0m", ttyf); - fflush(ttyf); - - if (tcgetattr(STDIN_FILENO, &old_termios) >= 0) { - - new_termios = old_termios; - - new_termios.c_lflag &= ~(ICANON|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; + } else { + char **l; - if (tcsetattr(STDIN_FILENO, TCSADRAIN, &new_termios) >= 0) { - size_t p = 0; - int r = 0; + if ((r = ask_password_agent(arg_message, arg_icon, timeout, arg_accept_cached, &l)) >= 0) { + char **p; - for (;;) { - size_t k; - char c; + STRV_FOREACH(p, l) { + puts(*p); - k = fread(&c, 1, 1, stdin); - - if (k <= 0) { - r = -EIO; - break; - } - - if (c == '\n') + if (!arg_multiple) break; - else if (c == '\b' || c == 127) { - if (p > 0) { - p--; - fputs("\b \b", ttyf); - } - } else { - passphrase[p++] = c; - fputc('*', ttyf); - } - - fflush(ttyf); } - fputc('\n', ttyf); - fclose(ttyf); - tcsetattr(STDIN_FILENO, TCSADRAIN, &old_termios); - - if (r < 0) - return -EIO; - - passphrase[p] = 0; - - fputs(passphrase, stdout); - fflush(stdout); - return 0; + strv_free(l); } - - } - - fclose(ttyf); - - if (!fgets(passphrase, sizeof(passphrase), stdin)) { - log_error("Failed to read password."); - return -EIO; } - truncate_nl(passphrase); - fputs(passphrase, stdout); - fflush(stdout); - - return 0; -} - -int main(int argc, char *argv[]) { - int r; - - log_parse_environment(); - log_open(); - - if ((r = parse_argv(argc, argv)) <= 0) - goto finish; - - if (arg_use_tty && isatty(STDIN_FILENO)) - r = ask_tty(); - else - r = ask_agent(); - finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }