Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- name: Install some build requirements
id: dnf
run: |
dnf install -y efivar-devel nss-devel openssl-devel
dnf install -y efivar-devel nss-devel openssl-devel nss-tools
- name: Run tests on ${{ matrix.distro }} for ${{ matrix.arch }}
id: test
run: |
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ deps all :
$(SUBDIRS) :
$(MAKE) -C $@ all

.PHONY: $(SUBDIRS)
test : all
@echo "Running functional tests..."
@cd $(TOPDIR) && tests/run-tests.sh

.PHONY: $(SUBDIRS) test

GITTAG = $(VERSION)

Expand Down
3 changes: 2 additions & 1 deletion src/efikeygen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,8 @@ int main(int argc, char *argv[])
memcpy(&ias.issuer, &cert->issuer, sizeof(ias.issuer));
memcpy(&ias.serialNumber, &cert->serialNumber, sizeof(ias.serialNumber));

add_trust(cms, &ias, is_ca ? ",,CTu" : ",,u");
if (add_trust(cms, &ias, is_ca ? ",,CTu" : ",,u") < 0)
nsserr(1, "could not set trust for certificate");

SECITEM_FreeItem(&sigder, PR_FALSE);
SECITEM_FreeItem(&signature, PR_FALSE);
Expand Down
62 changes: 14 additions & 48 deletions src/file_kmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdint.h>

#include "pesign.h"
#include "signed_data.h"

#include <prerror.h>

Expand All @@ -35,67 +36,32 @@ kmod_generate_digest(cms_context *cms, unsigned char *addr, size_t len)
return 0;
}

struct write_sig_info {
int outfd;
int rc;
size_t sig_len;
};

static void
kmod_signature_out(void *arg, const char *buf, unsigned long len)
{
struct write_sig_info *info = (struct write_sig_info *) arg;
int rc;

rc = write_file(info->outfd, buf, len);
if (rc < 0) {
info->rc = rc;
return;
}

info->sig_len += len;
}

