Compare commits
1 commit
rawhide
...
private-jk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b7b2e04b6 |
3 changed files with 424 additions and 0 deletions
60
tests/Regression/bz1841869-pwd-from-tty/runtest.sh
Executable file
60
tests/Regression/bz1841869-pwd-from-tty/runtest.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# File: ./tests/Regression/bz1841869-pwd-from-tty/runtest.sh
|
||||
# Author: Jiří Kučera <jkucera AT redhat.com>
|
||||
# Brief: Test whether the passphrase is read from tty properly
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Copyright (C) 2021 Red Hat, Inc.
|
||||
#
|
||||
# This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
# Include Beaker environment
|
||||
. /usr/share/beakerlib/beakerlib.sh || exit 1
|
||||
|
||||
TEST="${TEST:-/CoreOS/volume_key/tests/Regression/bz1841869-pwd-from-tty}"
|
||||
TESTVERSION="${TESTVERSION:-1.0}"
|
||||
PACKAGES="${PACKAGES:-volume_key}"
|
||||
REQUIRES="${REQUIRES:-gcc cryptsetup nss-tools expect tcllib}"
|
||||
|
||||
_PASSPHRASE="t0pS3cr31!"
|
||||
|
||||
rlJournalStart
|
||||
rlPhaseStartSetup
|
||||
rlAssertRpm --all
|
||||
rlRun "TmpDir=\$(mktemp -d)" 0 "Creating tmp directory"
|
||||
rlRun "cp -v vk_wrap.c ${TmpDir}" 0 "Copying vk_wrap.c"
|
||||
rlRun "cp -v vk_dummy.c ${TmpDir}" 0 "Copying vk_dummy.c"
|
||||
rlRun "pushd ${TmpDir}"
|
||||
rlRun "gcc --std=c99 -pedantic -W -Wall -Werror -o vk_wrap vk_wrap.c" 0 \
|
||||
"Compiling vk_wrap.c"
|
||||
rlRun "gcc --std=c99 -pedantic -W -Wall -Werror -o vk_dummy vk_dummy.c" 0 \
|
||||
"Compiling vk_dummy.c"
|
||||
rlPhaseEnd
|
||||
|
||||
rlPhaseStartTest
|
||||
_PARAMS="VK_WRAP_PROGAM=${TmpDir}/vk_dummy"
|
||||
_PARAMS="${_PARAMS} VK_WRAP_PASSPHRASE=${_PASSPHRASE}"
|
||||
rlRun "${_PARAMS} ${TmpDir}/vk_wrap" 0 "Run volume_key via wrapper"
|
||||
rlPhaseEnd
|
||||
|
||||
rlPhaseStartCleanup
|
||||
rlRun "popd"
|
||||
rlRun "rm -rfd $TmpDir" 0 "Removing tmp directory"
|
||||
rlPhaseEnd
|
||||
rlJournalPrintText
|
||||
rlJournalEnd
|
||||
185
tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c
Normal file
185
tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* File: ./tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c
|
||||
* Author: Jiří Kučera <jkucera AT redhat.com>
|
||||
* Brief: Dummy volume_key (only request passphrase)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct ditem {
|
||||
char *message;
|
||||
struct ditem *next;
|
||||
};
|
||||
|
||||
struct diagnose {
|
||||
struct ditem *head, *tail;
|
||||
};
|
||||
|
||||
static int diagnose_append(struct diagnose *diagnose, char *message)
|
||||
{
|
||||
struct ditem *p = NULL;
|
||||
|
||||
if ((p = malloc(sizeof(struct ditem))) == NULL)
|
||||
return -1;
|
||||
p->message = message;
|
||||
p->next = NULL;
|
||||
|
||||
if (diagnose->head == NULL)
|
||||
diagnose->head = diagnose->tail = p;
|
||||
else {
|
||||
diagnose->tail->next = p;
|
||||
diagnose->tail = p;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void diagnose_dispose(struct diagnose *diagnose)
|
||||
{
|
||||
struct ditem *p = NULL, *t = NULL;
|
||||
|
||||
for (p = diagnose->head; (t = p); p = p->next, free(t))
|
||||
free(t->message);
|
||||
diagnose->head = diagnose->tail = NULL;
|
||||
}
|
||||
|
||||
static void diagnose_show(struct diagnose *diagnose)
|
||||
{
|
||||
struct ditem *p = NULL;
|
||||
|
||||
fputs("=== Diagnose ===\n", stderr);
|
||||
for (p = diagnose->head; p; p = p->next)
|
||||
fprintf(sdterr, "- %s\n", p->message ? p->message : "(null)");
|
||||
fputs("================\n", stderr);
|
||||
}
|
||||
|
||||
static void log_read(int nread, struct diagnose *diagnose)
|
||||
{
|
||||
char *s = NULL;
|
||||
|
||||
if (nread < 0) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
diagnose_append(diagnose, strdup("(EINTR)"));
|
||||
break;
|
||||
case EAGAIN:
|
||||
diagnose_append(diagnose, strdup("(EAGAIN)"));
|
||||
break;
|
||||
default:
|
||||
if (asprintf(&s, "(%s)", strerror(errno)) < 0)
|
||||
s = NULL;
|
||||
diagnose_append(diagnose, s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (nread == 0)
|
||||
diagnose_append(diagnose, strdup("(EOF)"));
|
||||
else {
|
||||
if (asprintf(&s, "Read %d bytes.", nread) < 0)
|
||||
s = NULL;
|
||||
diagnose_append(diagnose, s);
|
||||
}
|
||||
}
|
||||
|
||||
static char *get_password(const char *prompt, struct diagnose *diagnose)
|
||||
{
|
||||
int tty = -1, in_file = -1, out_file = -1, echo_disabled = 0;
|
||||
int nread = 0, tread = 0;
|
||||
char buf[LINE_MAX + 1], *p = NULL;
|
||||
struct termios otermios;
|
||||
|
||||
if ((tty = open("/dev/tty", O_RDONLY, 0)) < 0) {
|
||||
in_file = STDIN_FILENO;
|
||||
out_file = STDERR_FILENO;
|
||||
}
|
||||
else
|
||||
out_file = in_file = tty;
|
||||
|
||||
write(out_file, prompt, strlen(prompt));
|
||||
|
||||
if (tcgetattr(in_file, &otermios) == 0) {
|
||||
struct termios ntermios;
|
||||
|
||||
ntermios = otermios;
|
||||
ntermios.c_lflag &= ~ECHO;
|
||||
echo_disabled = tcsetattr(in_file, TCSAFLUSH, &ntermios) == 0;
|
||||
}
|
||||
|
||||
while (tread < LINE_MAX) {
|
||||
nread = read(in_file, buf + tread, LINE_MAX - tread);
|
||||
log_read(nread, diagnose);
|
||||
if (nread < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
else if (nread == 0)
|
||||
break;
|
||||
tread += nread;
|
||||
}
|
||||
buf[tread] = '\0';
|
||||
|
||||
if (echo_disabled) {
|
||||
tcsetattr(in_file, TCSAFLUSH, &otermios);
|
||||
write(out_file, "\n", 1);
|
||||
}
|
||||
|
||||
if (tty >= 0)
|
||||
close(tty);
|
||||
|
||||
if (nread < 0)
|
||||
return NULL;
|
||||
|
||||
p = strchr(buf, '\r');
|
||||
if (p != NULL)
|
||||
*p = '\0';
|
||||
p = strchr(buf, '\n');
|
||||
if (p != NULL)
|
||||
*p = '\0';
|
||||
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct diagnose diagnose = {NULL, NULL};
|
||||
char *passphrase = NULL, *s = NULL;
|
||||
|
||||
passphrase = get_password("Please, enter the passphrase: ", &diagnose);
|
||||
if (asprintf(&s, "The passphrase is: %s", passphrase) < 0)
|
||||
s = NULL;
|
||||
free(passphrase);
|
||||
diagnose_append(&diagnose, s);
|
||||
diagnose_show(&diagnose);
|
||||
diagnose_dispose(&diagnose);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
179
tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c
Normal file
179
tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* File: ./tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c
|
||||
* Author: Jiří Kučera <jkucera AT redhat.com>
|
||||
* Brief: Run volume_key, emulate EINTR and EAGAIN
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This program 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 program 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 program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum emultypes {
|
||||
NO_EMUL,
|
||||
EINTR_EMUL,
|
||||
EAGAIN_EMUL
|
||||
};
|
||||
|
||||
static void error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static char *get_env(const char *name, int optional)
|
||||
{
|
||||
char *p = getenv(name);
|
||||
|
||||
if (!optional && (p == NULL || *p == '\0'))
|
||||
return (error("Environment variable %s is not set!\n", name), NULL);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int get_emul_type(void)
|
||||
{
|
||||
char *p = get_env("VK_WRAP_EMULATE", 1);
|
||||
|
||||
if (p == NULL)
|
||||
return NO_EMUL;
|
||||
else if (strcmp(p, "EINTR") == 0)
|
||||
return EINTR_EMUL;
|
||||
else if (strcmp(p, "EAGAIN") == 0)
|
||||
return EAGAIN_EMUL;
|
||||
return NO_EMUL;
|
||||
}
|
||||
|
||||
static void close_pipe(int pipefd[2])
|
||||
{
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
}
|
||||
|
||||
static void freeargs(char **args)
|
||||
{
|
||||
char **p = args;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
while (*p)
|
||||
free(*p++);
|
||||
free(args);
|
||||
}
|
||||
|
||||
static char **mkargs(const *program, int argc, char **argv)
|
||||
{
|
||||
char **args = NULL;
|
||||
size_t args_size = (argc + 1) * sizeof(*args);
|
||||
int i = 0;
|
||||
|
||||
if ((args = malloc(args_size)) == NULL)
|
||||
return NULL;
|
||||
memset(args, '\0', args_size);
|
||||
if ((args[0] = strdup(program)) == NULL)
|
||||
return (freeargs(args), NULL);
|
||||
for (i = 1; i < argc; i++)
|
||||
if ((args[i] = strdup(argv[i])) == NULL)
|
||||
return (freeargs(args), NULL);
|
||||
return args;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *program = NULL, *passphrase = NULL;
|
||||
int emultype = NO_EMUL, cpid = 0, pipefd[2];
|
||||
|
||||
if ((program = get_env("VK_WRAP_PROGRAM", 0)) == NULL)
|
||||
return EXIT_FAILURE;
|
||||
if ((passphrase = get_env("VK_WRAP_PASSPHRASE", 0)) == NULL)
|
||||
return EXIT_FAILURE;
|
||||
emultype = get_emul_type();
|
||||
|
||||
if (pipe(pipefd) < 0)
|
||||
return (perror("pipe"), EXIT_FAILURE);
|
||||
|
||||
/* Set non-blocking reading only if we emulate EAGAIN */
|
||||
if (emultype == EAGAIN_EMUL && fcntl(pipefd[0], F_SETFL, O_NONBLOCK) < 0)
|
||||
return (perror("fcntl"), close_pipe(pipefd), EXIT_FAILURE);
|
||||
|
||||
cpid = fork();
|
||||
if (cpid < 0)
|
||||
return (perror("fork"), close_pipe(pipefd), EXIT_FAILURE);
|
||||
|
||||
if (cpid == 0) {
|
||||
char **args = NULL;
|
||||
|
||||
/* We are in the child, close the writing end of the pipe */
|
||||
close(pipefd[1]);
|
||||
/* Map the reading end of the pipe to standard input */
|
||||
if (pipefd[0] != STDIN_FILENO) {
|
||||
if (dup2(pipefd[0], STDIN_FILENO) < 0)
|
||||
return (perror("dup2"), close(pipefd[0]), EXIT_FAILURE);
|
||||
close(pipefd[0]);
|
||||
}
|
||||
/* Copy the program arguments */
|
||||
if ((args = mkargs(program, argc, argv)) == NULL)
|
||||
return (perror("mkargs"), EXIT_FAILURE);
|
||||
/* Replace the current process image with the program */
|
||||
if (execv(args[0], args) < 0)
|
||||
return (perror("execv"), freeargs(args), EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* We are in the parent, close the reading end of the pipe */
|
||||
close(pipefd[0]);
|
||||
switch (emultype) {
|
||||
case EINTR_EMUL:
|
||||
/* Hopefully this emulate EINTR */
|
||||
sleep(2); /* Wait for the child to enter read */
|
||||
/* Now child is inside kernel space, send it the signal */
|
||||
kill(cpid, SIGSTOP);
|
||||
sleep(1); /* Wait a bit */
|
||||
/* Resume child */
|
||||
kill(cpid, SIGCONT);
|
||||
break;
|
||||
case EAGAIN_EMUL:
|
||||
/* Delay writing the passphrase. When the child read from an empty pipe
|
||||
in the non-blocking mode, read() returns -1 and sets errno to EAGAIN
|
||||
*/
|
||||
sleep(3);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Write the passphrase */
|
||||
if (write(pipefd[1], passphrase, strlen(passphrase)) < 0)
|
||||
perror("write");
|
||||
if (write(pipefd[1], "\n", 1) < 0)
|
||||
perror("write");
|
||||
close(pipefd[1]); /* Reader will see EOF */
|
||||
/* Wait for the child to finish */
|
||||
waitpid(cpid, NULL, 0);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue