提交 6778bc1b 编写于 作者: R Roberto Sassu

Update to version v0.3

上级 14f17bca
*.o
src/gen_digest_lists
src/verify_digest_lists
src/upload_digest_lists
src/modify_digest_lists
src/gen_parser_metadata
src/verify_digest_lists
tests/lib
tests/ima
tests/rpm
tests/gen
*Makefile.in
*Makefile
*~
ar-lib
config.h
......
SUBDIRS = docs \
include \
initrd \
lib \
generators \
parsers \
scripts \
src
src \
systemd \
tests
EXTRA_DIST = AUTHORS \
CHANGES
......
此差异已折叠。
......@@ -2,16 +2,36 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([digest-list-tools], [0.2], [roberto.sassu@huawei.com])
AC_INIT([digest-list-tools], [0.3], [roberto.sassu@huawei.com])
AC_CONFIG_HEADERS([config.h])
CFLAGS="$CFLAGS -Wall"
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
AM_PROG_AR
AC_PROG_CC
LT_INIT
# Enable debug.
AC_ARG_ENABLE([debug], [ --enable-debug Turn on debugging],
[debug=${enableval}], [debug=no])
if test -z "$CFLAGS"; then
if test "$debug" == yes; then
CFLAGS="-O0 -g"
else
CFLAGS="-O2"
fi
fi
if test "$debug" == yes; then
CFLAGS="$CFLAGS -DDEBUG"
fi
big_endian=false
AC_C_BIGENDIAN ([big_endian=true], [big_endian=false])
if test "$big_endian" == true; then
CFLAGS="$CFLAGS -D__BIG_ENDIAN__"
fi
# Checks for programs.
AM_PROG_AR
AC_PROG_CC
AC_PROG_CXX
AC_PROG_AWK
AC_PROG_CC
......@@ -19,19 +39,35 @@ AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_PROG_LIBTOOL
# Checks for libraries.
AC_CHECK_LIB([crypto], [SHA256_Init],[found=yes],[],[])
AC_CHECK_LIB([rpm], [headerGet],[found=yes],[],[])
AC_CHECK_LIB([rpmio], [Fopen],[found=yes],[],[])
AC_CHECK_LIB([curl], [curl_easy_setopt],[found=yes],[],[])
AC_CHECK_LIB([z], [inflate],[found=yes],[],[])
AC_CHECK_LIB(rpm, headerGet,[rpm=yes], [rpm=no], [])
AC_CHECK_LIB([crypto], [SHA256_Init], [crypto=yes], [crypto=no], [])
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h])
AC_CHECK_HEADERS([curl/curl.h])
AC_CHECK_HEADERS([rpm/rpmlib.h])
AC_CHECK_HEADERS([rpm/rpmlib.h], [], [rpm=no])
AC_CHECK_HEADERS([openssl/evp.h], [], [crypto=no])
PKG_CHECK_EXISTS(cmocka,
[AC_CHECK_HEADERS([stdarg.h stddef.h setjmp.h],
[], dnl We are only intrested in action-if-not-found
[AC_MSG_WARN([Header files stdarg.h stddef.h setjmp.h are
required by cmocka])
cmocka_required_headers="no"
]
)
AS_IF([test x"$cmocka_required_headers" != x"no"],
[PKG_CHECK_MODULES([CMOCKA], [cmocka], [cmocka="yes"])]
)],
dnl PKG_CHECK_EXISTS ACTION-IF-NOT-FOUND
[AC_MSG_WARN([No libcmocka library found, cmocka tests
will not be built])])
AM_CONDITIONAL([RPM], [test "$rpm" = yes])
AM_CONDITIONAL([CRYPTO], [test "$crypto" = yes])
AM_CONDITIONAL([CMOCKA], [test "$cmocka" = yes])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
......@@ -45,26 +81,6 @@ AC_FUNC_MALLOC
AC_FUNC_MMAP
AC_CHECK_FUNCS([ftruncate memset munmap strstr])
# Enable debug.
AC_ARG_ENABLE([debug],
[ --enable-debug Turn on debugging],
[case "${enableval}" in
yes) debug=true ;;
no) debug=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
esac],[debug=false])
AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])
if test -z "$CFLAGS"; then
if test "$debug" == true; then
CFLAGS="-O0 -g"
else
CFLAGS="-O2 -g"
fi
elif test "$debug" == true; then
CFLAGS="$CFLAGS -O0 -g"
fi
# Check availability of initrd tools
DRACUT_SUBDIR=
AC_CHECK_FILE(/usr/bin/dracut, [DRACUT_SUBDIR=dracut], [])
......@@ -74,13 +90,26 @@ AC_CHECK_FILE(/usr/sbin/mkinitramfs,
[INITRAMFS_TOOLS_SUBDIR=initramfs-tools], [])
AC_SUBST([INITRAMFS_TOOLS_SUBDIR])
CFLAGS="$CFLAGS -Wall -Werror"
AC_SUBST(CFLAGS)
AC_CONFIG_FILES([Makefile
initrd/Makefile
initrd/dracut/Makefile
initrd/initramfs-tools/Makefile
docs/Makefile
include/Makefile
lib/Makefile
src/Makefile
scripts/Makefile])
docs/Makefile
include/Makefile
initrd/Makefile
initrd/dracut/Makefile
initrd/initramfs-tools/Makefile
lib/Makefile
generators/Makefile
parsers/Makefile
scripts/Makefile
src/Makefile
systemd/Makefile
tests/Makefile])
AC_OUTPUT
cat <<EOF
CFLAGS: ${CFLAGS}
EOF
name: digest-list-tools-parser-sig
Version: 0.2
Release: 1%{?dist}
Summary: Digest list parser signature
Source0: parser_data
Source1: parser_data.sig
Source2: parser_metadata
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
License: GPL-2.0
Url: https://github.com/euleros/digest-list-tools
Requires: digest-list-tools
BuildRequires: digest-list-tools
BuildArch: noarch
%description
This package includes the signature of the digest list parser.
%prep
%build
%install
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/ima/digest_lists
cp %{SOURCE0} ${RPM_BUILD_ROOT}%{_sysconfdir}/ima/digest_lists/
cp %{SOURCE1} ${RPM_BUILD_ROOT}%{_sysconfdir}/ima/digest_lists/
cp %{SOURCE2} ${RPM_BUILD_ROOT}%{_sysconfdir}/ima/digest_lists/
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%{_sysconfdir}/ima/digest_lists/parser_data
%{_sysconfdir}/ima/digest_lists/parser_data.sig
%{_sysconfdir}/ima/digest_lists/parser_metadata
%changelog
* Thu Apr 05 2018 Roberto Sassu <roberto.sassu@huawei.com> - 0.2
- PGP signatures
- Multiple digest algorithms
- User space digest list parser
- DEB package format
* Wed Nov 15 2017 Roberto Sassu <roberto.sassu@huawei.com> - 0.1
- Initial version
name: digest-list-tools
Version: 0.2
Release: 1%{?dist}
Summary: Digest list tools
Source0: %{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
License: GPL-2.0
Url: https://github.com/euleros/digest-list-tools
BuildRequires: autoconf automake libcurl-devel libtool rpm-devel dracut gzip
BuildRequires: txt2man
%if 0%{?suse_version}
BuildRequires: glibc-devel-static zlib-devel-static libopenssl-devel
BuildRequires: linux-glibc-devel keyutils-devel
%endif
%if 0%{?fedora}
BuildRequires: glibc-static zlib-static openssl-devel kernel-headers
BuildRequires: keyutils-libs-devel
%endif
%description
This package includes the tools for configure the IMA Digest Lists extension.
%prep
%setup -q
%build
autoreconf -iv
%configure
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/ima/digest_lists
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man1
cd %_builddir/%{name}-%{version}
txt2man -t %{name} README | gzip > ${RPM_BUILD_ROOT}%{_mandir}/man1/%{name}.1.gz
cd docs
for doc in $(ls *.txt); do txt2man -t ${doc%.txt} $doc |
gzip > ${RPM_BUILD_ROOT}%{_mandir}/man1/${doc%.txt}.1.gz; done
%post
ldconfig
%postun
ldconfig
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%dir %{_sysconfdir}/ima
%dir %{_sysconfdir}/ima/digest_lists
%{_bindir}/gen_digest_lists
%{_bindir}/gen_parser_metadata
%{_bindir}/setup_ima_digest_lists
%{_bindir}/upload_digest_lists
%{_bindir}/verify_digest_lists
%{_bindir}/modify_digest_lists
%{_libdir}/libdigestlists.so.0
%{_libdir}/libdigestlists.so.0.0.0
%dir /usr/lib/dracut/modules.d/98digestlist
%{_prefix}/lib/dracut/modules.d/98digestlist/module-setup.sh
%exclude /usr/lib64/libdigestlists.a
%exclude /usr/lib64/libdigestlists.la
%exclude /usr/lib64/libdigestlists.so
%exclude /usr/lib64/libparserstatic.a
%exclude /usr/lib64/libparserstatic.la
%doc
%dir /usr/share/digest-list-tools
%{_datarootdir}/digest-list-tools/README
%{_datarootdir}/digest-list-tools/gen_digest_lists.txt
%{_datarootdir}/digest-list-tools/setup_ima_digest_lists.txt
%{_datarootdir}/digest-list-tools/verify_digest_lists.txt
%{_datarootdir}/digest-list-tools/modify_digest_lists.txt
%{_datarootdir}/digest-list-tools/gen_parser_metadata.txt
%{_mandir}/man1/gen_digest_lists.1.gz
%{_mandir}/man1/gen_parser_metadata.1.gz
%{_mandir}/man1/setup_ima_digest_lists.1.gz
%{_mandir}/man1/verify_digest_lists.1.gz
%{_mandir}/man1/modify_digest_lists.1.gz
%{_mandir}/man1/upload_digest_lists.1.gz
%{_mandir}/man1/%{name}.1.gz
%changelog
* Thu Apr 05 2018 Roberto Sassu <roberto.sassu@huawei.com> - 0.2
- PGP signatures
- Multiple digest algorithms
- User space digest list parser
- DEB package format
* Wed Nov 15 2017 Roberto Sassu <roberto.sassu@huawei.com> - 0.1
- Initial version
dist_pkgdata_DATA = gen_digest_lists.txt \
setup_ima_digest_lists.txt \
verify_digest_lists.txt \
modify_digest_lists.txt \
gen_parser_metadata.txt
setup_ima_digest_lists_demo.txt \
upload_digest_lists.txt \
verify_digest_lists.txt
......@@ -7,75 +7,55 @@ gen_digest_lists [options]
DESCRIPTION
gen_digest_lists can be used to generate digest lists from the RPM/DEB
databases, from an RPM package, or from a list of digests in ASCII format.
gen_digest_lists can be used to generate digest lists with one of the
available generators (e.g. rpm, ima).
OPTIONS
-a: append metadata to an existing file
-d <directory>: directory containing digest lists
-d <directory>: directory where digest lists and metadata are stored
-f <format>: format of the input file
-f <input format>: format of the input where digests are taken from
- rpmdb: RPM database (default)
- debdb: DEB database
- rpmpkg: RPM package
- ascii: ASCII file with line format <algo>:<digest>
-i <path>: path of the input file
-h: display help
-i <path>: path of the file where digests are taken from
-o <operation>: operation to do:
- add: insert a new digest list at the position specified
- append: add a new digest list after the existing ones
- remove: remove a digest list at the position specified
- sign: sign a digest list
-m <file name>: metadata file name (default: metadata)
-p <position>: position of the file in the directory to add/remove
-o <output format>: output format of the digest list
- compact: compact digest list (default)
- rpm: RPM package header
-t <compact id>: type of compact list to generate
-w: files are mutable
-m <modifiers>: compact list modifiers separated by comma
-e <algorithm>: digest algorithm
-a <algorithm>: hash algorithm
-s: sign digest list with gpg
-s: sign generated digest lists
-k <key name>: gpg key name
-k <key>: key to sign
-j <distro name>: distribution name
-w [<key password>]: key password or prompt
-u <repo url>: URL of the repository
-h: display help
EXAMPLES
Generate a compact digest list for each installed RPM package and copy it to
/etc/ima/digest_lists.
# gen_digest_lists -d /etc/ima/digest_lists
Generate an RPM digest list for each installed RPM package and copy it to
/etc/ima/digest_lists.
# gen_digest_lists -d /etc/ima/digest_lists -o rpm
# gen_digest_lists -f rpm+db -t file -o append
Generate an RPM digest list from an RPM package and copy it to
/etc/ima/digest_lists.
# gen_digest_lists -d /etc/ima/digest_lists -f rpmpkg -i <RPM package>
Generate a compact digest list from a file containing digests of immutable files
and copy it to /etc/ima/digest_lists.
# gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file>
Generate a compact digest list from a file containing digests of mutable files
and copy it to /etc/ima/digest_lists.
# gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file> -w
# gen_digest_lists -f rpm+pkg -i <RPM path> -t file -o append
Generate a compact digest list from a file containing digests of mutable
files and copy it to /etc/ima/digest_lists. Append digest list metadata to
/etc/ima/digest_lists/metadata.
Generate a compact digest list of immutable files from the IMA measurement
list.
# gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file> -w -a
# gen_digest_lists -f ima+ima_ng -t file_immutable -o append
AUTHOR
......@@ -83,5 +63,6 @@ Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH. Free use of
this software is granted under the terms of the GNU Public License 2.0
(GPLv2).
NAME
gen_parser_metadata - generate parser metadata
SYNOPSIS
gen_parser_metadata [options]
DESCRIPTION
gen_digest_lists can be used to generate parser metadata, to be loaded by IMA
before the parser is executed.
The file 'parser_data' contains information about the parser (digest, version).
The file 'parser_data.sig' contains the PGP signature of 'parser_data'. The file
'parser_metadata' contains the parser data and signature with the metadata
format.
If the first two files are provided (e.g. in a software package), this tool
should be executed to generate the third one (which requires the path of the
parser).
OPTIONS
-d <directory>: directory where the data is stored
-h: display help
-e <algorithm>: digest algorithm
-p <parser data file>: name of parser data file
-m <parser metadata file>: name of parser metadata file
-i <parser binary path>: path of the digest list parser
-s: sign digest list with gpg
-k <key name>: gpg key name
-w: overwrite parser data and signature
-o <binary metadata path>: binary path included as metadata
EXAMPLES
Generate parser data, sign it, and write metadata
$ gen_parser_metadata -d /etc/ima_digest_lists -w -s
Write metadata with existing parser data
$ gen_parser_metadata -d /etc/ima_digest_lists
AUTHOR
Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
NAME
modify_digest_lists - modify digest list metadata
SYNOPSIS
modify_digest_lists [options]
DESCRIPTION
modify_digest_lists can be used to modify digest list metadata.
OPTIONS
-d <directory>: directory where digest lists and metadata are stored
(default: /etc/ima/digest_lists)
-m <file name>: metadata file name (default: metadata)
-h: display help
-e <algorithm>: digest algorithm
-r <entry ID>: ID of the entry to remove
-s: set ima_algo extended attribute
EXAMPLES
Display digest list metadata and digest list stored in /etc/ima/digest_lists.
# modify_digest_lists -d /etc/ima/digest_lists
Remove entry #1 from digest list metadata
# modify_digest_lists -d /etc/ima/digest_lists -r 1
AUTHOR
Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
......@@ -3,17 +3,19 @@ setup_ima_digest_lists - generate digest lists for measurement and appraisal
SYNOPSIS
setup_ima_digest_lists initial|immutable|mutable [options]
setup_ima_digest_lists parser|distro|immutable|mutable [options]
DESCRIPTION
setup_ima_digest_lists can be used to generate digest lists for measurement and
appraisal. Digest lists can be generated from the RPM/DEB database, or from IMA
appraisal. Digest lists can be generated from the RPM database, or from IMA
measurements.
COMMANDS
initial: generate digest lists from the RPM/DEB database
parser: generate digest lists for the parser
distro: generate digest lists from the RPM database
immutable: generate a digest list of immutable files from IMA measurements
......@@ -21,44 +23,39 @@ mutable: generate a digest list of mutable files from IMA measurements
OPTIONS
-h: display help
-d <directory>: directory where digest lists and metadata are stored
-e <algorithm>: digest algorithm
-a: append metadata
-d <directory>: directory where digest lists are stored
-i: use current IMA measurement list
-s: sign digest list with gpg
-k <key name>: gpg key name
-V <kernel version>: kernel version
-D <search dirs>: directories containing immutable/mutable files
-E <exclude dirs>: excluded directories
-f: freeze mutable files
-u: update digest lists of mutable files
-r: replace mutable files with backup
-g: generate digest lists of mutable files
-u: unfreeze mutable files
-a: run initramfs generator
-r: remove list of mutable files
-s: sign digest list
-g: generate digest lists from lists of mutable files
-v <private key>: path of private key in PEM format
-z: add empty files to the list of mutable files
-x <x509 certificate>: path of X.509 certificate in PEM format
-h: display help
EXAMPLES
Generate digest lists from the RPM database.
# setup_ima_digest_lists initial
# setup_ima_digest_lists distro
Generate a digest list of immutable files in /etc from IMA measurements and
append metadata.
Generate a digest list of immutable files in /etc from IMA measurements.
# setup_ima_digest_lists immutable -a -i -D "/etc"
......@@ -68,5 +65,6 @@ Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH. Free use of
this software is granted under the terms of the GNU Public License 2.0
(GPLv2).
NAME
setup_ima_digest_lists_demo - demo script to generate digest lists
SYNOPSIS
setup_ima_digest_lists_demo distro|immutable|mutable-generate|mutable-sign
[private key (PEM)] [x.509 certificate (PEM)]
DESCRIPTION
setup_ima_digest_lists_demo is a demo script that simplifies the generation
of digest lists.
COMMANDS
distro: generate digest lists from the RPM database
immutable: generate digest lists of immutable files from IMA measurements
mutable-generate: generate digest lists of mutable files from IMA measurements
mutable-sign: sign digest lists after update with final digest
OPTIONS
<private key>: path of private key in PEM format
<x509 certificate>: path of X.509 certificate in PEM format
-h: display help
EXAMPLES
Generate digest lists from the RPM database.
# setup_ima_digest_lists distro
Generate a digest list of immutable files in /etc from IMA measurements.
# setup_ima_digest_lists immutable -a -i -D "/etc"
AUTHOR
Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH. Free use of
this software is granted under the terms of the GNU Public License 2.0
(GPLv2).
NAME
upload_digest_lists - upload digest list metadata and digest lists
upload_digest_lists - upload digest lists
SYNOPSIS
......@@ -7,24 +7,27 @@ upload_digest_lists [options]
DESCRIPTION
upload_digest_lists uploads digest list metadata and digest lists to IMA.
upload_digest_lists uploads digest lists to IMA or write the converted digest
lists to a file.
OPTIONS
-d <directory>: directory where digest lists and metadata are stored
(default: /etc/ima/digest_lists)
-d <directory>: directory containing digest lists
-m <file name>: metadata file name (default: metadata)
-o <file>: write converted digest list to a file
-h: display help
-e <algorithm>: digest algorithm
EXAMPLES
Upload digest list metadata and digest lists stored in /etc/ima/digest_lists.
Upload digest lists stored in /etc/ima/digest_lists.
# upload_digest_lists -d /etc/ima/digest_lists
Convert all digest lists in /etc/ima/digest_lists to the compact format and
save the converted lists to converted_lists.
# upload_digest_lists -d /etc/ima/digest_lists -m metadata -e sha256
# upload_digest_lists -d /etc/ima/digest_lists -o converted_lists
AUTHOR
......@@ -32,5 +35,6 @@ Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
Copyright (C) 2018,2019 Huawei Technologies Duesseldorf GmbH. Free use of
this software is granted under the terms of the GNU Public License 2.0
(GPLv2).
NAME
verify_digest_lists - verify integrity of digest list metadata and digest lists
verify_digest_lists - verify the integrity of digest lists
SYNOPSIS
......@@ -11,23 +11,15 @@ verify_digest_lists can be used to verify the integrity of digest lists.
OPTIONS
-d <directory>: directory where digest lists and metadata are stored
(default: /etc/ima/digest_lists)
-m <file name>: metadata file name (default: metadata)
-i <digest>: expected metadata digest
-d <directory>: directory containing digest lists
-h: display help
-e <algorithm>: digest algorithm
EXAMPLES
Verify digest list metadata and digest list stored in /etc/ima/digest_lists.
Verify digest lists stored in /etc/ima/digest_lists.
# verify_digest_lists -d /etc/ima/digest_lists -m metadata -e sha256 \
-i <metadata digest>
# verify_digest_lists
AUTHOR
......@@ -35,5 +27,6 @@ Written by Roberto Sassu, <roberto.sassu at huawei.com>.
COPYING
Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH. Free use of this
software is granted under the terms of the GNU Public License 2.0 (GPLv2).
Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH. Free use of
this software is granted under the terms of the GNU Public License 2.0
(GPLv2).
digestlistdir=$(libdir)/digestlist
digestlist_LTLIBRARIES=libgenerator-ima.la libgenerator-copy.la
libgenerator_LDFLAGS=-no-undefined -avoid-version
if CMOCKA
libgenerator_LIBADD=$(top_srcdir)/lib/libdigestlist-base-test.la
else
libgenerator_LIBADD=$(top_srcdir)/lib/libdigestlist-base.la
endif
libgenerator_CFLAGS=-I$(top_srcdir)/include
libgenerator_ima_la_LDFLAGS=${libgenerator_LDFLAGS}
libgenerator_ima_la_LIBADD=${libgenerator_LIBADD}
libgenerator_ima_la_CFLAGS=${libgenerator_CFLAGS}
libgenerator_ima_la_SOURCES=ima.c
libgenerator_copy_la_LDFLAGS=${libgenerator_LDFLAGS}
libgenerator_copy_la_LIBADD=${libgenerator_LIBADD}
libgenerator_copy_la_CFLAGS=${libgenerator_CFLAGS}
libgenerator_copy_la_SOURCES=copy.c
if CRYPTO
digestlist_LTLIBRARIES+=libgenerator-compact.la
libgenerator_compact_la_LDFLAGS=${libgenerator_LDFLAGS}
libgenerator_compact_la_LIBADD=${libgenerator_LIBADD} \
$(top_srcdir)/lib/libdigestlist-gen.la
libgenerator_compact_la_CFLAGS=${libgenerator_CFLAGS}
libgenerator_compact_la_SOURCES=compact.c
endif
if RPM
digestlist_LTLIBRARIES+=libgenerator-rpm.la
libgenerator_rpm_la_LDFLAGS=${libgenerator_LDFLAGS}
libgenerator_rpm_la_LIBADD=${libgenerator_LIBADD} -lrpm -lrpmio
libgenerator_rpm_la_CFLAGS=${libgenerator_CFLAGS}
libgenerator_rpm_la_SOURCES=rpm.c
endif
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: compact.c
* Generates compact digest lists.
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "compact_list.h"
#include "crypto.h"
#include "xattr.h"
#define FORMAT "compact"
int generator(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out, enum compact_types type,
u16 modifiers, enum hash_algo algo)
{
struct path_struct *cur;
u8 digest[SHA512_DIGEST_SIZE];
char filename[NAME_MAX + 1], *basename, *link, *target;
LIST_HEAD(list_head);
struct list_struct *list;
struct stat st;
int ret = 0, fd, prefix_len;
if (list_empty(head_in)) {
printf("Input path not specified\n");
return -EINVAL;
}
cur = list_first_entry(head_in, struct path_struct, list);
basename = strrchr(cur->path, '/');
if (!basename)
basename = cur->path;
else
basename++;
prefix_len = gen_filename_prefix(filename, sizeof(filename), pos,
FORMAT, type);
snprintf(filename + prefix_len, sizeof(filename) - prefix_len, "%s",
basename);
fd = openat(dirfd, filename, O_WRONLY | O_CREAT, 0600);
if (fd < 0) {
printf("Cannot open %s\n", filename);
return fd;
}
list = compact_list_init(&list_head, type, modifiers, algo);
if (!list)
goto out;
list_for_each_entry(cur, head_in, list) {
if (lstat(cur->path, &st) == -1)
continue;
if (S_ISLNK(st.st_mode)) {
target = realpath(cur->path, NULL);
if (ret < 0)
goto out;
free(cur->path);
cur->path = target;
}
ret = calc_file_digest(digest, -1, cur->path, algo);
if (ret < 0) {
printf("Cannot calculate digest of %s\n", cur->path);
goto out_free;
}
ret = compact_list_add_digest(fd, list, digest);
if (ret < 0) {
printf("Cannot add digest to compact list\n");
goto out_free;
}
if (getuid() == 0) {
ret = write_ima_xattr(-1, cur->path, NULL, 0, NULL, 0,
algo);
if (ret < 0) {
printf("Cannot write xattr to %s\n", cur->path);
goto out_free;
}
}
if (!strcmp(basename, "upload_digest_lists")) {
link = strchr(strchr(filename, '-') + 1, '-') + 1;
unlinkat(dirfd, link, 0);
ret = symlinkat(filename, dirfd, link);
if (ret < 0) {
printf("Cannot create symbolic link\n");
goto out_free;
}
}
}
ret = add_path_struct(filename, head_out);
if (ret < 0)
goto out_free;
out_free:
ret = compact_list_flush_all(fd, &list_head);
if (ret < 0)
printf("Cannot write digest list to %s\n", filename);
out:
close(fd);
if (ret < 0)
unlinkat(dirfd, filename, 0);
return ret;
}
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: copy.c
* Copy existing file to a digest list directory.
*/
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include "lib.h"
#include "compact_list.h"
int generator(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out, enum compact_types type,
u16 modifiers, enum hash_algo algo)
{
struct path_struct *cur;
char filename[NAME_MAX + 1];
char *basename;
void *buf;
loff_t size;
int ret = 0, fd;
if (list_empty(head_in)) {
printf("Input path not specified\n");
return -EINVAL;
}
list_for_each_entry(cur, head_in, list) {
basename = strrchr(cur->path, '/');
if (!basename)
basename = cur->path;
else
basename++;
snprintf(filename, sizeof(filename), "%d-%s_list-%s", pos,
compact_types_str[type], basename);
ret = read_file_from_path(-1, cur->path, &buf, &size);
if (ret < 0)
goto out;
fd = openat(dirfd, filename, O_WRONLY | O_CREAT, 0600);
if (fd < 0) {
munmap(buf, size);
ret = fd;
goto out;
}
ret = write_check(fd, buf, size);
munmap(buf, size);
close(fd);
if (ret < 0)
goto out;
ret = add_path_struct(filename, head_out);
if (ret < 0)
goto out;
pos++;
}
out:
if (ret < 0)
unlinkat(dirfd, filename, 0);
return ret;
}
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: ima.c
* Generates IMA digest lists.
*/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include "compact_list.h"
#define IMA_ASCII_PATH IMA_SECURITYFS_PATH "/ascii_runtime_measurements"
#define IMA_ASCII_FORMAT "ima+ima_ng"
int ima_ng_generator(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out, enum compact_types type,
u16 modifiers, enum hash_algo algo)
{
char filename[NAME_MAX + 1], buffer[1024];
time_t t = time(NULL);
struct path_struct *cur;
struct tm tm;
size_t size;
int ret, i, fd_in, fd_out, prefix_len;
if (list_empty(head_in)) {
ret = add_path_struct(IMA_ASCII_PATH, head_in);
if (ret < 0)
return ret;
}
tm = *localtime(&t);
prefix_len = gen_filename_prefix(filename, sizeof(filename), pos,
IMA_ASCII_FORMAT, type);
snprintf(filename + prefix_len, sizeof(filename) - prefix_len,
"%04d%02d%02d_%02d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
fd_out = openat(dirfd, filename, O_CREAT | O_WRONLY, 0600);
if (fd_out < 0) {
printf("Cannot open %s\n", filename);
return fd_out;
}
for (i = 0; i < sizeof(modifiers) * 8; i++) {
if (!(modifiers & (1 << i)))
continue;
if (i) {
ret = write_check(fd_out, " ", 1);
if (ret < 0)
goto out;
}
ret = write_check(fd_out, compact_modifiers_str[i],
strlen(compact_modifiers_str[i]));
if (ret < 0)
goto out;
}
ret = write_check(fd_out, "\n", 1);
if (ret < 0)
goto out;
list_for_each_entry(cur, head_in, list) {
fd_in = open(cur->path, O_RDONLY);
if (fd_in < 0) {
printf("Cannot open %s\n", IMA_ASCII_PATH);
goto out;
}
while ((size = read(fd_in, buffer, sizeof(buffer))) > 0) {
ret = write_check(fd_out, buffer, size);
if (ret < 0) {
close(fd_in);
goto out;
}
}
close(fd_in);
}
ret = add_path_struct(filename, head_out);
if (ret < 0)
goto out;
out:
close(fd_out);
if (ret < 0)
unlinkat(dirfd, filename, 0);
return ret;
}
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
......@@ -9,69 +9,63 @@
* License.
*
* File: rpm.c
* Writes RPM digest lists.
* Generate RPM digest lists.
*/
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "rpm.h"
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmtag.h>
void get_rpm_path(Header rpm, char *outdir, char *output_path,
enum digest_data_sub_types output_fmt)
#include "compact_list.h"
#include "xattr.h"
#define FORMAT "rpm"
const unsigned char rpm_header_magic[8] = {
0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
};
static void gen_filename(Header rpm, int pos, enum compact_types type,
char *filename, int filename_len)
{
char *prefix = (output_fmt == DATA_SUB_TYPE_RPM) ? "rpm" : "compact";
rpmtd name = rpmtdNew(), version = rpmtdNew();
rpmtd release = rpmtdNew(), arch = rpmtdNew();
int prefix_len;
headerGet(rpm, RPMTAG_NAME, name, 0);
headerGet(rpm, RPMTAG_VERSION, version, 0);
headerGet(rpm, RPMTAG_RELEASE, release, 0);
headerGet(rpm, RPMTAG_ARCH, arch, 0);
snprintf(output_path, MAX_PATH_LENGTH, "%s/%s-%s-%s-%s.%s",
outdir, prefix, rpmtdGetString(name), rpmtdGetString(version),
rpmtdGetString(release), rpmtdGetString(arch));
rpmtdReset(name);
rpmtdReset(version);
rpmtdReset(release);
rpmtdReset(arch);
}
prefix_len = gen_filename_prefix(filename, filename_len, pos, FORMAT,
type);
static int write_rpm_header_signature(Header rpm, char *digest_list_path)
{
char rpm_sig_path[MAX_PATH_LENGTH];
rpmtd signature = rpmtdNew();
int ret, fd;
snprintf(rpm_sig_path, sizeof(rpm_sig_path), "%s.sig",
digest_list_path);
fd = open(rpm_sig_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
return -EACCES;
headerGet(rpm, RPMTAG_RSAHEADER, signature, 0);
ret = write_check(fd, signature->data, signature->count);
rpmtdReset(signature);
snprintf(filename + prefix_len, filename_len - prefix_len,
"%s-%s-%s-%s", rpmtdGetString(name), rpmtdGetString(version),
rpmtdGetString(release), rpmtdGetString(arch));
close(fd);
return ret;
rpmtdFree(name);
rpmtdFree(version);
rpmtdFree(release);
rpmtdFree(arch);
}
static int write_rpm_header(Header rpm, char *outdir, char *output_path)
static int gen_rpm_digest_list(Header rpm, int dirfd, char *filename,
struct list_head *head_out)
{
rpmtd immutable = rpmtdNew();
rpmtd immutable;
ssize_t ret;
int fd;
get_rpm_path(rpm, outdir, output_path, DATA_SUB_TYPE_RPM);
if (strstr(output_path, "gpg-pubkey") != NULL)
return -ENOENT;
fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
fd = openat(dirfd, filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
return -EACCES;
......@@ -79,90 +73,100 @@ static int write_rpm_header(Header rpm, char *outdir, char *output_path)
if (ret < 0)
goto out;
immutable = rpmtdNew();
headerGet(rpm, RPMTAG_HEADERIMMUTABLE, immutable, 0);
ret = write_check(fd, immutable->data, immutable->count);
rpmtdReset(immutable);
rpmtdFree(immutable);
out:
close(fd);
if (ret < 0)
unlinkat(dirfd, filename, 0);
else
ret = add_path_struct(filename, head_out);
return ret;
}
static int write_digest_list_from_rpm(Header hdr, char *outdir,
enum digest_data_sub_types output_fmt,
char *digest_list_path, int sign,
char *gpg_key_name)
static int find_file(struct list_head *head, char *filename)
{
int ret;
struct path_struct *cur;
char *filename_ptr, *cur_path_ptr;
if (list_empty(head))
return 0;
if (output_fmt == DATA_SUB_TYPE_COMPACT_LIST) {
ret = compact_list_from_rpm(hdr, outdir, digest_list_path);
if (!ret && sign)
ret = sign_digest_list(digest_list_path, gpg_key_name);
} else if (output_fmt == DATA_SUB_TYPE_RPM) {
ret = write_rpm_header(hdr, outdir, digest_list_path);
if (!ret)
ret = write_rpm_header_signature(hdr, digest_list_path);
} else {
ret = -EINVAL;
list_for_each_entry(cur, head, list) {
cur_path_ptr = strchr(cur->path, '-') + 1;
cur_path_ptr = strchr(cur_path_ptr, '-') + 1;
filename_ptr = strchr(filename, '-') + 1;
filename_ptr = strchr(filename_ptr, '-') + 1;
if (!strcmp(cur_path_ptr, filename_ptr))
return 1;
}
return ret;
return 0;
}
int digest_list_from_rpmdb(char *outdir, char *metadata_path,
enum digest_data_sub_types output_fmt,
int sign, char *gpg_key_name)
int db_generator(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out, enum compact_types type,
u16 modifiers, enum hash_algo algo)
{
char digest_list_path[MAX_PATH_LENGTH];
char filename[NAME_MAX + 1];
rpmts ts = NULL;
Header hdr;
rpmdbMatchIterator mi;
LIST_HEAD(digest_list_head);
int ret;
ret = get_digest_lists(dirfd, type, &digest_list_head);
if (ret < 0)
goto out;
ts = rpmtsCreate();
ret = rpmReadConfigFiles(NULL, NULL);
if (ret != RPMRC_OK) {
rpmlog(RPMLOG_NOTICE, "Unable to read RPM configuration.\n");
exit(1);
goto out;
}
mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
while ((hdr = rpmdbNextIterator(mi)) != NULL) {
hdr = headerLink(hdr);
ret = write_digest_list_from_rpm(hdr, outdir, output_fmt,
digest_list_path, sign,
gpg_key_name);
if (ret < 0 && ret != -ENOENT) {
pr_err("Failed to write %s, ret: %d\n",
digest_list_path, ret);
goto end;
} else if (ret == -ENOENT) {
ret = 0;
goto end;
}
gen_filename(hdr, pos, type, filename, sizeof(filename));
ret = write_digests_and_metadata(outdir, metadata_path,
digest_list_path,
output_fmt, sign);
end:
headerFree(hdr);
if (strstr(filename, "gpg-pubkey") != NULL)
continue;
if (ret < 0)
if (find_file(&digest_list_head, filename))
continue;
ret = gen_rpm_digest_list(hdr, dirfd, filename, head_out);
if (ret < 0) {
printf("Cannot generate %s digest list\n", filename);
break;
}
pos++;
}
if (hdr)
headerFree(hdr);
rpmdbFreeIterator(mi);
rpmFreeRpmrc();
rpmtsFree(ts);
out:
free_path_structs(&digest_list_head);
return ret;
}
int digest_lists_from_rpmpkg(char *outdir, char *metadata_path,
char *package_path,
enum digest_data_sub_types output_fmt,
int sign, char *gpg_key_name)
static int _pkg_generator(int dirfd, int pos, char *path,
struct list_head *head_out, enum compact_types type,
enum hash_algo algo)
{
char digest_list_path[MAX_PATH_LENGTH];
char filename[NAME_MAX + 1];
Header hdr;
rpmts ts = NULL;
FD_t fd;
......@@ -170,47 +174,62 @@ int digest_lists_from_rpmpkg(char *outdir, char *metadata_path,
rpmVSFlags vsflags = 0;
ts = rpmtsCreate();
ret = rpmReadConfigFiles(NULL, NULL);
if (ret != RPMRC_OK) {
rpmlog(RPMLOG_NOTICE, "Unable to read RPM configuration.\n");
exit(1);
ret = -ENOENT;
goto out_ts;
}
vsflags |= _RPMVSF_NODIGESTS;
vsflags |= _RPMVSF_NOSIGNATURES;
rpmtsSetVSFlags(ts, vsflags);
fd = Fopen(package_path, "r.ufdio");
fd = Fopen(path, "r.ufdio");
if ((!fd) || Ferror(fd)) {
rpmlog(RPMLOG_NOTICE, "Failed to open package file (%s)\n",
Fstrerror(fd));
if (fd)
Fclose(fd);
return -EINVAL;
rpmlog(RPMLOG_NOTICE, "Failed to open package file %s, %s\n",
path, Fstrerror(fd));
ret = -EACCES;
goto out_fd;
}
ret = rpmReadPackageFile(ts, fd, package_path, &hdr);
ret = rpmReadPackageFile(ts, fd, "rpm", &hdr);
if (ret != RPMRC_OK) {
rpmlog(RPMLOG_NOTICE, "Could not read package file\n");
goto out;
rpmlog(RPMLOG_NOTICE, "Could not read package file %s\n", path);
goto out_fd;
}
ret = write_digest_list_from_rpm(hdr, outdir, output_fmt,
digest_list_path, sign,
gpg_key_name);
if (ret < 0 && ret != -ENOENT) {
pr_err("Failed to write %s, ret: %d\n", digest_list_path, ret);
goto out;
} else if (ret == -ENOENT) {
ret = 0;
goto out;
}
gen_filename(hdr, pos, type, filename, sizeof(filename));
ret = write_digests_and_metadata(outdir, metadata_path,
digest_list_path, output_fmt, sign);
out:
ret = gen_rpm_digest_list(hdr, dirfd, filename, head_out);
if (ret < 0)
printf("Cannot generate %s digest list\n", filename);
out_fd:
Fclose(fd);
out_ts:
rpmtsFree(ts);
return ret;
}
int pkg_generator(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out, enum compact_types type,
u16 modifiers, enum hash_algo algo)
{
struct path_struct *cur;
int ret = 0;
if (list_empty(head_in)) {
printf("Input path not specified\n");
return -EINVAL;
}
list_for_each_entry(cur, head_in, list) {
ret = _pkg_generator(dirfd, pos, cur->path, head_out, type,
algo);
if (ret < 0)
return ret;
}
return ret;
}
noinst_HEADERS = kernel_lib.h \
kernel_ima.h \
compact_list.h \
rpm.h \
metadata.h \
list.h \
lib.h \
pgp.h
compact_list.h \
crypto.h \
xattr.h
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
......@@ -15,14 +15,41 @@
#ifndef _COMPACT_LIST_H
#define _COMPACT_LIST_H
#include "metadata.h"
#include "rpm.h"
#include "lib.h"
int compact_list_from_rpm(Header rpm, char *outdir, char *output_path);
#define COMPACT_LIST_SIZE_MAX 64 * 1024 * 1024 - 1
int digest_list_from_ascii(char *outdir, char *metadata_path,
char *input_path,
enum digest_data_sub_types output_fmt,
int is_mutable, int sign, char *gpg_key_name);
extern char *compact_types_str[COMPACT__LAST];
extern char *compact_modifiers_str[COMPACT_MOD__LAST];
struct list_struct {
struct list_head list;
struct compact_list_hdr *hdr;
};
struct list_struct *compact_list_init(struct list_head *list_head,
enum compact_types type, u16 modifiers,
enum hash_algo algo);
int compact_list_add_digest(int fd, struct list_struct *list, u8 *digest);
int compact_list_upload(int fd, struct list_struct *list);
int compact_list_flush_all(int fd, struct list_head *list_head);
typedef int (*generator_func)(int dirfd, int pos, struct list_head *head_in,
struct list_head *head_out,
enum compact_types type, u16 modifiers,
enum hash_algo algo);
typedef int (*parser_func)(int imafd, struct list_head *head,
loff_t size, void *buf);
int gen_filename_prefix(char *filename, int filename_len, int pos,
const char *format, enum compact_types type);
typedef int (*filter_lists)(const struct dirent *file);
int filter_parser_list_symlink(const struct dirent *file);
extern filter_lists filter[COMPACT__LAST];
int get_digest_lists(int dirfd, enum compact_types type,
struct list_head *head);
int compare_lists(const struct dirent **e1, const struct dirent **e2);
#endif /*_COMPACT_LIST_H*/
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: crypto.h
* Header of crypto.c.
*/
#ifndef _CRYPTO_H
#define _CRYPTO_H
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "lib.h"
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#define MAX_SIGNATURE_SIZE 1024
int calc_file_digest(u8 *digest, int dirfd, char *path, enum hash_algo algo);
int sign_files(int dirfd, struct list_head *head, char *key_path,
char *keypass, enum hash_algo algo);
struct key_struct {
struct list_head list;
RSA *key;
u8 keyid[4];
};
void free_keys(struct list_head *head);
struct key_struct *new_key(struct list_head *head, int dirfd, char *key_path,
char *keypass, bool private);
struct key_struct *lookup_key(struct list_head *head, int dirfd, char *key_path,
u8 *keyid);
int verify_file(struct list_head *head, int dirfd, char *filename);
#endif /*_CRYPTO_H*/
/*
* Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: deb.h
* Header of deb.c.
*/
#ifndef _DEB_H
#define _DEB_H
#include "kernel_ima.h"
#include "metadata.h"
#define UBUNTU_REPO_URL "http://archive.ubuntu.com/ubuntu"
#define DPKG_QUERY_FMT "'${Package} ${Version} ${Architecture}\n'"
#define DPKG_QUERY_CMD "dpkg-query -W -f " DPKG_QUERY_FMT
int ima_parse_deb_package(loff_t size, void *buf, u16 data_algo, void *ctx,
callback_func func);
int ima_parse_deb_packages_gz(loff_t size, void *buf, u16 data_algo, void *ctx,
callback_func func);
int ima_parse_deb_release(loff_t size, void *buf, u16 data_algo, void *ctx,
callback_func func);
int digest_list_from_deb_mirror(char *outdir, char *metadata_path,
enum digest_data_sub_types output_fmt,
char *distro, char *repo_url);
#endif /* _DEB_H */
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: kernel_ima.h
* IMA functions header
*/
#ifndef _KERNEL_IMA_H
#define _KERNEL_IMA_H
#include "kernel_lib.h"
#include "securityfs.h"
#include "lib.h"
#define min(x,y) x > y ? y : x
#define ENFORCE_FIELDS 0x00000001
#define ENFORCE_BUFEND 0x00000002
#define DIGEST_FLAG_DIGEST_ALGO 0x01
#define DIGEST_FLAG_IMMUTABLE 0x02
#define PARSER_STRING "~parser~\n"
#define REQ_PARSER_VERSION 1
#define REQ_METADATA_VERSION 1
#define IMA_MAX_DIGEST_SIZE 64
#define IMA_MEASURE 0x00000001
#define IMA_APPRAISE 0x00000004
extern int ima_hash_algo;
extern int ima_canonical_fmt;
extern int current;
extern int parser_task;
extern int ima_digest_list_actions;
struct ima_field_data {
u8 *data;
u_int32_t len;
};
enum compact_list_entry_ids {COMPACT_DIGEST, COMPACT_DIGEST_MUTABLE,
COMPACT_DIGEST_LIST};
struct compact_list_hdr {
u16 entry_id;
u16 algo;
u32 count;
u32 datalen;
} __attribute__((packed));
enum digest_metadata_fields {DATA_ALGO, DATA_TYPE, DATA_TYPE_EXT,
DATA_DIGEST_ALGO, DATA_DIGEST,
DATA_SIG_FMT, DATA_SIG,
DATA_FILE_PATH, DATA_LENGTH, DATA__LAST};
enum data_sig_formats {SIG_FMT_NONE, SIG_FMT_IMA, SIG_FMT_PGP, SIG_FMT_PKCS7};
enum digest_data_types {DATA_TYPE_HEADER, DATA_TYPE_DIGEST_LIST, DATA_TYPE_KEY,
DATA_TYPE_PARSER, DATA_TYPE_REG_FILE};
enum digest_data_sub_types {DATA_SUB_TYPE_COMPACT_LIST,
DATA_SUB_TYPE_RPM,
DATA_SUB_TYPE_DEB_RELEASE,
DATA_SUB_TYPE_DEB_PACKAGES_GZ,
DATA_SUB_TYPE_DEB_PACKAGE,
DATA_SUB_TYPE__LAST};
int ima_hash_setup(char *str);
int ima_get_buflen(int maxfields, struct ima_field_data *fields,
unsigned long *len_mask);
int ima_write_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname);
int ima_check_parser(u8 *data, u32 data_len, u8 **digest, u16 *digest_algo);
ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf);
typedef int (*callback_func)(void *ctx, char *line);
#endif /* _KERNEL_IMA_H */
......@@ -2,7 +2,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright 2007 rPath, Inc. - All Rights Reserved
* Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.com>
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
......@@ -20,9 +20,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <linux/xattr.h>
#ifdef __BIG_ENDIAN__
#include <linux/byteorder/big_endian.h>
#else
#include <linux/byteorder/little_endian.h>
#endif
#include "list.h"
/* kernel types */
typedef u_int8_t u8;
......@@ -30,51 +39,14 @@ typedef u_int16_t u16;
typedef u_int32_t u32;
typedef u_int64_t u64;
typedef int bool;
typedef long loff_t;
#define true 1
#define false 0
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
/* bitmap */
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define BITS_PER_LONG 64
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#define small_const_nbits(nbits) \
(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
static inline bool constant_test_bit(int nr, const void *addr)
{
const u32 *p = (const u32 *)addr;
return ((1UL << (nr & 31)) & (p[nr >> 5])) != 0;
}
#define test_bit(nr,addr) constant_test_bit((nr),(addr))
/* errors */
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define EIO 5 /* I/O error */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EEXIST 17 /* File exists */
#define EINVAL 22 /* Invalid argument */
#define ENODATA 61 /* No data available */
#define EBADMSG 74 /* Not a data message */
#define EMSGSIZE 90 /* Message too long */
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
#define pr_err printf
#define pr_info printf
......@@ -94,6 +66,8 @@ static inline void pr_debug(const char *__restrict __format, ...)
/* endianness conversion */
#define be32_to_cpu __be32_to_cpu
#define be16_to_cpu __be16_to_cpu
#define cpu_to_be32 __cpu_to_be32
#define cpu_to_be16 __cpu_to_be16
#define le16_to_cpu __le16_to_cpu
#define le32_to_cpu __le32_to_cpu
#define cpu_to_le16 __cpu_to_le16
......@@ -142,20 +116,75 @@ enum hash_algo {
HASH_ALGO__LAST
};
/* from crypto/hash_info.c */
extern const char *const hash_algo_name[HASH_ALGO__LAST];
extern const int hash_digest_size[HASH_ALGO__LAST];
void bitmap_zero(unsigned long *dst, unsigned int nbits);
void bitmap_set(unsigned long *map, unsigned int start, int len);
/* from kernel.h */
int hex2bin(u8 *dst, const char *src, size_t count);
#define XATTR_SECURITY_PREFIX "security."
#define XATTR_IMA_SUFFIX "ima"
#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
/* from xattr.h */
#define XATTR_IMA_ALGO_SUFFIX "ima_algo"
#define XATTR_NAME_IMA_ALGO XATTR_SECURITY_PREFIX XATTR_IMA_ALGO_SUFFIX
/* from ima.h */
extern bool ima_canonical_fmt;
/* from integrity.h */
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
EVM_XATTR_PORTABLE_DIGSIG,
IMA_XATTR_LAST
};
enum evm_ima_sig_fmt {
SIG_FMT_IMA,
SIG_FMT_PGP,
SIG_FMT__LAST,
};
struct signature_v2_hdr {
uint8_t type; /* xattr type */
uint8_t version; /* signature format version */
uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */
__be32 keyid; /* IMA key identifier - not X509/PGP specific */
__be16 sig_size; /* signature size */
uint8_t sig[0]; /* signature payload */
} __attribute__((packed));
/* from integrity.h */
enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE, COMPACT__LAST };
enum compact_modifiers { COMPACT_MOD_IMMUTABLE, COMPACT_MOD__LAST };
struct ima_digest {
struct list_head list;
enum hash_algo algo;
enum compact_types type;
u16 modifiers;
u8 digest[0];
};
/* from ima_digest_list.c */
struct compact_list_hdr {
u8 version;
u8 _reserved;
u16 type;
u16 modifiers;
u16 algo;
u32 count;
u32 datalen;
} __attribute__((packed));
typedef int (*add_digest_func)(u8 *digest, enum hash_algo algo,
enum compact_types type, u16 modifiers);
int default_func(u8 *digest, enum hash_algo algo, enum compact_types type,
u16 modifiers);
int ima_parse_compact_list(loff_t size, void *buf,
add_digest_func ima_digest_data_entry);
#endif /* _KERNEL_LIB_H */
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
......@@ -9,28 +9,51 @@
* License.
*
* File: lib.h
* Header of lib.h.
* Library header.
*/
#ifndef _LIB_H
#define _LIB_H
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <dlfcn.h>
#include <dirent.h>
#include "list.h"
#include "kernel_lib.h"
#define MAX_PATH_LENGTH 2048
#ifdef UNIT_TESTING
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#endif
extern char *digest_lists_dir_path;
extern int parse_metadata, remove_file, set_ima_algo;
int calc_digest(u8 *digest, void *data, int len, enum hash_algo algo);
int calc_file_digest(u8 *digest, char *path, enum hash_algo algo);
int check_digest(void *data, int len, char *path,
enum hash_algo algo, u8 *input_digest);
int read_file_from_path(const char *path, void **buf, loff_t *size);
#define SYSFS_PATH "/sys"
#define SECURITYFS_PATH SYSFS_PATH "/kernel/security"
#define IMA_SECURITYFS_PATH SECURITYFS_PATH "/ima"
int read_file_from_path(int dirfd, const char *path, void **buf, loff_t *size);
ssize_t write_check(int fd, const void *buf, size_t count);
void hexdump(u8 *buf, int len);
#endif /* _LIB_H */
struct lib {
struct list_head list;
char *format;
void *handle;
void *func;
};
struct lib *lookup_lib(struct list_head *head, const char *lib_type,
const char *format, int format_len);
void free_libs(struct list_head *head);
struct path_struct {
struct list_head list;
char *path;
};
int add_path_struct(char *path, struct list_head *head);
void move_path_structs(struct list_head *dest, struct list_head *src);
void free_path_structs(struct list_head *head);
#endif /*_LIB_H*/
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
# define POISON_POINTER_DELTA 0
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA)
/*
* Copied from include/linux/...
*/
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
struct list_head {
struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) ({ \
struct list_head *head__ = (ptr); \
struct list_head *pos__ = READ_ONCE(head__->next); \
pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
})
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
#endif /*_LINUX_LIST_H*/
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: metadata.h
* Header of metadata.c.
*/
#ifndef _METADATA_H
#define _METADATA_H
#include "kernel_ima.h"
#include "pgp.h"
enum input_formats { INPUT_FMT_RPMDB, INPUT_FMT_RPMPKG,
INPUT_FMT_DIGEST_LIST_ASCII, INPUT_FMT_DEBDB,
INPUT_FMT__LAST };
int write_digests_and_metadata(char *outdir, char *metadata_path,
char *digest_list_path,
enum digest_data_sub_types output_fmt, int sign);
int write_parser_data(char *parser_data_path, char *binary_path);
int write_parser_metadata(char *parser_data_path, char *parser_metadata_path,
char *binary_path);
int write_metadata_header(char *metadata_path);
int write_pgp_key(char *metadata_path, char *pgp_key_path);
#endif /*_METADATA_H*/
/*
* Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
* Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH
*
* Authors:
* David Howells <dhowells@redhat.com>
* Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: pgp.h
* Header of pgp.c.
*/
#ifndef _PGP_H
#define _PGP_H
#include "kernel_lib.h"
#include "lib.h"
struct pgp_key_ID {
u8 id[8];
} __attribute__((packed));
struct pgp_time {
u8 time[4];
} __attribute__((packed));
/*
* PGP public-key algorithm identifiers [RFC4880: 9.1]
*/
enum pgp_pubkey_algo {
PGP_PUBKEY_RSA_ENC_OR_SIG = 1,
PGP_PUBKEY_RSA_ENC_ONLY = 2,
PGP_PUBKEY_RSA_SIG_ONLY = 3,
PGP_PUBKEY_ELGAMAL = 16,
PGP_PUBKEY_DSA = 17,
PGP_PUBKEY__LAST
};
/*
* PGP symmetric-key algorithm identifiers [RFC4880: 9.2]
*/
enum pgp_symkey_algo {
PGP_SYMKEY_PLAINTEXT = 0,
PGP_SYMKEY_IDEA = 1,
PGP_SYMKEY_3DES = 2,
PGP_SYMKEY_CAST5 = 3,
PGP_SYMKEY_BLOWFISH = 4,
PGP_SYMKEY_AES_128KEY = 7,
PGP_SYMKEY_AES_192KEY = 8,
PGP_SYMKEY_AES_256KEY = 9,
PGP_SYMKEY_TWOFISH_256KEY = 10,
};
/*
* PGP compression algorithm identifiers [RFC4880: 9.3]
*/
enum pgp_compr_algo {
PGP_COMPR_UNCOMPRESSED = 0,
PGP_COMPR_ZIP = 1,
PGP_COMPR_ZLIB = 2,
PGP_COMPR_BZIP2 = 3,
};
/*
* PGP hash algorithm identifiers [RFC4880: 9.4]
*/
enum pgp_hash_algo {
PGP_HASH_MD5 = 1,
PGP_HASH_SHA1 = 2,
PGP_HASH_RIPE_MD_160 = 3,
PGP_HASH_SHA256 = 8,
PGP_HASH_SHA384 = 9,
PGP_HASH_SHA512 = 10,
PGP_HASH_SHA224 = 11,
PGP_HASH__LAST
};
/*
* PGP packet type tags [RFC4880: 4.3].
*/
enum pgp_packet_tag {
PGP_PKT_RESERVED = 0,
PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1,
PGP_PKT_SIGNATURE = 2,
PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3,
PGP_PKT_ONEPASS_SIGNATURE = 4,
PGP_PKT_SECRET_KEY = 5,
PGP_PKT_PUBLIC_KEY = 6,
PGP_PKT_SECRET_SUBKEY = 7,
PGP_PKT_COMPRESSED_DATA = 8,
PGP_PKT_SYM_ENC_DATA = 9,
PGP_PKT_MARKER = 10,
PGP_PKT_LITERAL_DATA = 11,
PGP_PKT_TRUST = 12,
PGP_PKT_USER_ID = 13,
PGP_PKT_PUBLIC_SUBKEY = 14,
PGP_PKT_USER_ATTRIBUTE = 17,
PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18,
PGP_PKT_MODIFY_DETECT_CODE = 19,
PGP_PKT_PRIVATE_0 = 60,
PGP_PKT_PRIVATE_3 = 63,
PGP_PKT__HIGHEST = 63
};
/*
* Signature (tag 2) packet [RFC4880: 5.2].
*/
enum pgp_signature_version {
PGP_SIG_VERSION_3 = 3,
PGP_SIG_VERSION_4 = 4,
};
enum pgp_signature_type {
PGP_SIG_BINARY_DOCUMENT_SIG = 0x00,
PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01,
PGP_SIG_STANDALONE_SIG = 0x02,
PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10,
PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11,
PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12,
PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13,
PGP_SIG_SUBKEY_BINDING_SIG = 0x18,
PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19,
PGP_SIG_DIRECTLY_ON_KEY = 0x1F,
PGP_SIG_KEY_REVOCATION_SIG = 0x20,
PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28,
PGP_SIG_CERT_REVOCATION_SIG = 0x30,
PGP_SIG_TIMESTAMP_SIG = 0x40,
PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50,
};
struct pgp_signature_v3_packet {
enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */
u8 length_of_hashed; /* == 5 */
struct {
enum pgp_signature_type signature_type : 8;
struct pgp_time creation_time;
} __attribute__((packed)) hashed;
struct pgp_key_ID issuer;
enum pgp_pubkey_algo pubkey_algo : 8;
enum pgp_hash_algo hash_algo : 8;
} __attribute__((packed));
struct pgp_signature_v4_packet {
enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */
enum pgp_signature_type signature_type : 8;
enum pgp_pubkey_algo pubkey_algo : 8;
enum pgp_hash_algo hash_algo : 8;
} __attribute__((packed));
extern enum hash_algo pgp_algo_mapping[PGP_HASH__LAST];
int pgp_get_signature_data(const u8 *signature, size_t signature_len,
u8 **data, size_t *data_len);
int pgp_get_digest_algo(const u8 *data, size_t datalen, u16 *algo);
int sign_digest_list(char *path, char *key_name);
int dearmor_gpg(char *path);
int get_default_key(char *outdir, char *key_path, char *signed_data);
#endif /* _PGP_H */
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: rpm.h
* Header of rpm.c.
*/
#ifndef _RPM_H
#define _RPM_H
#include <rpm/rpmlib.h>
#include <rpm/header.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmlog.h>
#include "metadata.h"
#include "compact_list.h"
void get_rpm_path(Header rpm, char *outdir, char *output_path,
enum digest_data_sub_types output_fmt);
int ima_parse_rpm(loff_t size, void *buf, u16 data_algo, void *ctx,
callback_func func);
int digest_list_from_rpmdb(char *outdir, char *metadata_path,
enum digest_data_sub_types output_fmt,
int sign, char *gpg_key_name);
int digest_lists_from_rpmpkg(char *outdir, char *metadata_path,
char *package_path,
enum digest_data_sub_types output_fmt,
int sign, char *gpg_key_name);
#endif /* _RPM_H */
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: securityfs.h
* Header of securityfs.c.
*/
#ifndef _SECURITYFS_H
#define _SECURITYFS_H
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include "kernel_ima.h"
#define SYSFS_PATH "/sys"
#define SECURITYFS_PATH SYSFS_PATH "/kernel/security"
#define IMA_SECURITYFS_PATH SECURITYFS_PATH "/ima"
#define IMA_DIGEST_LIST_DATA_PATH IMA_SECURITYFS_PATH "/digest_list_data"
#define IMA_DIGEST_LIST_METADATA_PATH IMA_SECURITYFS_PATH \
"/digest_list_metadata"
#define MOUNT_FLAGS MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME
enum securityfs_files {DIGEST_LIST_METADATA, DIGEST_LIST_DATA};
extern int ima_fd;
extern int digests;
extern int sent_digests;
extern int digest_lists;
enum actions {ACTION_RESET, ACTION_ADD, ACTION_FLUSH};
int ima_add_digest_data_entry(u8 *digest, u16 digest_algo, u8 flags, u16 type,
enum actions action);
int ima_flush_digest_list_buffer(void);
int ima_init_upload(enum securityfs_files id);
void ima_end_upload(void);
int ima_upload_metadata(void *buf, loff_t size);
#endif /* _SECURITYFS_H */
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: xattr.h
* Header of xattr.c.
*/
#ifndef _XATTR_H
#define _XATTR_H
#include "lib.h"
int write_ima_xattr(int dirfd, char *path, u8 *keyid, size_t keyid_len,
u8 *sig, size_t sig_len, enum hash_algo algo);
int read_ima_xattr(int dirfd, char *path, u8 **buf,
u8 **keyid, size_t *keyid_len,
u8 **sig, size_t *sig_len, enum hash_algo *algo);
#endif /*_XATTR_H*/
pkgdracutdir = $(prefix)/lib/dracut/modules.d/98digestlist
dracut_modulesdir=$(prefix)/lib/dracut/modules.d/98digestlist
dracut_modules_SCRIPTS=module-setup.sh
pkgdracut_SCRIPTS = module-setup.sh
dracut_confdir=$(sysconfdir)/dracut.conf.d
dracut_conf_DATA=digestlist.conf
do_strip=no
add_dracutmodules+=" digestlist"
......@@ -11,7 +11,25 @@ depends() {
}
install() {
mkdir -m 0755 -p ${initdir}/etc/ima
cp /usr/bin/upload_digest_lists ${initdir}/usr/bin/upload_digest_lists
cp -a /etc/ima/digest_lists ${initdir}/etc/ima
if [ ! -e /etc/ima/digest_lists ]; then
return 0
fi
if [ "$(find /etc/ima/digest_lists)" = "/etc/ima/digest_lists" ]; then
return 0
fi
inst_dir /etc/ima/digest_lists
inst_multiple /etc/ima/digest_lists/*
inst_binary upload_digest_lists
inst_libdir_file "digestlist/libparser-*.so"
libc=$(realpath $(ldd /usr/bin/upload_digest_lists | grep libc.so | \
awk '{print $3}'))
cp -a $libc ${initdir}${libc}
libdl=$(realpath $(ldd /usr/bin/upload_digest_lists | grep libdl | \
awk '{print $3}'))
cp -a $libdl ${initdir}${libdl}
ld=$(realpath $(ldd /usr/bin/upload_digest_lists | grep ld-linux | \
awk '{print $1}'))
cp -a $ld ${initdir}${ld}
}
......@@ -4,21 +4,39 @@ PREREQ="udev"
prereqs()
{
echo "$PREREQ"
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_file binary /usr/bin/upload_digest_lists
for file in $(find /etc/ima/digest_lists -type f); do
for file in $(ls -d /etc/ima/digest_lists/*); do
copy_file binary $file
done
libdir=$(dirname $(ldconfig -p| awk '{ if ($(NF-1) == "=>")
lib=$NF; if (lib != "") exit } END { print lib }'))
for file in $(ls -d $libdir/digestlist/libparser-*.so); do
copy_file binary $file
done
libc=$(realpath $(ldd /usr/bin/upload_digest_lists | grep libc.so | \
awk '{print $3}'))
cp -a $libc ${DESTDIR}${libc}
libdl=$(realpath $(ldd /usr/bin/upload_digest_lists | grep libdl | \
awk '{print $3}'))
cp -a $libdl ${DESTDIR}${libdl}
ld=$(realpath $(ldd /usr/bin/upload_digest_lists | grep ld-linux | \
awk '{print $1}'))
cp -a $ld ${DESTDIR}${ld}
mkdir ${DESTDIR}/sys
lib_LTLIBRARIES = libdigestlists.la libparserstatic.la
lib_LTLIBRARIES=libdigestlist-base.la libdigestlist-gen.la
libdigestlists_la_LIBADD = -lcrypto -lrpm -lrpmio -lcurl
libdigestlist_base_la_CFLAGS=-I$(top_srcdir)/include
libdigestlist_base_la_LIBADD=-ldl
libdigestlist_base_la_SOURCES=kernel_lib.c lib.c compact_list.c xattr.c
libdigestlists_la_CFLAGS = -I$(top_srcdir)/include -DCRYPTO
if CMOCKA
noinst_LTLIBRARIES=libdigestlist-base-test.la
libdigestlist_base_test_la_CFLAGS=${libdigestlist_base_la_CFLAGS} -DUNIT_TESTING
libdigestlist_base_test_la_LIBADD=${libdigestlist_base_la_LIBADD} -lcmocka
libdigestlist_base_test_la_SOURCES=${libdigestlist_base_la_SOURCES}
endif
libdigestlists_la_SOURCES = kernel_lib.c \
kernel_ima.c \
metadata.c \
lib.c \
securityfs.c \
pgp.c \
parsers/rpm.c \
parsers/deb.c \
generators/compact_list.c \
generators/rpm.c \
generators/deb.c
libparserstatic_la_CFLAGS = -I$(top_srcdir)/include
libparserstatic_la_LDFLAGS = -all-static -lc -lz
libparserstatic_la_SOURCES = kernel_lib.c \
kernel_ima.c \
lib.c \
securityfs.c \
pgp.c \
parsers/rpm.c \
parsers/deb.c
if CRYPTO
libdigestlist_gen_la_CFLAGS=${libdigestlist_base_la_CFLAGS}
libdigestlist_gen_la_LIBADD=-lcrypto
libdigestlist_gen_la_SOURCES=crypto.c
endif
/*
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: compact_list.c
* Writes compact digest lists.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <dirent.h>
#include <sys/mman.h>
#include "compact_list.h"
#include "lib.h"
char *compact_types_str[COMPACT__LAST] = {
[COMPACT_KEY] = "key",
[COMPACT_PARSER] = "parser",
[COMPACT_FILE] = "file",
};
char *compact_modifiers_str[COMPACT_MOD__LAST] = {
[COMPACT_MOD_IMMUTABLE] = "immutable",
};
struct list_struct *compact_list_init(struct list_head *head,
enum compact_types type, u16 modifiers,
enum hash_algo algo)
{
struct list_struct *list;
list_for_each_entry(list, head, list) {
if (list->hdr->type == type &&
list->hdr->modifiers == modifiers &&
list->hdr->algo == algo)
return list;
}
list = malloc(sizeof(*list));
if (!list)
return list;
list->hdr = mmap(NULL, COMPACT_LIST_SIZE_MAX, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (list->hdr == MAP_FAILED) {
printf("Cannot allocate buffer\n");
return NULL;
}
list->hdr->version = 1;
list->hdr->type = type;
list->hdr->modifiers = modifiers;
list->hdr->algo = algo;
list->hdr->count = 0;
list->hdr->datalen = 0;
list_add_tail(&list->list, head);
return list;
}
int compact_list_add_digest(int fd, struct list_struct *list, u8 *digest)
{
struct compact_list_hdr *hdr = list->hdr;
int digest_len = hash_digest_size[hdr->algo];
void *ptr;
ptr = (void *)hdr + sizeof(*hdr) + hdr->datalen;
memcpy(ptr, digest, digest_len);
hdr->datalen += digest_len;
hdr->count++;
if (hdr->datalen + digest_len < COMPACT_LIST_SIZE_MAX)
return 0;
return compact_list_upload(fd, list);
}
int compact_list_upload(int fd, struct list_struct *list)
{
struct compact_list_hdr *hdr = list->hdr;
struct compact_list_hdr h = { .version = hdr->version,
.type = hdr->type,
.modifiers = hdr->modifiers,
.algo = hdr->algo,
.count = 0,
.datalen = 0 };
u32 datalen;
int ret;
if (fd < 0)
return 0;
hdr = list->hdr;
datalen = hdr->datalen;
if (!datalen)
return 0;
if (ima_canonical_fmt) {
hdr->type = cpu_to_le16(hdr->type);
hdr->modifiers = cpu_to_le16(hdr->modifiers);
hdr->algo = cpu_to_le16(hdr->algo);
hdr->count = cpu_to_le32(hdr->count);
hdr->datalen = cpu_to_le32(hdr->datalen);
}
ret = write_check(fd, (void *)hdr, sizeof(*hdr) + datalen);
memcpy(hdr, &h, sizeof(h));
return ret;
}
int compact_list_flush_all(int fd, struct list_head *head)
{
struct list_struct *p, *q;
int ret = 0;
if (fd < 0)
return 0;
list_for_each_entry_safe(p, q, head, list) {
if (!ret)
ret = compact_list_upload(fd, p);
munmap(p->hdr, COMPACT_LIST_SIZE_MAX);
list_del(&p->list);
free(p);
}
return ret;
}
int gen_filename_prefix(char *filename, int filename_len, int pos,
const char *format, enum compact_types type)
{
return snprintf(filename, filename_len, "%d-%s_list-%s-", pos,
compact_types_str[type], format);
}
static int filter_lists_common(const struct dirent *file,
enum compact_types type)
{
const char *filename = file->d_name;
char id_str[NAME_MAX + 1];
unsigned long pos;
char *ptr;
pos = strtoul(filename, &ptr, 10);
if (pos < 0)
return 0;
snprintf(id_str, sizeof(id_str), "-%s_list-", compact_types_str[type]);
if (strncmp(ptr, id_str, strlen(id_str)))
return 0;
if (!strncmp(filename + strlen(filename) - 4, ".sig", 4))
return 0;
return 1;
}
int filter_key_lists(const struct dirent *file)
{
return filter_lists_common(file, COMPACT_KEY);
}
int filter_parser_lists(const struct dirent *file)
{
return filter_lists_common(file, COMPACT_PARSER);
}
int filter_parser_list_symlink(const struct dirent *file)
{
if (file->d_type == DT_LNK &&
!strncmp(file->d_name, "compact-", 8))
return 1;
return 0;
}
int filter_file_lists(const struct dirent *file)
{
return filter_lists_common(file, COMPACT_FILE);
}
filter_lists filter[COMPACT__LAST] = {
[COMPACT_KEY] = filter_key_lists,
[COMPACT_PARSER] = filter_parser_lists,
[COMPACT_FILE] = filter_file_lists,
};
int get_digest_lists(int dirfd, enum compact_types type, struct list_head *head)
{
struct dirent **digest_lists;
int ret = 0, i, n;
n = scandirat(dirfd, ".", &digest_lists, filter[type], compare_lists);
if (n == -1) {
printf("Unable to access digest lists\n");
return -EACCES;
}
for (i = 0; i < n; i++) {
if (!ret)
ret = add_path_struct(digest_lists[i]->d_name, head);
free(digest_lists[i]);
}
free(digest_lists);
return ret;
}
int compare_lists(const struct dirent **e1, const struct dirent **e2)
{
unsigned long v1 = strtoul((*e1)->d_name, NULL, 10);
unsigned long v2 = strtoul((*e2)->d_name, NULL, 10);
return v1 - v2;
}
/*
* Copyright (C) 2011 Nokia Corporation
* Copyright (C) 2011,2012,2013 Intel Corporation
* Copyright (C) 2013,2014 Samsung Electronics
* Copyright (C) 2017-2019 Huawei Technologies Duesseldorf GmbH
*
* Authors:
* Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
* <dmitry.kasatkin@intel.com>
* <d.kasatkin@samsung.com>
* Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: crypto.c
* Calculate file digest.
*/
#include "crypto.h"
#include "xattr.h"
static int calc_digest(u8 *digest, void *data, int len, enum hash_algo algo)
{
EVP_MD_CTX *mdctx;
const EVP_MD *md;
int ret = -EINVAL;
OpenSSL_add_all_algorithms();
md = EVP_get_digestbyname(hash_algo_name[algo]);
if (!md)
goto out;
mdctx = EVP_MD_CTX_create();
if (!mdctx)
goto out;
if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
goto out_mdctx;
if (EVP_DigestUpdate(mdctx, data, len) != 1)
goto out_mdctx;
if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1)
goto out_mdctx;
ret = 0;
out_mdctx:
EVP_MD_CTX_destroy(mdctx);
out:
EVP_cleanup();
return ret;
}
int calc_file_digest(u8 *digest, int dirfd, char *path, enum hash_algo algo)
{
void *data;
struct stat st;
int fd, ret = 0;
if (dirfd >= 0) {
if (fstatat(dirfd, path, &st, 0) == -1)
return -EACCES;
fd = openat(dirfd, path, O_RDONLY);
} else {
if (stat(path, &st) == -1)
return -EACCES;
fd = open(path, O_RDONLY);
}
if (fd < 0)
return -EACCES;
data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (data == MAP_FAILED) {
ret = -ENOMEM;
goto out;
}
ret = calc_digest(digest, data, st.st_size, algo);
out:
if (data != MAP_FAILED)
munmap(data, st.st_size);
close(fd);
return ret;
}
struct RSA_ASN1_template {
const uint8_t *data;
size_t size;
};
/*
* Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
*/
static const uint8_t RSA_digest_info_SHA1[] = {
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
0x2B, 0x0E, 0x03, 0x02, 0x1A,
0x05, 0x00, 0x04, 0x14
};
static const uint8_t RSA_digest_info_SHA256[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
0x05, 0x00, 0x04, 0x20
};
static const uint8_t RSA_digest_info_SHA512[] = {
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
0x05, 0x00, 0x04, 0x40
};
const struct RSA_ASN1_template RSA_ASN1_templates[HASH_ALGO__LAST] = {
#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
[HASH_ALGO_SHA1] = _(SHA1),
[HASH_ALGO_SHA256] = _(SHA256),
[HASH_ALGO_SHA512] = _(SHA512),
#undef _
};
static void free_key(struct key_struct *k)
{
RSA_free(k->key);
free(k);
}
void free_keys(struct list_head *head)
{
struct key_struct *cur, *tmp;
list_for_each_entry_safe(cur, tmp, head, list) {
list_del(&cur->list);
free_key(cur);
}
}
struct key_struct *new_key(struct list_head *head, int dirfd, char *key_path,
char *keypass, bool private)
{
u8 digest[SHA512_DIGEST_SIZE], *pkey = NULL;
struct key_struct *new = NULL;
EVP_PKEY *public_key = NULL;
X509 *crt = NULL;
FILE *fp;
int ret = -EINVAL, fd, pkey_len;
OpenSSL_add_all_algorithms();
if (dirfd != -1)
fd = openat(dirfd, key_path, O_RDONLY);
else
fd = open(key_path, O_RDONLY);
if (fd < 0)
goto out;
fp = fdopen(fd, "r");
if (!fp) {
ret = -EACCES;
close(fd);
goto out;
}
new = calloc(1, sizeof(*new));
if (!new) {
ret = -ENOMEM;
goto out_fp;
}
if (private)
new->key = PEM_read_RSAPrivateKey(fp, NULL, NULL,
(void *)keypass);
else {
crt = d2i_X509_fp(fp, NULL);
if (!crt) {
printf("d2i_X509_fp() failed\n");
goto out_fp;
}
public_key = X509_extract_key(crt);
if (!public_key) {
printf("X509_extract_key() failed\n");
goto out_fp;
}
new->key = EVP_PKEY_get1_RSA(public_key);
}
if (!new->key) {
ret = -ENOENT;
goto out_key;
}
pkey_len = i2d_RSAPublicKey(new->key, &pkey);
if (pkey_len < 0) {
printf("Cannot extract public key\n");
goto out_key;
}
ret = calc_digest(digest, pkey, pkey_len, HASH_ALGO_SHA1);
memcpy(new->keyid, digest + 16, 4);
list_add_tail(&new->list, head);
free(pkey);
out_key:
if (ret < 0) {
free_key(new);
new = NULL;
}
out_fp:
if (public_key)
EVP_PKEY_free(public_key);
if (crt)
X509_free(crt);
fclose(fp);
out:
EVP_cleanup();
return new;
}
struct key_struct *lookup_key(struct list_head *head, int dirfd, char *key_path,
u8 *keyid)
{
struct key_struct *cur = NULL;
list_for_each_entry(cur, head, list)
if (!memcmp(cur->keyid, keyid, sizeof(cur->keyid)))
return cur;
if (key_path)
return cur;
return new_key(head, dirfd, key_path, NULL, false);
}
static int sign_file(int dirfd, char *filename, char *key_path, char *keypass,
enum hash_algo algo)
{
u8 digest[SHA512_DIGEST_SIZE], sig[MAX_SIGNATURE_SIZE];
const struct RSA_ASN1_template *asn1;
struct key_struct *k;
LIST_HEAD(key_head);
u8 *buf;
int ret = 0, digest_len, sig_len;
k = new_key(&key_head, -1, key_path, keypass, true);
if (!k)
return -ENOENT;
ret = calc_file_digest(digest, dirfd, filename, algo);
if (ret < 0)
goto out_key;
digest_len = hash_digest_size[algo];
asn1 = &RSA_ASN1_templates[algo];
if (!asn1) {
printf("Algorithm %s not supported\n", hash_algo_name[algo]);
goto out_key;
}
buf = malloc(digest_len + asn1->size);
if (!buf) {
ret = -ENOMEM;
goto out_key;
}
memcpy(buf, asn1->data, asn1->size);
memcpy(buf + asn1->size, digest, digest_len);
sig_len = RSA_private_encrypt(digest_len + asn1->size, buf, sig, k->key,
RSA_PKCS1_PADDING);
if (sig_len < 0) {
printf("RSA_private_encrypt() failed: %d\n", sig_len);
goto out_buf;
}
ret = write_ima_xattr(dirfd, filename, k->keyid, sizeof(k->keyid),
sig, sig_len, algo);
out_buf:
free(buf);
out_key:
free_keys(&key_head);
return ret;
}
int sign_files(int dirfd, struct list_head *head, char *key_path,
char *keypass, enum hash_algo algo)
{
struct path_struct *cur;
int ret = 0;
list_for_each_entry(cur, head, list) {
ret = sign_file(dirfd, cur->path, key_path, keypass, algo);
if (ret < 0) {
printf("Cannot sign %s\n", cur->path);
return ret;
}
}
return ret;
}
int verify_file(struct list_head *head, int dirfd, char *filename)
{
u8 *buf, *keyid, *sig;
u8 digest[SHA512_DIGEST_SIZE], out[MAX_SIGNATURE_SIZE];
enum hash_algo algo;
struct key_struct *k;
const struct RSA_ASN1_template *asn1;
size_t keyid_len, sig_len, len;
int ret;
ret = read_ima_xattr(dirfd, filename, &buf, &keyid, &keyid_len,
&sig, &sig_len, &algo);
if (ret < 0) {
printf("Cannot read security.ima xattr: %d\n", ret);
return ret;
}
ret = calc_file_digest(digest, dirfd, filename, algo);
if (ret < 0)
goto out;
k = lookup_key(head, dirfd, NULL, keyid);
if (!k) {
printf("No key found for id %d\n", be32_to_cpu(keyid));
ret = -ENOENT;
goto out;
}
ret = RSA_public_decrypt(sig_len, sig, out, k->key, RSA_PKCS1_PADDING);
if (ret < 0) {
printf("RSA_public_decrypt() failed: %d\n", ret);
goto out;
}
len = ret;
asn1 = &RSA_ASN1_templates[algo];
if (len < asn1->size || memcmp(out, asn1->data, asn1->size)) {
printf("Verification failed: %d (asn1 mismatch)\n", ret);
goto out;
}
len -= asn1->size;
if (memcmp(out + asn1->size, digest, hash_digest_size[algo])) {
printf("Verification failed (digest mismatch)\n");
ret = -EINVAL;
}
ret = 0;
out:
free(buf);
return ret;
}
/*
* Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: compact_list.c
* Writes compact digest lists.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "compact_list.h"
int compact_list_from_rpm(Header rpm, char *outdir, char *output_path)
{
u16 digest_algo;
u32 rpm_digest_algo, default_rpmdigestalgo = PGP_HASH_MD5;
rpmtd rpm_digestalgo_td = rpmtdNew();
rpm_count_t data_count;
rpmfi fi;
u8 *output;
int ret, fd, datalen = 0, i, digest_len;
struct compact_list_hdr hdr = {0, 0, 0, 0};
get_rpm_path(rpm, outdir, output_path, DATA_SUB_TYPE_COMPACT_LIST);
if (strstr(output_path, "gpg-pubkey") != NULL)
return -ENOENT;
ret = headerGet(rpm, RPMTAG_FILEDIGESTALGO, rpm_digestalgo_td, 0);
if (ret < 0) {
rpm_digest_algo = default_rpmdigestalgo;
return -EINVAL;
}
rpm_digest_algo = rpmtdGetNumber(rpm_digestalgo_td);
digest_algo = pgp_algo_mapping[rpm_digest_algo];
digest_len = hash_digest_size[digest_algo];
rpmtdReset(rpm_digestalgo_td);
fi = rpmfiNew(NULL, rpm, RPMTAG_FILEDIGESTS, RPMFI_KEEPHEADER);
if (fi == NULL)
return -EINVAL;
data_count = rpmfiFC(fi);
output = malloc(digest_len * data_count);
if (output == NULL)
return -ENOMEM;
for (i = 0; i < data_count; i++) {
if (rpmfiNext(fi) == -1)
break;
if (strlen(rpmfiFN(fi)) == 0)
continue;
hex2bin(output + datalen, rpmfiFN(fi), digest_len);
hdr.count++;
datalen += digest_len;
}
fi = rpmfiFree(fi);
hdr.entry_id = COMPACT_DIGEST;
hdr.algo = digest_algo;
hdr.datalen = datalen;
if (ima_canonical_fmt) {
hdr.entry_id = cpu_to_le16(hdr.entry_id);
hdr.algo = cpu_to_le16(hdr.algo);
hdr.count = cpu_to_le32(hdr.count);
hdr.datalen = cpu_to_le32(hdr.datalen);
}
fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd < 0)
goto out;
ret = write_check(fd, &hdr, sizeof(hdr));
if (ret < 0)
goto out_close;
ret = write_check(fd, output, hdr.datalen);
out_close:
close(fd);
out:
free(output);
return ret;
}
static int compact_list_from_digest_list_ascii(char *input_path, char *outdir,
char *output_path,
int is_mutable)
{
const char *algo_name = hash_algo_name[ima_hash_algo];
int algo_prefix_len = strlen(algo_name) + 1;
int digest_len = hash_digest_size[ima_hash_algo];
u8 digest[digest_len];
char *data, *datap, *line, *input_basename;
int datalen = 0;
struct stat st;
int ret = 0, inputfd = -1, outputfd;
struct compact_list_hdr hdr = {0, 0, 0};
if (stat(input_path, &st) != 0)
return -EACCES;
if (st.st_size == 0)
return -EINVAL;
inputfd = open(input_path, O_RDONLY);
if (inputfd < 0)
return -EACCES;
data = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, inputfd, 0);
if (data == MAP_FAILED) {
ret = -ENOMEM;
goto out;
}
datap = data;
input_basename = rindex(input_path, '/');
if (input_basename == NULL)
input_basename = input_path;
else
input_basename += 1;
snprintf(output_path, MAX_PATH_LENGTH, "%s/compact-%s",
outdir, input_basename);
outputfd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (outputfd < 0) {
printf("Unable to write %s\n", output_path);
ret = -EACCES;
goto out;
}
lseek(outputfd, sizeof(hdr), SEEK_SET);
while (true) {
line = strsep(&datap, "\n");
if (line == NULL)
break;
if (strlen(line) < algo_prefix_len + digest_len * 2)
continue;
if (strncmp(line, algo_name, algo_prefix_len - 1)) {
printf("Digest algorithm mismatch, skipping: %s\n",
line);
continue;
}
hex2bin(digest, line + algo_prefix_len, digest_len);
ret = write_check(outputfd, digest, digest_len);
if (ret < 0)
goto out_outputfd;
hdr.count++;
datalen += digest_len;
}
hdr.entry_id = is_mutable ? COMPACT_DIGEST_MUTABLE : COMPACT_DIGEST;
hdr.algo = ima_hash_algo;
hdr.datalen = datalen;
if (ima_canonical_fmt) {
hdr.entry_id = cpu_to_le16(hdr.entry_id);
hdr.algo = cpu_to_le16(ima_hash_algo);
hdr.count = cpu_to_le32(hdr.count);
hdr.datalen = cpu_to_le32(datalen);
}
ret = lseek(outputfd, 0, SEEK_SET);
if (ret < 0) {
pr_err("lseek() error, ret: %d\n", ret);
goto out_outputfd;
}
ret = write_check(outputfd, &hdr, sizeof(hdr));
out_outputfd:
close(outputfd);
out:
close(inputfd);
if (data)
munmap(data, st.st_size);
return ret;
}
int digest_list_from_ascii(char *outdir, char *metadata_path,
char *input_path,
enum digest_data_sub_types output_fmt,
int is_mutable, int sign, char *gpg_key_name)
{
char digest_list_path[MAX_PATH_LENGTH];
int ret;
ret = compact_list_from_digest_list_ascii(input_path, outdir,
digest_list_path,
is_mutable);
if (ret < 0)
return ret;
if (sign) {
ret = sign_digest_list(digest_list_path, gpg_key_name);
if (ret < 0)
return ret;
}
return write_digests_and_metadata(outdir, metadata_path,
digest_list_path, output_fmt,
sign);
}
/*
* Copyright (C) 2018 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: deb.c
* Writes DEB digest lists.
*/
#include <stdio.h>
#include <fcntl.h>
#include <curl/curl.h>
#include "kernel_lib.h"
#include "deb.h"
#include "pgp.h"
static int download_file(char *url, char *path)
{
FILE *f;
int ret = -EINVAL;
CURL *curl;
CURLcode res;
f = fopen(path, "wb");
if (!f) {
pr_err("Failed to create %s\n", path);
return ret;
}
pr_info("Downloading %s\n", url);
curl = curl_easy_init();
if (!curl)
goto out;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)f);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
pr_err("curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
goto out;
}
ret = 0;
out:
curl_easy_cleanup(curl);
fclose(f);
return ret;
}
static void replace_slash(char *path)
{
char *path_ptr = path;
while ((path_ptr = strchr(path_ptr, '/')))
*path_ptr++ = '-';
}
static int download_deb_package(char *distro, char *repo_url, char *outdir,
char *package_path, char *digest_list_path,
u8 *digest)
{
char url[MAX_PATH_LENGTH + 1];
char *file_ptr = strrchr(package_path, '/');
int ret;
snprintf(url, sizeof(url), "%s/%s", repo_url, package_path);
snprintf(digest_list_path, MAX_PATH_LENGTH, "%s/%s",
outdir, file_ptr);
ret = check_digest(NULL, 0, digest_list_path, ima_hash_algo, digest);
if (!ret)
return ret;
return download_file(url, digest_list_path);
}
static int download_deb_packages_gz(char *distro, char *repo_url, char *outdir,
char *packages_gz_path,
char *digest_list_path)
{
char url[MAX_PATH_LENGTH];
snprintf(url, sizeof(url), "%s/dists/%s/%s", repo_url, distro,
packages_gz_path);
replace_slash(packages_gz_path);
snprintf(digest_list_path, MAX_PATH_LENGTH, "%s/%s-%s",
outdir, distro, packages_gz_path);
return download_file(url, digest_list_path);
}
static int download_deb_release(char *distro, char *repo_url, char *outdir,
char *digest_list_path)
{
char url[MAX_PATH_LENGTH];
char path[MAX_PATH_LENGTH];
int ret;
snprintf(url, sizeof(url), "%s/dists/%s/Release", repo_url, distro);
snprintf(digest_list_path, MAX_PATH_LENGTH, "%s/%s-Release",
outdir, distro);
ret = download_file(url, digest_list_path);
if (ret < 0)
return ret;
snprintf(url, sizeof(url), "%s/dists/%s/Release.gpg", repo_url, distro);
snprintf(path, sizeof(path), "%s/%s-Release.asc", outdir, distro);
ret = download_file(url, path);
if (!ret)
ret = dearmor_gpg(path);
return ret;
}
struct pkg_entry {
char *pkg;
int digests_added;
struct pkg_entry *next;
};
struct deb_ctx {
char *distro;
char *repo_url;
char *outdir;
char *metadata_path;
char *arch;
char path[MAX_PATH_LENGTH];
int check_packages;
int packages_found;
struct pkg_entry *packages;
};
static int deb_packages_gz_callback(void *ctx, char *line)
{
char digest_list_path[MAX_PATH_LENGTH], *file_ptr, *digest_ptr;
const char *filename_str = "Filename: ";
int ret = 0, filename_str_len = strlen(filename_str);
struct deb_ctx *c = (struct deb_ctx *)ctx;
struct pkg_entry *entry = c->packages;
const char *algo_name = hash_algo_name[ima_hash_algo];
u8 digest[IMA_MAX_DIGEST_SIZE];
if (!strncmp(line, filename_str, filename_str_len)) {
snprintf(c->path, sizeof(c->path), "%s",
line + filename_str_len);
return 0;
}
if (strncasecmp(line, algo_name, strlen(algo_name)))
return 0;
file_ptr = strrchr(c->path, '/') + 1;
digest_ptr = strchr(line, ':') + 2;
hex2bin(digest, digest_ptr, hash_digest_size[ima_hash_algo]);
while (entry) {
if (strcmp(file_ptr, entry->pkg)) {
entry = entry->next;
continue;
}
if (entry->digests_added) {
entry = entry->next;
continue;
}
if (c->check_packages) {
c->packages_found = 1;
return 0;
}
ret = download_deb_package(c->distro, c->repo_url, c->outdir,
c->path, digest_list_path, digest);
if (ret < 0)
return ret;
ret = write_digests_and_metadata(c->outdir, c->metadata_path,
digest_list_path,
DATA_SUB_TYPE_DEB_PACKAGE, 0);
if (!ret)
entry->digests_added = 1;
break;
}
return ret;
}
static int deb_release_callback(void *ctx, char *line)
{
struct deb_ctx *c = (struct deb_ctx *)ctx;
char digest_list_path[MAX_PATH_LENGTH];
const char *packages_gz_str = "Packages.gz";
int l_gz = strlen(packages_gz_str);
char *size_ptr, *file_ptr;
loff_t len;
void *buf;
int ret, fd;
size_ptr = line + 1 + hash_digest_size[ima_hash_algo] * 2;
file_ptr = size_ptr + 18;
if (!strstr(file_ptr, c->arch))
return 0;
if (strncmp(file_ptr + strlen(file_ptr) - l_gz, packages_gz_str, l_gz))
return 0;
ret = download_deb_packages_gz(c->distro, c->repo_url, c->outdir,
file_ptr, digest_list_path);
if (ret < 0)
return ret;
fd = read_file_from_path(digest_list_path, &buf, &len);
if (fd < 0)
return fd;
c->check_packages = 1;
c->packages_found = 0;
ret = ima_parse_deb_packages_gz(len, buf, ima_hash_algo, ctx,
deb_packages_gz_callback);
if (ret < 0)
goto out;
if (!c->packages_found) {
unlink(digest_list_path);
goto out;
}
ret = write_digests_and_metadata(c->outdir, c->metadata_path,
digest_list_path,
DATA_SUB_TYPE_DEB_PACKAGES_GZ, 0);
if (ret < 0)
goto out;
c->check_packages = 0;
ret = ima_parse_deb_packages_gz(len, buf, ima_hash_algo, ctx,
deb_packages_gz_callback);
out:
munmap(buf, len);
close(fd);
return ret;
}
int digest_list_from_deb_mirror(char *outdir, char *metadata_path,
enum digest_data_sub_types output_fmt,
char *distro, char *repo_url)
{
char digest_list_path[MAX_PATH_LENGTH];
char *distro_suffix[] = {"", "-updates", "-security"};
char repo[MAX_PATH_LENGTH], pkg_filename[MAX_PATH_LENGTH];
struct deb_ctx ctx = {repo, repo_url, outdir,
metadata_path, "amd64", {0}, 0, 0};
struct pkg_entry *entry, *next_entry;
loff_t len;
void *buf;
char *line = NULL;
char *pkg_name_ptr, *pkg_ver_ptr, *pkg_arch_ptr, *pkg_epoch_end;
size_t line_len, cur_len;
int i, fd, ret = 0;
FILE *f = popen(DPKG_QUERY_CMD, "r");
if (f == NULL) {
pr_err("Unable to execute dpkg-query\n");
return -EPERM;
}
curl_global_init(CURL_GLOBAL_DEFAULT);
while ((cur_len = getline(&line, &line_len, f)) != -1) {
entry = malloc(sizeof(*entry));
if (!entry) {
ret = -ENOMEM;
goto out;
}
line[cur_len - 1] = '\0';
pkg_name_ptr = line;
pkg_ver_ptr = strchr(pkg_name_ptr, ' ');
*pkg_ver_ptr++ = '\0';
pkg_epoch_end = strchr(pkg_ver_ptr, ':');
if (pkg_epoch_end)
pkg_ver_ptr = pkg_epoch_end + 1;
pkg_arch_ptr = strchr(pkg_ver_ptr, ' ');
*pkg_arch_ptr++ = '\0';
snprintf(pkg_filename, sizeof(pkg_filename), "%s_%s_%s.deb",
pkg_name_ptr, pkg_ver_ptr, pkg_arch_ptr);
entry->pkg = strdup(pkg_filename);
if (!entry->pkg) {
free(entry);
goto out;
}
entry->digests_added = 0;
entry->next = ctx.packages;
ctx.packages = entry;
}
for (i = 0; i < ARRAY_SIZE(distro_suffix); i++) {
snprintf(repo, sizeof(repo), "%s%s", distro, distro_suffix[i]);
ret = download_deb_release(repo, repo_url, outdir,
digest_list_path);
if (ret < 0)
goto out;
ret = write_digests_and_metadata(outdir, metadata_path,
digest_list_path,
DATA_SUB_TYPE_DEB_RELEASE, 0);
if (ret < 0)
goto out;
fd = read_file_from_path(digest_list_path, &buf, &len);
if (fd < 0)
goto out;
ret = ima_parse_deb_release(len, buf, ima_hash_algo,
&ctx, deb_release_callback);
munmap(buf, len);
close(fd);
if (ret < 0)
goto out;
}
out:
pclose(f);
entry = ctx.packages;
while(entry) {
if (!entry->digests_added)
pr_err("Warning: digests not added for package %s\n",
entry->pkg);
next_entry = entry->next;
free(entry->pkg);
free(entry);
entry = next_entry;
}
curl_global_cleanup();
return 0;
}
/*
* Copyright (C) 2017,2018 Huawei Technologies Duesseldorf GmbH
*
* Author: Roberto Sassu <roberto.sassu@huawei.com>
*
* 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, version 2 of the
* License.
*
* File: kernel_ima.c
* Includes IMA functions.
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include "kernel_ima.h"
#include "rpm.h"
#include "deb.h"
int current, parser_task, ima_policy_flag;
int ima_digest_list_actions = IMA_MEASURE | IMA_APPRAISE;
int num_entry;
#ifdef __BIG_ENDIAN__
int ima_canonical_fmt = 1;
#else
int ima_canonical_fmt = 0;
#endif
int ima_get_buflen(int maxfields, struct ima_field_data *fields,
unsigned long *len_mask)
{
int len = 0, i;
for (i = 0; i < maxfields; i++) {
if (len_mask == NULL || !test_bit(i, len_mask))
len += sizeof(u32);
len += fields[i].len;
}
return len;
}
int ima_hash_algo = HASH_ALGO_SHA256;
int ima_hash_setup(char *str)
{
int i;
for (i = 0; i < HASH_ALGO__LAST; i++) {
if (strcmp(str, hash_algo_name[i]) == 0) {
ima_hash_algo = i;
break;
}
}
if (i == HASH_ALGO__LAST)
return -EINVAL;
return 0;
}
int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname)
{
void *bufp = bufstartp;
int i;
for (i = 0; i < maxfields; i++) {
if (len_mask == NULL || !test_bit(i, len_mask)) {
if (bufp > (bufendp - sizeof(u32)))
break;
fields[i].len = le32_to_cpu(*(u32 *)bufp);
bufp += sizeof(u32);
}
if (bufp > (bufendp - fields[i].len))
break;
fields[i].data = bufp;
bufp += fields[i].len;
}
if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
bufname, maxfields, i);
return -EINVAL;
}
if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
bufname, bufendp, bufp);
return -EINVAL;
}
if (curfields)
*curfields = i;
if (bufcurp)
*bufcurp = bufp;
return 0;
}
int ima_write_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname)
{
void *bufp = bufstartp;
int i;
for (i = 0; i < maxfields; i++) {
if (len_mask == NULL || !test_bit(i, len_mask)) {
u32 field_len = fields[i].len;
if (bufp > (bufendp - sizeof(u32)))
break;
field_len = cpu_to_le32(field_len);
memcpy(bufp, &field_len, sizeof(field_len));
bufp += sizeof(u32);
}
if (bufp > (bufendp - fields[i].len))
break;
memcpy(bufp, fields[i].data, fields[i].len);
bufp += fields[i].len;
}
if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
bufname, maxfields, i);
return -EINVAL;
}
if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
bufname, bufendp, bufp);
return -EINVAL;
}
if (curfields)
*curfields = i;
if (bufcurp)
*bufcurp = bufp;
return 0;
}
/***********************
* Compact list parser *
***********************/
static int ima_parse_compact_list(loff_t size, void *buf, u16 data_algo,
void *ctx, callback_func func)
{
void *bufp = buf, *bufendp = buf + size;
struct compact_list_hdr *hdr;
u8 flags = 0;
u16 type = DATA_TYPE_REG_FILE;
int ret, i, digest_len;
if (current != parser_task)
return -EPERM;
while (bufp < bufendp) {
if (bufp + sizeof(*hdr) > bufendp) {
pr_err("compact list, missing header\n");
return -EINVAL;
}
hdr = bufp;
if (ima_canonical_fmt) {
hdr->entry_id = le16_to_cpu(hdr->entry_id);
hdr->algo = le16_to_cpu(hdr->algo);
hdr->count = le32_to_cpu(hdr->count);
hdr->datalen = le32_to_cpu(hdr->datalen);
}
if (hdr->algo < 0 || hdr->algo >= HASH_ALGO__LAST)
return -EINVAL;
if (hdr->algo != ima_hash_algo)
flags |= DIGEST_FLAG_DIGEST_ALGO;
digest_len = hash_digest_size[hdr->algo];
switch (hdr->entry_id) {
case COMPACT_DIGEST:
case COMPACT_DIGEST_LIST:
flags |= DIGEST_FLAG_IMMUTABLE;
if (hdr->entry_id == COMPACT_DIGEST_LIST)
type = DATA_TYPE_DIGEST_LIST;
break;
case COMPACT_DIGEST_MUTABLE:
break;
default:
pr_err("compact list, invalid data type\n");
return -EINVAL;
}
bufp += sizeof(*hdr);
for (i = 0; i < hdr->count &&
bufp + digest_len <= bufendp; i++) {
ret = ima_add_digest_data_entry(bufp, hdr->algo,
flags, type,
ACTION_ADD);
if (ret < 0 && ret != -EEXIST)
return ret;
bufp += digest_len;
}
if (i != hdr->count ||
bufp != (void *)hdr + sizeof(*hdr) + hdr->datalen) {
pr_err("compact list, invalid data\n");
return -EINVAL;
}
}
return bufp - buf;
}
/************
* Callback *
************/
struct callback_ctx {
u16 data_algo;
enum digest_data_sub_types sub_type;
};
int upload_callback(void *ctx, char *line)
{
char *digest_str;
u8 digest[IMA_MAX_DIGEST_SIZE];
struct callback_ctx *c = (struct callback_ctx *)ctx;
const char *packages_gz_str = "Packages.gz";
const char *algo_name = hash_algo_name[c->data_algo];
int l_gz = strlen(packages_gz_str);
u8 flags = 0;
enum digest_data_types type = DATA_TYPE_REG_FILE;
switch (c->sub_type) {
case DATA_SUB_TYPE_DEB_PACKAGE:
digest_str = line;
break;
case DATA_SUB_TYPE_DEB_PACKAGES_GZ:
if (strncasecmp(line, algo_name, strlen(algo_name)))
return 0;
digest_str = strchr(line, ':') + 2;
flags = DIGEST_FLAG_IMMUTABLE;
type = DATA_TYPE_DIGEST_LIST;
break;
case DATA_SUB_TYPE_DEB_RELEASE:
if (strncmp(line + strlen(line) - l_gz, packages_gz_str, l_gz))
return 0;
digest_str = line + 1;
flags = DIGEST_FLAG_IMMUTABLE;
type = DATA_TYPE_DIGEST_LIST;
break;
default:
return 0;
}
hex2bin(digest, digest_str, hash_digest_size[c->data_algo]);
return ima_add_digest_data_entry(digest, c->data_algo, flags, type,
ACTION_ADD);
}
int (*parser_func[DATA_SUB_TYPE__LAST])(loff_t size, void *buf, u16 data_algo,
void *ctx, callback_func func) = {
[DATA_SUB_TYPE_COMPACT_LIST] = ima_parse_compact_list,
[DATA_SUB_TYPE_RPM] = ima_parse_rpm,
[DATA_SUB_TYPE_DEB_PACKAGE] = ima_parse_deb_package,
[DATA_SUB_TYPE_DEB_PACKAGES_GZ] = ima_parse_deb_packages_gz,
[DATA_SUB_TYPE_DEB_RELEASE] = ima_parse_deb_release,
};
/***************************
* Digest list data parser *
***************************/
static int ima_parse_digest_list_data(u16 data_algo, u16 digest_algo,
u16 sub_type, u8 *digest, char *path,
u32 data_length)
{
struct callback_ctx ctx = {data_algo, sub_type};
void *digest_list;
loff_t digest_list_size;
int ret, fd;
if (parse_metadata)
return 0;
fd = read_file_from_path(path, &digest_list, &digest_list_size);
if (fd < 0) {
pr_err("Unable to open file: %s (%d)\n", path, fd);
return fd;
}
ret = check_digest(digest_list, digest_list_size, NULL,
digest_algo, digest);
if (ret < 0) {
pr_err("Digest verification for %s failed\n", path);
goto out;
}
if (parser_func[sub_type] == NULL) {
pr_err("Parser for type %d not implemented\n", sub_type);
ret = -EINVAL;
goto out;
}
ret = parser_func[sub_type](data_length, digest_list, data_algo,
(void *)&ctx, upload_callback);
if (ret < 0) {
pr_err("Error parsing file: %s (%d)\n", path, ret);
goto out;
}
ret = ima_flush_digest_list_buffer();
out:
munmap(digest_list, digest_list_size);
close(fd);
return ret;
}
/*******************************
* Digest list metadata viewer *
*******************************/
#define SPACES 2
#define DIGEST_LEN 4
#define ID_LENGTH 6
static char *digest_metadata_fields_str[DATA__LAST] = {
[DATA_ALGO] = "DATA_ALGO",
[DATA_TYPE] = "TYPE",
[DATA_TYPE_EXT] = "TYPE_EXT",
[DATA_DIGEST_ALGO] = "DIGEST_ALGO",
[DATA_DIGEST] = "DIGEST",
[DATA_SIG_FMT] = "SIG_FMT",
[DATA_SIG] = "SIG",
[DATA_FILE_PATH] = "FILE_PATH",
[DATA_LENGTH] = "DATA_LENGTH",
};
static int digest_metadata_fields_str_len[DATA__LAST] = {
[DATA_ALGO] = 9,
[DATA_TYPE] = 11,
[DATA_TYPE_EXT] = 12,
[DATA_DIGEST_ALGO] = 11,
[DATA_DIGEST] = DIGEST_LEN * 2,
[DATA_SIG_FMT] = 7,
[DATA_SIG] = DIGEST_LEN * 2,
[DATA_FILE_PATH] = 30,
[DATA_LENGTH] = 11,
};
static char *digest_data_types_str[DATA_TYPE_REG_FILE + 1] = {
[DATA_TYPE_HEADER] = "header",
[DATA_TYPE_DIGEST_LIST] = "digest_list",
[DATA_TYPE_PARSER] = "parser",
[DATA_TYPE_REG_FILE] = "file",
[DATA_TYPE_KEY] = "key",
};
static char *digest_data_sub_types_str[DATA_SUB_TYPE__LAST] = {
[DATA_SUB_TYPE_COMPACT_LIST] = "compact",
[DATA_SUB_TYPE_RPM] = "rpm",
[DATA_SUB_TYPE_DEB_RELEASE] = "deb_release",
[DATA_SUB_TYPE_DEB_PACKAGES_GZ] = "deb_packages",
[DATA_SUB_TYPE_DEB_PACKAGE] = "deb_package",
};
static char *data_sig_formats_str[SIG_FMT_PKCS7 + 1] = {
[SIG_FMT_NONE] = "",
[SIG_FMT_IMA] = "ima",
[SIG_FMT_PGP] = "pgp",
[SIG_FMT_PKCS7] = "pkcs7",
};
static void print_metadata_header(void)
{
int i;
pr_info("%*s", ID_LENGTH, "ID");
for (i = 0; i < ARRAY_SIZE(digest_metadata_fields_str); i++) {
printf("%*s", SPACES, "");
pr_info("%*s", digest_metadata_fields_str_len[i],
digest_metadata_fields_str[i]);
}
pr_info("\n");
}
static void print_metadata(struct ima_field_data *entry_data)
{
enum digest_data_types data_type;
enum digest_data_sub_types sub_type;
enum data_sig_formats sig_fmt;
char *file_ptr;
int i, len;
pr_info("%*d", ID_LENGTH, num_entry++);
for (i = 0; i < ARRAY_SIZE(digest_metadata_fields_str); i++) {
printf("%*s", SPACES, "");
if (!entry_data[i].len) {
pr_info("%*s", digest_metadata_fields_str_len[i], "");
continue;
}
switch(i) {
case DATA_ALGO:
case DATA_DIGEST_ALGO:
pr_info("%*s", digest_metadata_fields_str_len[i],
hash_algo_name[*(u16 *)entry_data[i].data]);
break;
case DATA_TYPE:
data_type = *(u16 *)entry_data[i].data;
pr_info("%*s", digest_metadata_fields_str_len[i],
digest_data_types_str[data_type]);
break;
case DATA_TYPE_EXT:
data_type = *(u16 *)entry_data[DATA_TYPE].data;
if (data_type == DATA_TYPE_DIGEST_LIST) {
sub_type = *(u32 *)entry_data[i].data;
pr_info("%*s",
digest_metadata_fields_str_len[i],
digest_data_sub_types_str[sub_type]);
} else {
len = min(DIGEST_LEN, entry_data[i].len);
pr_info("%*s",
digest_metadata_fields_str_len[i] -
len * 2, "");
hexdump(entry_data[i].data, len);
}
break;
case DATA_DIGEST:
hexdump(entry_data[i].data, DIGEST_LEN);
break;
case DATA_SIG_FMT:
sig_fmt = *(u16 *)entry_data[i].data;
pr_info("%*s", digest_metadata_fields_str_len[i],
data_sig_formats_str[sig_fmt]);
break;
case DATA_SIG:
hexdump(entry_data[i].data, DIGEST_LEN);
break;
case DATA_FILE_PATH:
file_ptr = strrchr((char *)entry_data[i].data, '/');
if (!file_ptr)
file_ptr = (char *)entry_data[i].data;
else
file_ptr++;
file_ptr = strndup(file_ptr,
min(entry_data[i].len,
digest_metadata_fields_str_len[i]));
pr_info("%*s", digest_metadata_fields_str_len[i],
file_ptr);
free(file_ptr);
break;
case DATA_LENGTH:
pr_info("%*d", digest_metadata_fields_str_len[i],
*(u16 *)entry_data[i].data);
break;
}
}
printf("\n");
}
/*******************************
* Digest list metadata parser *
*******************************/
int ima_check_parser(u8 *data, u32 data_len,
u8 **digest, u16 *digest_algo)
{
int parser_len = sizeof(PARSER_STRING) - 1;
int digest_len, expected_data_len;
u8 *datap = data + data_len - parser_len;
u16 version, algo;
version = *(u16 *)data;
if (ima_canonical_fmt)
version = le16_to_cpu(version);
if (version > REQ_PARSER_VERSION)
return -EINVAL;
algo = *(u16 *)(data + sizeof(u16));
if (ima_canonical_fmt)
algo = le16_to_cpu(algo);
if (algo < 0 || algo >= HASH_ALGO__LAST)
return -EINVAL;
digest_len = hash_digest_size[algo];
expected_data_len = sizeof(u16) * 2 + digest_len + parser_len;
if (data_len != expected_data_len)
return -EINVAL;
if (memcmp(datap, PARSER_STRING, parser_len))
return -EINVAL;
*digest = data + 2 * sizeof(u16);
*digest_algo = algo;
return 0;
}
static int ima_check_signature(u16 data_algo, u8 *type_ext, u32 type_ext_len,
u8 *digest, u32 digest_len, u16 sig_fmt,
u8 *sig, u32 sig_len)
{
return 0;
}
static int ima_digest_list_create_key(u8 *payload, u32 len)
{
return 0;
}
static void ima_digest_list_set_algo(char *pathname, u16 algo)
{
if (ima_fd == -1 && !set_ima_algo)
return;
removexattr(pathname, XATTR_NAME_IMA);
setxattr(pathname, XATTR_NAME_IMA_ALGO, &algo, sizeof(algo), 0);
}
static void ima_remove_file(char *path)
{
char path_tmp[MAX_PATH_LENGTH];
struct stat st;
if (!remove_file || !path)
return;
unlink(path);
snprintf(path_tmp, sizeof(path_tmp), "%s.sig", path);
if (!stat(path_tmp, &st))
unlink(path_tmp);
}
ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf)
{
struct ima_field_data entry;
struct ima_field_data entry_data[DATA__LAST] = {
[DATA_ALGO] = {.len = sizeof(u16)},
[DATA_TYPE] = {.len = sizeof(u16)},
[DATA_DIGEST_ALGO] = {.len = sizeof(u16)},
[DATA_SIG_FMT] = {.len = sizeof(u16)},
[DATA_LENGTH] = {.len = sizeof(u32)},
};
DECLARE_BITMAP(data_mask, DATA__LAST);
void *bufp = buf, *bufendp = buf + size;
u16 data_algo, data_type, digest_algo, sig_fmt, version, parser_algo;
u8 flags = DIGEST_FLAG_IMMUTABLE;
u8 *digest;
char *path;
int ret;
if (current != parser_task)
return -EPERM;
bitmap_zero(data_mask, DATA__LAST);
bitmap_set(data_mask, DATA_ALGO, 1);
bitmap_set(data_mask, DATA_TYPE, 1);
bitmap_set(data_mask, DATA_DIGEST_ALGO, 1);
bitmap_set(data_mask, DATA_SIG_FMT, 1);
bitmap_set(data_mask, DATA_LENGTH, 1);
ret = ima_parse_buf(bufp, bufendp, &bufp, 1, &entry, NULL, NULL,
ENFORCE_FIELDS, "metadata list entry");
if (ret < 0)
return ret;
ret = ima_parse_buf(entry.data, entry.data + entry.len, NULL,
DATA__LAST, entry_data, NULL, data_mask,
ENFORCE_FIELDS | ENFORCE_BUFEND,
"metadata entry data");
if (ret < 0)
return ret;
data_algo = *(u16 *)entry_data[DATA_ALGO].data;
data_type = *(u16 *)entry_data[DATA_TYPE].data;
digest_algo = *(u16 *)entry_data[DATA_DIGEST_ALGO].data;
sig_fmt = *(u16 *)entry_data[DATA_SIG_FMT].data;
digest = entry_data[DATA_DIGEST].data;
path = (char *)entry_data[DATA_FILE_PATH].data;
if (ima_canonical_fmt) {
data_algo = le16_to_cpu(data_algo);
data_type = le16_to_cpu(data_type);
digest_algo = le16_to_cpu(digest_algo);
sig_fmt = le16_to_cpu(sig_fmt);
}
switch (data_type) {
case DATA_TYPE_HEADER:
if (entry_data[DATA_TYPE_EXT].len != sizeof(u16))
return -EINVAL;
version = le16_to_cpu(*(u16 *)entry_data[DATA_TYPE_EXT].data);
if (version > REQ_METADATA_VERSION)
return -EINVAL;
goto out;
case DATA_TYPE_DIGEST_LIST:
/* digest lists, except the compact, are parsed in user space */
break;
case DATA_TYPE_KEY:
ret = ima_digest_list_create_key(entry_data[DATA_ALGO].data,
entry_data[DATA_ALGO].len);
goto out;
case DATA_TYPE_PARSER:
ret = ima_check_parser(entry_data[DATA_TYPE_EXT].data,
entry_data[DATA_TYPE_EXT].len,
&digest, &parser_algo);
if (ret < 0)
return ret;
if (parser_algo != data_algo) {
pr_err("Parser digest algorithm mismatch\n");
return -EINVAL;
}
digest_algo = parser_algo;
break;
default:
pr_err("Invalid data type %d\n", data_type);
return -EINVAL;
}
if (digest_algo != ima_hash_algo) {
if (digest_algo < 0 || digest_algo >= HASH_ALGO__LAST) {
pr_err("Unknown algorithm\n");
return -EINVAL;
}
flags |= DIGEST_FLAG_DIGEST_ALGO;
ima_digest_list_set_algo(path, digest_algo);
}
if (ima_policy_flag & IMA_APPRAISE) {
ret = ima_check_signature(data_algo,
entry_data[DATA_TYPE_EXT].data,
entry_data[DATA_TYPE_EXT].len,
digest, entry_data[DATA_DIGEST].len,
sig_fmt, entry_data[DATA_SIG].data,
entry_data[DATA_SIG].len);
if (ret < 0) {
if (ret == -ENOENT)
goto out;
pr_err("Failed signature verification of: %s (%d)\n",
path, ret);
return ret;
}
} else {
ima_digest_list_actions &= ~IMA_APPRAISE;
}
ret = ima_add_digest_data_entry(digest, digest_algo, flags, data_type,
ACTION_RESET);
if (ret < 0 && ret != -EEXIST)
return ret;
if (data_type == DATA_TYPE_DIGEST_LIST) {
u32 data_sub_type, data_length;
if (entry_data[DATA_TYPE_EXT].len != sizeof(u32))
return -EINVAL;
data_sub_type = *(u32 *)entry_data[DATA_TYPE_EXT].data;
data_length = *(u32 *)entry_data[DATA_LENGTH].data;
if (ima_canonical_fmt) {
data_sub_type = le32_to_cpu(data_sub_type);
data_length = le32_to_cpu(data_length);
}
ret = ima_parse_digest_list_data(data_algo, digest_algo,
data_sub_type, digest, path,
data_length);
if (ret < 0)
return ret;
}
out:
if (ima_fd == -1) {
if (data_type == DATA_TYPE_HEADER) {
num_entry = 0;
print_metadata_header();
}
print_metadata(entry_data);
}
if (remove_file)
ima_remove_file(path);
return bufp - buf;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
bin_SCRIPTS = setup_ima_digest_lists
bin_SCRIPTS=setup_ima_digest_lists setup_ima_digest_lists_demo xattr.awk
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
systemddir=$(prefix)/lib/systemd/system
systemd_DATA=setup-ima-digest-lists.service
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册