ssize_t
kmod_write_signature(cms_context *cms, int outfd)
{
SEC_PKCS7ContentInfo *cinfo;
SECItem *digest = cms->digests[cms->selected_digest].pe_digest;
SECStatus rv;
struct write_sig_info info = {
.outfd = outfd,
};
SECItem sd_der;
ssize_t rc = -1;

cinfo = SEC_PKCS7CreateSignedData(cms->cert,
certUsageObjectSigner, NULL,
digest_get_digest_oid(cms),
digest, NULL, NULL);
if (!cinfo) {
memset(&sd_der, '\0', sizeof(sd_der));

/* Use generate_spc_signed_data() instead of SEC_PKCS7CreateSignedData()
* to avoid strict certificate chain validation that fails for self-signed
* certificates even when trust flags are properly set. */
rc = generate_spc_signed_data(cms, &sd_der);
if (rc < 0) {
cms->log(cms, LOG_ERR, "failed to create signed data: %s (%s)",
PORT_ErrorToString(PORT_GetError()),
PORT_ErrorToName(PORT_GetError()));
return -1;
}

rv = SEC_PKCS7Encode(cinfo, kmod_signature_out, &info, NULL, NULL,
NULL);
if (rv != SECSuccess) {
cms->log(cms, LOG_ERR, "failed to encode signed data: %d", rv);
goto out;
}

if (info.rc != 0) {
cms->log(cms, LOG_ERR, "Signed data encode error %d", info.rc);
rc = info.rc;
goto out;
rc = write_file(outfd, sd_der.data, sd_der.len);
if (rc < 0) {
cms->log(cms, LOG_ERR, "failed to write signature: %m");
return -1;
}

rc = info.sig_len;

out:
SEC_PKCS7DestroyContentInfo(cinfo);
return rc;
return sd_der.len;
}

static const char magic_number[] = "~Module signature appended~\n";
Expand Down
63 changes: 63 additions & 0 deletions tests/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash
# Main test runner for pesign functional tests

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

FAILED_TESTS=()
PASSED_TESTS=()

echo "======================================"
echo "Running pesign functional tests"
echo "======================================"
echo

run_test() {
local test_script="$1"
local test_name=$(basename "$test_script" .sh)

echo -e "${YELLOW}Running: ${test_name}${NC}"

if bash "$test_script"; then
echo -e "${GREEN}✓ PASSED: ${test_name}${NC}"
PASSED_TESTS+=("$test_name")
else
echo -e "${RED}✗ FAILED: ${test_name}${NC}"
FAILED_TESTS+=("$test_name")
fi
echo
}

# Run all test scripts
for test in "$SCRIPT_DIR"/test-*.sh; do
if [ -f "$test" ]; then
run_test "$test"
fi
done

# Summary
echo "======================================"
echo "Test Summary"
echo "======================================"
echo -e "${GREEN}Passed: ${#PASSED_TESTS[@]}${NC}"
echo -e "${RED}Failed: ${#FAILED_TESTS[@]}${NC}"

if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
echo
echo "Failed tests:"
for test in "${FAILED_TESTS[@]}"; do
echo -e " ${RED}✗${NC} $test"
done
exit 1
fi

echo
echo -e "${GREEN}All tests passed!${NC}"
exit 0
117 changes: 117 additions & 0 deletions tests/test-ca-signed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/bin/bash
# Test CA and signing certificate setup
# Based on AlmaLinux documentation

set -e

TEST_NAME="ca-signed"
WORK_DIR=$(mktemp -d)
trap "rm -rf $WORK_DIR" EXIT

echo " Setting up CA and signing certificates..."

# Create certificate database
mkdir -m 0700 -p "$WORK_DIR/ca"
echo "" | certutil -d "$WORK_DIR/ca" -N --empty-password

# Generate CA certificate
./src/efikeygen -d "$WORK_DIR/ca" \
--ca --self-sign \
--not-valid-after=$(date +%s --date='+10 years') \
--nickname='Test Secure Boot CA' \
--common-name='CN=Test Secure Boot CA,O=Test Organization,E=test@example.com'

# Verify CA was created
if ! certutil -d "$WORK_DIR/ca" -L -n 'Test Secure Boot CA' > /dev/null 2>&1; then
echo " ERROR: CA certificate not created"
exit 1
fi

# Generate signing certificate signed by CA (marked as --kernel)
./src/efikeygen -d "$WORK_DIR/ca" \
--kernel \
--not-valid-after=$(date +%s --date='+10 years') \
--signer='Test Secure Boot CA' \
--nickname='Test Secure Boot Signing' \
--common-name='CN=Test Secure Boot Signing,O=Test Organization,E=test@example.com'

# Verify signing certificate was created
if ! certutil -d "$WORK_DIR/ca" -L -n 'Test Secure Boot Signing' > /dev/null 2>&1; then
echo " ERROR: Signing certificate not created"
exit 1
fi

# Export certificates
certutil -d "$WORK_DIR/ca" -L -n "Test Secure Boot CA" -r > "$WORK_DIR/test-secureboot-ca.cer"
certutil -d "$WORK_DIR/ca" -L -n "Test Secure Boot Signing" -r > "$WORK_DIR/test-secureboot.cer"

# Verify certificate files exist and are not empty
if [ ! -s "$WORK_DIR/test-secureboot-ca.cer" ]; then
echo " ERROR: CA certificate export failed"
exit 1
fi

if [ ! -s "$WORK_DIR/test-secureboot.cer" ]; then
echo " ERROR: Signing certificate export failed"
exit 1
fi

echo " ✓ CA and signing certificates created successfully"

# Verify CA trust flags (CA cert should have CA trust)
CA_TRUST=$(certutil -d "$WORK_DIR/ca" -L | grep "Test Secure Boot CA" | awk '{print $NF}')
if echo "$CA_TRUST" | grep -q "C\|T"; then
echo " ✓ CA trust flags: $CA_TRUST"
else
echo " ✗ CA trust flags missing CA trust: $CA_TRUST"
exit 1
fi

# Verify signing cert trust flags
SIGN_TRUST=$(certutil -d "$WORK_DIR/ca" -L | grep "Test Secure Boot Signing" | awk '{print $NF}')
if [ -z "$SIGN_TRUST" ]; then
echo " ✗ No trust flags found for signing cert"
exit 1
else
echo " ✓ Signing cert trust flags: $SIGN_TRUST"
fi

# Get test data directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEST_DATA="$SCRIPT_DIR/data"

# Use real kernel and module files
KERNEL="$TEST_DATA/vmlinuz-6.19.10-200.fc43.x86_64"
MODULE="$TEST_DATA/vfat.ko"

# Test signing a kernel (should work - cert was created with --kernel)
echo " Testing kernel signing with CA-signed certificate..."
if ./src/pesign --certdir "$WORK_DIR/ca" \
--certificate 'Test Secure Boot Signing' \
--in "$KERNEL" \
--sign \
--out "$WORK_DIR/kernel-signed" 2>"$WORK_DIR/kernel-error.log"; then
echo " ✓ CA-signed cert signed kernel (exit 0)"
./src/pesign --show-signature --in "$WORK_DIR/kernel-signed" 2>/dev/null | head -5
else
echo " ✗ CA-signed cert failed to sign kernel (exit $?)"
[ -s "$WORK_DIR/kernel-error.log" ] && cat "$WORK_DIR/kernel-error.log"
exit 1
fi

# Test signing a module (kernel certs can sign modules)
echo " Testing module signing with CA-signed certificate..."
if ./src/pesign --certdir "$WORK_DIR/ca" \
--certificate 'Test Secure Boot Signing' \
--in "$MODULE" \
--sign \
--out "$WORK_DIR/module-signed.ko" 2>"$WORK_DIR/module-error.log"; then
echo " ✓ CA-signed cert signed module (exit 0)"
./src/pesign --show-signature --in "$WORK_DIR/module-signed.ko" 2>/dev/null | head -5
else
echo " ✗ CA-signed cert failed to sign module (exit $?)"
[ -s "$WORK_DIR/module-error.log" ] && cat "$WORK_DIR/module-error.log"
exit 1
fi

exit 0
91 changes: 91 additions & 0 deletions tests/test-self-signed-kernel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/bin/bash
# Test self-signed kernel signing certificate setup
# Based on Red Hat documentation

set -e

TEST_NAME="self-signed-kernel"
WORK_DIR=$(mktemp -d)
trap "rm -rf $WORK_DIR" EXIT

echo " Setting up self-signed kernel signing certificate..."

# Create certificate database
mkdir -m 0700 -p "$WORK_DIR/certdb"
echo "" | certutil -d "$WORK_DIR/certdb" -N --empty-password

# Generate self-signed kernel certificate
./src/efikeygen --dbdir "$WORK_DIR/certdb" \
--self-sign \
--kernel \
--common-name 'CN=Test Kernel Signing Key' \
--nickname 'Test Kernel Key'

# Verify certificate was created
if ! certutil -d "$WORK_DIR/certdb" -L -n 'Test Kernel Key' > /dev/null 2>&1; then
echo " ERROR: Certificate not created"
exit 1
fi

# Export certificate
certutil -d "$WORK_DIR/certdb" \
-n 'Test Kernel Key' \
-Lr \
> "$WORK_DIR/kernel_cert.cer"

# Verify certificate file exists and is not empty
if [ ! -s "$WORK_DIR/kernel_cert.cer" ]; then
echo " ERROR: Certificate export failed"
exit 1
fi

echo " ✓ Self-signed kernel certificate created successfully"

# Verify trust flags are set (should be u,u,u for user cert)
TRUST=$(certutil -d "$WORK_DIR/certdb" -L | grep "Test Kernel Key" | awk '{print $NF}')
if [ -z "$TRUST" ]; then
echo " ✗ No trust flags found"
exit 1
else
echo " ✓ Trust flags: $TRUST"
fi

# Get test data directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEST_DATA="$SCRIPT_DIR/data"

# Use real kernel and module files
KERNEL="$TEST_DATA/vmlinuz-6.19.10-200.fc43.x86_64"
MODULE="$TEST_DATA/vfat.ko"

# Test signing a kernel (should work)
echo " Testing kernel signing with kernel certificate..."
if ./src/pesign --certdir "$WORK_DIR/certdb" \
--certificate 'Test Kernel Key' \
--in "$KERNEL" \
--sign \
--out "$WORK_DIR/kernel-signed" 2>"$WORK_DIR/kernel-error.log"; then
echo " ✓ Kernel cert signed kernel (exit 0)"
./src/pesign --show-signature --in "$WORK_DIR/kernel-signed" 2>/dev/null | head -5
else
echo " ✗ Kernel cert failed to sign kernel (exit $?)"
[ -s "$WORK_DIR/kernel-error.log" ] && cat "$WORK_DIR/kernel-error.log"
exit 1
fi

# Test signing a module (kernel certs can sign modules)
echo " Testing module signing with kernel certificate..."
if ./src/pesign --certdir "$WORK_DIR/certdb" \
--certificate 'Test Kernel Key' \
--in "$MODULE" \
--sign \
--out "$WORK_DIR/module-signed.ko" 2>"$WORK_DIR/module-error.log"; then
echo " ✓ Kernel cert signed module (exit 0)"
./src/pesign --show-signature --in "$WORK_DIR/module-signed.ko" 2>/dev/null | head -5
else
echo " ✗ Kernel cert failed to sign module (exit $?)"
[ -s "$WORK_DIR/module-error.log" ] && cat "$WORK_DIR/module-error.log"
exit 1
fi

exit 0
Loading