From 610cfdaca7a463d32f4212669ae6b6b55dca4b54 Mon Sep 17 00:00:00 2001 From: gryf Date: Wed, 17 Dec 2025 07:55:24 +0100 Subject: [PATCH] Added askpass sources --- README.rst | 5 +- askpass.c | 418 ++++++++++++++++++++++++++++++++++++++++++++++++ askpass.license | 7 + 3 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 askpass.c create mode 100644 askpass.license diff --git a/README.rst b/README.rst index f56db8f..aa7ee5e 100644 --- a/README.rst +++ b/README.rst @@ -125,7 +125,9 @@ configuration options in ``mkinitramfs.toml`` file: You'll need to put at least ``ip``, ``netmask``, ``gateway`` to make this work with defaults, with assumption that interface is ``eth0`` and ``root`` user -have needed ``authorized_keys`` file. +have needed ``authorized_keys`` file. There is also ``askpass.c`` which origins +from `better-initramfs`_ project, included in this repository just for +preservation purposes. Then execute script with flag ``-b`` which include dropbear part.: @@ -137,3 +139,4 @@ Then execute script with flag ``-b`` which include dropbear part.: .. _cryptsetup: https://gitlab.com/cryptsetup/cryptsetup/blob/master/README.md .. _ykchalresp: https://github.com/Yubico/yubikey-personalization .. _dropbear: https://matt.ucc.asn.au/dropbear/dropbear.html +.. _better-initramfs: https://bitbucket.org/piotrkarbowski/better-initramfs diff --git a/askpass.c b/askpass.c new file mode 100644 index 0000000..e6c4769 --- /dev/null +++ b/askpass.c @@ -0,0 +1,418 @@ +/* + * askpass.c - prompts a user for a passphrase using any suitable method + * and prints the result to stdout. + * + * Copyright (C) 2008 David Härdeman + * + * This package 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 2 of the License, or + * (at your option) any later version. + * + * This package 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 package; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * 2012 - Piotr Karbowski + * * Dropped splasy code. + */ + +#define _GNU_SOURCE +#define _BSD_SOURCE +#define _POSIX_C_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static bool disable_method(const char *method); + +/***************************************************************************** + * Utility functions * + *****************************************************************************/ +static void +debug(const char *fmt, ...) +{ + va_list ap; + static bool first = true; + static FILE *dbgfile; + + if (!DEBUG) + return; + + if (first) { + first = false; + dbgfile = fopen("/tmp/askpass.debug", "a"); + } + + if (!dbgfile) + return; + + va_start(ap, fmt); + vfprintf(dbgfile, fmt, ap); + va_end(ap); +} + +static void +usage(const char *arg0, const char *errmsg) +{ + if (errmsg) + fprintf(stderr, "Error: %s\nUsage: %s PROMPT\n", errmsg, arg0); + else + fprintf(stderr, "Usage: %s PROMPT\n", arg0); + exit(EXIT_FAILURE); +} + +static void +fifo_common_finish(int fd, char **buf, size_t *used, size_t *size) +{ + if (fd >= 0) + close(fd); + + if (!*buf) + return; + + memset(*buf, '\0', *size); + free(*buf); + *buf = NULL; + *used = 0; + *size = 0; +} + +static bool +fifo_common_read(int fd, char **buf, size_t *used, size_t *size) +{ + ssize_t result; + +again: + if ((*size - *used) == 0) { + *size += 4096; + *buf = realloc(*buf, *size); + if (!*buf) { + *size = 0; + *used = 0; + debug("Failed to allocate memory for passphrase\n"); + return false; + } + } + +reread: + result = read(fd, *buf + *used, *size - *used); + + if (result < 0) { + if (errno == EAGAIN) + return false; + if (errno == EINTR) + goto reread; + debug("Error when reading from fifo\n"); + return false; + } + + debug("Read %i bytes from fifo\n", (int)result); + *used += result; + + if (result == 0) + return true; + + goto again; +} + + +/***************************************************************************** + * fifo functions * + *****************************************************************************/ +#define FIFO_PATH "/luks_passfifo" +static size_t fifoused = 0; +static size_t fifosize = 0; +static char *fifobuf = NULL; + +static void +fifo_finish(int fd) +{ + fifo_common_finish(fd, &fifobuf, &fifoused, &fifosize); +} + +static bool +fifo_read(int fd, char **buf, size_t *size) +{ + debug("In fifo_read\n"); + if (fifo_common_read(fd, &fifobuf, &fifoused, &fifosize)) { + *buf = fifobuf; + *size = fifoused; + return true; + } + + return false; +} + +static int +fifo_prepare(const char *prompt) +{ + int ret; + + ret = mkfifo(FIFO_PATH, 0600); + if (ret && errno != EEXIST) + return -1; + + return open(FIFO_PATH, O_RDONLY | O_NONBLOCK); +} + +/***************************************************************************** + * console functions * + *****************************************************************************/ +#define CONSOLE_PATH "/dev/console" +static struct termios term_old; +static bool term_set = false; +static char *consolebuf = NULL; +static size_t consolebuflen = 0; + +static void +console_finish(int fd) +{ + if (consolebuf) { + memset(consolebuf, '\0', consolebuflen); + free(consolebuf); + consolebuf = NULL; + consolebuflen = 0; + } + + if (!term_set || fd < 0) + return; + + term_set = false; + tcsetattr(fd, TCSAFLUSH, &term_old); + fprintf(stderr, "\n"); + klogctl(7, NULL, 0); +} + +bool +console_read(int fd, char **buf, size_t *size) +{ + ssize_t nread; + + /* Console is in ICANON mode so we'll get entire lines */ + nread = getline(&consolebuf, &consolebuflen, stdin); + + if (nread < 0) + return NULL; + + /* Strip trailing newline, if any */ + if (nread > 0 && consolebuf[nread - 1] == '\n') { + nread--; + consolebuf[nread] = '\0'; + } + + *size = nread; + *buf = consolebuf; + + return true; +} + +static int +console_prepare(const char *prompt) +{ + struct termios term_new; + char *prompt_ptr = prompt; + char *newline = NULL; + + if (!isatty(STDIN_FILENO)) { + if (access(CONSOLE_PATH, R_OK | W_OK)) { + debug("No access to console device " CONSOLE_PATH "\n"); + return -1; + } + + if (!freopen(CONSOLE_PATH, "r", stdin) || + !freopen(CONSOLE_PATH, "a", stdout) || + !freopen(CONSOLE_PATH, "a", stderr) || + !isatty(STDIN_FILENO)) { + debug("Failed to open console\n"); + return -1; + } + } + + if (tcgetattr(STDIN_FILENO, &term_old)) { + debug("Failed to get terminal settings\n"); + return -1; + } + + term_new = term_old; + term_new.c_lflag &= ~ECHO; + term_new.c_lflag |= ICANON; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_new)) { + debug("Failed to disable echoing\n"); + return -1; + } + + /* handle any non-literal embedded newlines in prompt */ + while ( (newline = strstr(prompt_ptr,"\\n")) != NULL ) { + /* Calculate length of string leading up to newline. */ + int line_len = newline - prompt_ptr; + + /* Force trimming of prompt to location of newline. */ + if (fwrite(prompt_ptr, line_len, 1, stderr) < 1 || + fwrite("\n", 1, 1, stderr) < 1) { + debug("Failed to print prompt\n"); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_old); + return -1; + } + + /* Skip over newline. */ + prompt_ptr = newline + 2; + } + if (fputs(prompt_ptr, stderr) < 0) { + debug("Failed to print prompt\n"); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_old); + return -1; + } + + /* Disable printk to console */ + klogctl(6, NULL, 0); + term_set = true; + return STDIN_FILENO; +} + +/***************************************************************************** + * main functions * + *****************************************************************************/ + +struct method { + const char *name; + int (*prepare)(const char *prompt); + bool (*read)(int fd, char **buf, size_t *size); + void (*finish)(int fd); + bool active; + bool enabled; + int fd; +}; + +static struct method methods[] = { + { "fifo", fifo_prepare, fifo_read, fifo_finish, false, true, -1 }, + { "console", console_prepare, console_read, console_finish, false, true, -1 } +}; + +static bool +disable_method(const char *method) +{ + int i; + bool result = false; + + debug("Disabling method %s\n", method ? method : "ALL"); + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + /* A NULL method means all methods should be disabled */ + if (method && strcmp(methods[i].name, method)) + continue; + if (!methods[i].enabled) + continue; + if (methods[i].active) + methods[i].finish(methods[i].fd); + + methods[i].active = false; + methods[i].fd = -1; + methods[i].enabled = false; + result = true; + } + + return result; +} + +int +main(int argc, char **argv, char **envp) +{ + char *pass = NULL; + size_t passlen = 0; + int i; + int nfds; + fd_set fds; + int ret; + bool done = false; + sigset_t sigset; + + if (argc != 2) + usage(argv[0], "incorrect number of arguments"); + + sigfillset(&sigset); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (!methods[i].enabled) + continue; + debug("Enabling method %s\n", methods[i].name); + methods[i].fd = methods[i].prepare(argv[1]); + if (methods[i].fd < 0) + methods[i].active = false; + else + methods[i].active = true; + } + + while (!done) { + nfds = 0; + FD_ZERO(&fds); + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (!methods[i].enabled || methods[i].fd < 0) + continue; + debug("method %i has fd %i and name %s\n", i, methods[i].fd, methods[i].name); + FD_SET(methods[i].fd, &fds); + if (methods[i].fd + 1 > nfds) + nfds = methods[i].fd + 1; + } + + if (nfds == 0) { + debug("All methods disabled\n"); + exit(EXIT_FAILURE); + } + + debug("Starting select with nfds %i\n", nfds); + ret = select(nfds, &fds, NULL, NULL, NULL); + + if (ret <= 0) { + if (ret == 0 || errno == EINTR) + continue; + debug("Select failed\n"); + disable_method(NULL); + exit(EXIT_FAILURE); + } + + for (i = 0; i < ARRAY_SIZE(methods); i++) { + if (!methods[i].enabled || methods[i].fd < 0) + continue; + if (!FD_ISSET(methods[i].fd, &fds)) + continue; + if (methods[i].read(methods[i].fd, &pass, &passlen) && pass) { + done = true; + break; + } + } + } + + debug("Writing %i bytes to stdout\n", (int)passlen); + write(STDOUT_FILENO, pass, passlen); + disable_method(NULL); + exit(EXIT_SUCCESS); +} + diff --git a/askpass.license b/askpass.license new file mode 100644 index 0000000..72e4ba0 --- /dev/null +++ b/askpass.license @@ -0,0 +1,7 @@ +Copyright (c) 2010-2018, Piotr Karbowski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer i…LL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.