diff --git a/plans/ci.fmf b/plans/ci.fmf deleted file mode 100644 index c1627f9..0000000 --- a/plans/ci.fmf +++ /dev/null @@ -1,5 +0,0 @@ -summary: Basic smoke test -discover: - how: fmf -execute: - how: tmt diff --git a/.fmf/version b/tests/.fmf/version similarity index 100% rename from .fmf/version rename to tests/.fmf/version diff --git a/tests/Regression/bz1841869-pwd-from-tty/runtest.sh b/tests/Regression/bz1841869-pwd-from-tty/runtest.sh new file mode 100755 index 0000000..7bc1c22 --- /dev/null +++ b/tests/Regression/bz1841869-pwd-from-tty/runtest.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# File: ./tests/Regression/bz1841869-pwd-from-tty/runtest.sh +# Author: Jiří Kučera +# 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 diff --git a/tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c b/tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c new file mode 100644 index 0000000..98e92e5 --- /dev/null +++ b/tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c @@ -0,0 +1,185 @@ +/* + * File: ./tests/Regression/bz1841869-pwd-from-tty/vk_dummy.c + * Author: Jiří Kučera + * 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 +#include + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c b/tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c new file mode 100644 index 0000000..d41bcad --- /dev/null +++ b/tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c @@ -0,0 +1,179 @@ +/* + * File: ./tests/Regression/bz1841869-pwd-from-tty/vk_wrap.c + * Author: Jiří Kučera + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/tests/Sanity/basic-sanity/main.fmf b/tests/Sanity/basic-sanity/main.fmf index e87514e..ad4c0fa 100644 --- a/tests/Sanity/basic-sanity/main.fmf +++ b/tests/Sanity/basic-sanity/main.fmf @@ -11,6 +11,6 @@ require: - nss-tools - expect - tcllib -framework: beakerlib test: ./runtest.sh duration: 10m +tier: 1 diff --git a/tests/Sanity/basic-sanity/runtest.sh b/tests/Sanity/basic-sanity/runtest.sh index 23f72d5..5ea92c8 100755 --- a/tests/Sanity/basic-sanity/runtest.sh +++ b/tests/Sanity/basic-sanity/runtest.sh @@ -28,6 +28,7 @@ _TESTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" # Include Beaker environment +. /usr/bin/rhts-environment.sh || exit 1 . /usr/share/beakerlib/beakerlib.sh || exit 1 # Include utils diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..c113f70 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,14 @@ +--- +- hosts: localhost + roles: + - role: standard-test-beakerlib + tags: + - classic + - container + fmf_filter: "tier: 1" + required_packages: + - volume_key + - cryptsetup + - expect + - tcllib + - nss-tools diff --git a/volume_key.spec b/volume_key.spec index 1e7129f..c72f454 100644 --- a/volume_key.spec +++ b/volume_key.spec @@ -32,8 +32,8 @@ Summary: An utility for manipulating storage encryption keys and passphrases Name: volume_key Version: 0.3.12 -Release: 29%{?dist} -License: GPL-2.0-only AND (MPL-1.1 OR GPL-2.0-or-later OR LGPL-2.1-or-later) +Release: 14%{?dist} +License: GPLv2 URL: https://pagure.io/%{name}/ Requires: %{name}-libs%{?_isa} = %{version}-%{release} @@ -44,12 +44,10 @@ Patch0: volume_key-0.3.12-support_LUKS2_and_more.patch # Fix resource leaks # - backport of bf6618ec0b09b4e51fc97fa021e687fbd87599ba Patch1: volume_key-0.3.12-fix_resource_leaks.patch -BuildRequires: autoconf, automake, libtool BuildRequires: make BuildRequires: gcc BuildRequires: cryptsetup-devel, gettext-devel, glib2-devel, /usr/bin/gpg2 -BuildRequires: gpgme-devel, libblkid-devel, nss-devel -BuildRequires: python3-devel, python3-setuptools +BuildRequires: gpgme-devel, libblkid-devel, nss-devel, python3-devel %if 0%{?drop_python2} < 1 BuildRequires: python2-devel %endif @@ -120,9 +118,8 @@ Requires: %{name}-libs%{?_isa} = %{version}-%{release} %prep %setup -q -%patch -P0 -p1 -%patch -P1 -p1 -autoreconf -fiv +%patch0 -p1 +%patch1 -p1 %build %configure %{?with_pythons} @@ -173,51 +170,6 @@ exit 1; \ %endif %changelog -* Tue Nov 11 2025 Michal Hlavinka - 0.3.12-29 -- rebuild for gpgme 2.0 - -* Fri Sep 19 2025 Python Maint - 0.3.12-28 -- Rebuilt for Python 3.14.0rc3 bytecode - -* Fri Aug 15 2025 Python Maint - 0.3.12-27 -- Rebuilt for Python 3.14.0rc2 bytecode - -* Fri Jul 25 2025 Fedora Release Engineering - 0.3.12-26 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - -* Mon Jun 02 2025 Python Maint - 0.3.12-25 -- Rebuilt for Python 3.14 - -* Sun Jan 19 2025 Fedora Release Engineering - 0.3.12-24 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Sat Jul 20 2024 Fedora Release Engineering - 0.3.12-23 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Fri Jun 07 2024 Python Maint - 0.3.12-22 -- Rebuilt for Python 3.13 - -* Sat Jan 27 2024 Fedora Release Engineering - 0.3.12-21 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Sat Jul 22 2023 Fedora Release Engineering - 0.3.12-20 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Tue Jun 13 2023 Python Maint - 0.3.12-19 -- Rebuilt for Python 3.12 - -* Sat Jan 21 2023 Fedora Release Engineering - 0.3.12-18 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild - -* Sat Jul 23 2022 Fedora Release Engineering - 0.3.12-17 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild - -* Mon Jun 13 2022 Python Maint - 0.3.12-16 -- Rebuilt for Python 3.11 - -* Sat Jan 22 2022 Fedora Release Engineering - 0.3.12-15 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - * Wed Aug 04 2021 Jiri Kucera - 0.3.12-14 - Fix FTBFS - Move License tag back to GPLv2 (this is the effective license)