public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
From: Aaron Merey <amerey@redhat.com>
To: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Mark Wielaard <mark@klomp.org>, elfutils-devel@sourceware.org
Subject: Re: [rfc] [patch] PR28204: debuginfod ima signature verification
Date: Thu, 9 May 2024 13:56:11 -0400	[thread overview]
Message-ID: <CAJDtP-Sq=m0nUwy4B-UsN-mAEp72Hv55HT3vM7VBiJafS4DK7A@mail.gmail.com> (raw)
In-Reply-To: <20240505013003.GA1291@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 64457 bytes --]

Hi Frank,

I've pointed out a couple nits below, but otherwise the patch LGTM.
I've also attached a diff for handling DEBUGINFOD_IMA_CERT_PATH in
profile.fish.in that should apply on top of this patch.

I know there's already been a lot of discussion re. ima:permissive and
I'm weighing in rather late, but FWIW I do support including it.
Currently individual ELF sections cannot be downloaded when
ima:enforcing is active.  With ima:permissive we could support proper
section queries while also being able to perform some amount of
ima verification.

On Tue, Apr 16, 2024 at 6:15 PM Frank Ch. Eigler <fche@redhat.com> wrote:
>
> Hi -
>
> The following is the candidate patch for the basic functionality.
> It's been corrected for whitespace & error codes, given more complete
> docs and commit message.  See also the users/fche/try-bz2824f branch.
>
>
>     debuginfod: PR28204 - RPM IMA per-file signature verification
>
>     Recent versions of Fedora/RHEL include per-file cryptographic
>     signatures in RPMs, not just an overall RPM signature.  This work
>     extends debuginfod client & server to extract, transfer, and verify
>     those signatures.  These allow clients to assure users that the
>     downloaded files have not been corrupted since their original
>     packaging.  Downloads that fail the test are rejected.
>
>     Clients may select a desired level of enforcement for sets of URLs in
>     the DEBUGINFOD_URLS by inserting special markers ahead of them:
>
>     ima:ignore       pay no attention to absence or presence of signatures
>     ima:enforcing    require every file to be correctly signed
>
>     The default is ima:ignore mode.  In ima:enforcing mode, section
>     queries are forced to be entire-file downloads, as it is not
>     possible to crypto-verify just sections.
>
>     IMA signatures are verified against a set of signing certificates.
>     These are normally published by distributions.  The environment
>     variable $DEBUGINFOD_IMA_CERT_PATH contains a colon-separated path for
>     finding DER or PEM formatted certificates / public keys.  These
>     certificates are assumed trusted.  The profile.d scripts transcribe
>     /etc/debuginfod/*.certdir files into that variable.
>
>     As for implementation:
>
>     * configure.ac: Add --enable-default-ima-cert-path=PATH parameter.
>       Check for libimaevm (using headers only).
>
>     * config/Makefile.am: Install defaults into /etc files.
>     * config/profile.{csh,sh}.in: Process defaults into env variables.
>     * config/elfutils.spec.in: Add more buildrequires.
>
>     * debuginfod/debuginfod.cxx (handle_buildid_r_match): Added extraction of the
>       per-file IMA signature for the queried file and store in http header.
>       (find_globbed_koji_filepath): New function.
>       (parse_opt): New flag --koji-sigcache.
>     * debuginfod/debuginfod-client.c (debuginfod_query_server): Added policy for
>       validating IMA signatures
>       (debuginfod_validate_imasig): New function, with friends.
>     * debuginfod/debuginfod.h.in: Added DEBUGINFOD_IMA_CERT_PATH_ENV_VAR.
>     * debuginfod/Makefile.am: Add linker flags for rpm and crypto.
>
>     * doc/debuginfod-client-config.7: Document DEBUGINFOD_IMA_CERT_PATH,
>       update DEBUGINFOD_URLS.
>     * doc/debuginfod.8: Document --koji-sigcache.
>     * doc/debuginfod-find.1, doc/debuginfod_find_debuginfo.3: Update SECURITY.
>
>     * tests/run-debuginfod-ima-verification.sh: New test.
>     * tests/debuginfod-ima: Some new files for the tests.
>     * tests/Makefile.am: run/distribute them.
>
>     Signed-off-by: Ryan Goldberg <rgoldber@redhat.com>
>     Signed-off-by: Frank Ch. Eigler <fche@redhat.com>
>
> diff --git a/config/Makefile.am b/config/Makefile.am
> index ae14e625b726..5a28e66d4408 100644
> --- a/config/Makefile.am
> +++ b/config/Makefile.am
> @@ -46,12 +46,16 @@ pkgconfig_DATA += libdebuginfod.pc
>         if [ -n "@DEBUGINFOD_URLS@" ]; then \
>                 echo "@DEBUGINFOD_URLS@" > $(DESTDIR)$(sysconfdir)/debuginfod/elfutils.urls; \
>         fi
> +       if [ -n "@DEBUGINFOD_IMA_CERT_PATH@" ]; then \
> +               echo "@DEBUGINFOD_IMA_CERT_PATH@" > $(DESTDIR)$(sysconfdir)/debuginfod/elfutils.certpath; \
> +       fi
>
>  uninstall-local:
>         rm -f $(DESTDIR)$(sysconfdir)/profile.d/debuginfod.sh
>         rm -f $(DESTDIR)$(sysconfdir)/profile.d/debuginfod.csh
>         rm -f $(DESTDIR)$(datadir)/fish/vendor_conf.d/debuginfod.fish
>         rm -f $(DESTDIR)$(sysconfdir)/debuginfod/elfutils.urls
> +       rm -f $(DESTDIR)$(sysconfdir)/debuginfod/elfutils.certpath
>         -rmdir $(DESTDIR)$(sysconfdir)/debuginfod
>  endif
>
> diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in
> index 4d802a25ad5f..460729972420 100644
> --- a/config/elfutils.spec.in
> +++ b/config/elfutils.spec.in
> @@ -43,6 +43,12 @@ BuildRequires: curl
>  # For run-debuginfod-response-headers.sh test case
>  BuildRequires: socat
>
> +# For debuginfod rpm IMA verification
> +BuildRequires: rpm-devel
> +BuildRequires: ima-evm-utils-devel
> +BuildRequires: openssl-devel
> +BuildRequires: rpm-sign
> +
>  %define _gnu %{nil}
>  %define _programprefix eu-
>
> diff --git a/config/profile.csh.in b/config/profile.csh.in
> index d962d969c05b..1da9626c711b 100644
> --- a/config/profile.csh.in
> +++ b/config/profile.csh.in
> @@ -4,13 +4,19 @@
>  # See also [man debuginfod-client-config] for other environment variables
>  # such as $DEBUGINFOD_MAXSIZE, $DEBUGINFOD_MAXTIME, $DEBUGINFOD_PROGRESS.
>
> +set prefix="@prefix@"
>  if (! $?DEBUGINFOD_URLS) then
> -    set prefix="@prefix@"
>      set DEBUGINFOD_URLS=`sh -c 'cat /dev/null "$0"/*.urls 2>/dev/null; :' "@sysconfdir@/debuginfod" | tr '\n' ' '`
>      if ( "$DEBUGINFOD_URLS" != "" ) then
>          setenv DEBUGINFOD_URLS "$DEBUGINFOD_URLS"
>      else
>          unset DEBUGINFOD_URLS
>      endif
> -    unset prefix
> +    set DEBUGINFOD_IMA_CERT_PATH=`sh -c 'cat /dev/null "$0"/*.certpath 2>/dev/null; :' "@sysconfdir@/debuginfod" | tr '\n' ':'`
> +    if ( "$DEBUGINFOD_IMA_CERT_PATH" != "" ) then
> +        setenv DEBUGINFOD_IMA_CERT_PATH "$DEBUGINFOD_IMA_CERT_PATH"
> +    else
> +        unset DEBUGINFOD_IMA_CERT_PATH
> +    endif
>  endif
> +unset prefix
> diff --git a/config/profile.sh.in b/config/profile.sh.in
> index 84d3260ddcfc..7db399960915 100644
> --- a/config/profile.sh.in
> +++ b/config/profile.sh.in
> @@ -4,9 +4,15 @@
>  # See also [man debuginfod-client-config] for other environment variables
>  # such as $DEBUGINFOD_MAXSIZE, $DEBUGINFOD_MAXTIME, $DEBUGINFOD_PROGRESS.
>
> +prefix="@prefix@"
>  if [ -z "$DEBUGINFOD_URLS" ]; then
>      prefix="@prefix@"

This second definition of prefix can be removed.

>      DEBUGINFOD_URLS=$(cat /dev/null "@sysconfdir@/debuginfod"/*.urls 2>/dev/null | tr '\n' ' ' || :)
>      [ -n "$DEBUGINFOD_URLS" ] && export DEBUGINFOD_URLS || unset DEBUGINFOD_URLS
> -    unset prefix
>  fi
> +
> +if [ -z "$DEBUGINFOD_IMA_CERT_PATH" ]; then
> +    DEBUGINFOD_IMA_CERT_PATH=$(cat "@sysconfdir@/debuginfod"/*.certpath 2>/dev/null | tr '\n' ':' || :)
> +    [ -n "$DEBUGINFOD_IMA_CERT_PATH" ] && export DEBUGINFOD_IMA_CERT_PATH || unset DEBUGINFOD_IMA_CERT_PATH
> +fi
> +unset prefix
> diff --git a/configure.ac b/configure.ac
> index a279bb5282c9..d75d9ba02e79 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -667,6 +667,33 @@ case "$ac_cv_search__obstack_free" in
>  esac
>  AC_SUBST([obstack_LIBS])
>
> +enable_ima_verification="x"
> +AC_CHECK_LIB(rpm, headerGet, [
> +  AC_CHECK_DECL(RPMSIGTAG_FILESIGNATURES,
> +  [
> +    enable_ima_verification=$enable_ima_verification"rpm"
> +    AC_SUBST(rpm_LIBS, '-lrpm -lrpmio')
> +  ],
> +  [], [#include <rpm/rpmlib.h>])
> +])
> +
> +dnl we use only the header, not the code of this library
> +AC_CHECK_HEADER(imaevm.h, [
> +  enable_ima_verification=$enable_ima_verification"imaevm"
> +])
> +
> +AC_CHECK_LIB(crypto, EVP_MD_CTX_new, [
> +  enable_ima_verification=$enable_ima_verification"crypto"
> +  AC_SUBST(crypto_LIBS, '-lcrypto')
> +])
> +
> +debuginfod_ima_verification_enabled="no"
> +if test "$enable_ima_verification" = "xrpmimaevmcrypto"; then
> +  debuginfod_ima_verification_enabled="yes"
> +  AC_DEFINE([ENABLE_IMA_VERIFICATION], [1], [Define if the required ima verification libraries are available])
> +fi
> +AM_CONDITIONAL([ENABLE_IMA_VERIFICATION],[test "$enable_ima_verification" = "xrpmimaevmcrypto"])
> +
>  dnl The directories with content.
>
>  dnl Documentation.
> @@ -881,6 +908,15 @@ AC_ARG_ENABLE(debuginfod-urls,
>               fi],
>              [default_debuginfod_urls=""])
>  AC_SUBST(DEBUGINFOD_URLS, $default_debuginfod_urls)
> +AC_ARG_ENABLE(debuginfod-ima-cert-path,
> +            [AS_HELP_STRING([--enable-debuginfod-ima-cert-path@<:@=PATH@:>@],[add PATH to profile.d DEBUGINFOD_IMA_CERT_PATH])],
> +            [if test "x${enableval}" = "xyes";
> +             then AC_MSG_ERROR([PATH required])
> +             elif test "x${enableval}" != "xno"; then
> +             default_debuginfod_ima_cert_path="${enableval}";
> +             fi],
> +            [default_debuginfod_ima_cert_path=""])
> +AC_SUBST(DEBUGINFOD_IMA_CERT_PATH, $default_debuginfod_ima_cert_path)

It might be helpful to add AC_ARG_ENABLE for debuginfod_ima_verification.
Then a configure error can alert us to any missing libraries if
--enable-debuginfod-ima-verification=yes.

>  AC_CONFIG_FILES([config/profile.sh config/profile.csh config/profile.fish])
>
>  AC_OUTPUT
> @@ -920,6 +956,7 @@ AC_MSG_NOTICE([
>      libdebuginfod client support       : ${enable_libdebuginfod}
>      Debuginfod server support          : ${enable_debuginfod}
>      Default DEBUGINFOD_URLS            : ${default_debuginfod_urls}
> +    Debuginfod RPM sig checking        : ${debuginfod_ima_verification_enabled} ${default_debuginfod_ima_cert_path}
>
>    EXTRA TEST FEATURES (used with make check)
>      have bunzip2 installed (required)  : ${HAVE_BUNZIP2}
> diff --git a/debuginfod/Makefile.am b/debuginfod/Makefile.am
> index 125be97bbfcc..5e4f9669d7c1 100644
> --- a/debuginfod/Makefile.am
> +++ b/debuginfod/Makefile.am
> @@ -70,7 +70,7 @@ bin_PROGRAMS += debuginfod-find
>  endif
>
>  debuginfod_SOURCES = debuginfod.cxx
> -debuginfod_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS) $(libmicrohttpd_LIBS) $(sqlite3_LIBS) $(libarchive_LIBS) -lpthread -ldl
> +debuginfod_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS) $(libmicrohttpd_LIBS) $(sqlite3_LIBS) $(libarchive_LIBS) $(rpm_LIBS) -lpthread -ldl
>
>  debuginfod_find_SOURCES = debuginfod-find.c
>  debuginfod_find_LDADD = $(libdw) $(libelf) $(libeu) $(libdebuginfod) $(argp_LDADD) $(fts_LIBS)
> @@ -97,7 +97,7 @@ libdebuginfod_so_LIBS = libdebuginfod_pic.a
>  if DUMMY_LIBDEBUGINFOD
>  libdebuginfod_so_LDLIBS =
>  else
> -libdebuginfod_so_LDLIBS = -lpthread $(libcurl_LIBS) $(fts_LIBS) $(libelf)
> +libdebuginfod_so_LDLIBS = -lpthread $(libcurl_LIBS) $(fts_LIBS) $(libelf) $(crypto_LIBS)
>  endif
>  $(LIBDEBUGINFOD_SONAME): $(srcdir)/libdebuginfod.map $(libdebuginfod_so_LIBS)
>         $(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \
> @@ -117,7 +117,6 @@ install: install-am libdebuginfod.so
>                 $(DESTDIR)$(libdir)/libdebuginfod-$(PACKAGE_VERSION).so
>         ln -fs libdebuginfod-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/$(LIBDEBUGINFOD_SONAME)
>         ln -fs libdebuginfod-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdebuginfod.so
> -
>  uninstall: uninstall-am
>         rm -f $(DESTDIR)$(libdir)/libdebuginfod-$(PACKAGE_VERSION).so
>         rm -f $(DESTDIR)$(libdir)/$(LIBDEBUGINFOD_SONAME)
> diff --git a/debuginfod/debuginfod-client.c b/debuginfod/debuginfod-client.c
> index 4e7a8a2ad9ff..4dc6b4411eb2 100644
> --- a/debuginfod/debuginfod-client.c
> +++ b/debuginfod/debuginfod-client.c
> @@ -1,5 +1,5 @@
>  /* Retrieve ELF / DWARF / source files from the debuginfod.
> -   Copyright (C) 2019-2021 Red Hat, Inc.
> +   Copyright (C) 2019-2024 Red Hat, Inc.
>     Copyright (C) 2021, 2022 Mark J. Wielaard <mark@klomp.org>
>     This file is part of elfutils.
>
> @@ -47,6 +47,17 @@
>  #include <stdlib.h>
>  #include <gelf.h>
>
> +#ifdef ENABLE_IMA_VERIFICATION
> +#include <openssl/sha.h>
> +#include <openssl/pem.h>
> +#include <openssl/evp.h>
> +#include <openssl/x509v3.h>
> +#include <arpa/inet.h>
> +#include <imaevm.h>
> +#endif
> +typedef enum {ignore, enforcing, undefined} ima_policy_t;
> +
> +
>  /* We might be building a bootstrap dummy library, which is really simple. */
>  #ifdef DUMMY_LIBDEBUGINFOD
>
> @@ -92,6 +103,7 @@ void debuginfod_end (debuginfod_client *c) { }
>  #include <sys/stat.h>
>  #include <sys/utsname.h>
>  #include <curl/curl.h>
> +#include <fnmatch.h>
>
>  /* If fts.h is included before config.h, its indirect inclusions may not
>     give us the right LFS aliases of these functions, so map them manually.  */
> @@ -130,6 +142,17 @@ libcurl_init(void)
>      }
>  }
>
> +
> +#ifdef ENABLE_IMA_VERIFICATION
> +struct public_key_entry
> +{
> +  struct public_key_entry *next; /* singly-linked list */
> +  uint32_t keyid; /* last 4 bytes of sha1 of public key */
> +  EVP_PKEY *key; /* openssl */
> +};
> +#endif
> +
> +
>  struct debuginfod_client
>  {
>    /* Progress/interrupt callback function. */
> @@ -164,8 +187,14 @@ struct debuginfod_client
>       handle data, etc. So those don't have to be reparsed and
>       recreated on each request.  */
>    char * winning_headers;
> +
> +#ifdef ENABLE_IMA_VERIFICATION
> +  /* IMA public keys */
> +  struct public_key_entry *ima_public_keys;
> +#endif
>  };
>
> +
>  /* The cache_clean_interval_s file within the debuginfod cache specifies
>     how frequently the cache should be cleaned. The file's st_mtime represents
>     the time of last cleaning.  */
> @@ -225,6 +254,182 @@ struct handle_data
>    size_t response_data_size;
>  };
>
> +
> +
> +#ifdef ENABLE_IMA_VERIFICATION
> +  static inline unsigned char hex2dec(char c)
> +  {
> +    if (c >= '0' && c <= '9') return (c - '0');
> +    if (c >= 'a' && c <= 'f') return (c - 'a') + 10;
> +    if (c >= 'A' && c <= 'F') return (c - 'A') + 10;
> +    return 0;
> +  }
> +
> +  static inline ima_policy_t ima_policy_str2enum(const char* ima_pol)
> +  {
> +    if (NULL == ima_pol)                    return undefined;
> +    if (0 == strcmp(ima_pol, "ignore"))     return ignore;
> +    if (0 == strcmp(ima_pol, "enforcing"))  return enforcing;
> +    return undefined;
> +  }
> +
> +  static inline const char* ima_policy_enum2str(ima_policy_t ima_pol)
> +  {
> +    switch (ima_pol)
> +    {
> +    case ignore:
> +      return "ignore";
> +    case enforcing:
> +      return "enforcing";
> +    case undefined:
> +      return "undefined";
> +    }
> +    return "";
> +  }
> +
> +
> +static uint32_t extract_skid_pk(EVP_PKEY *pkey) // compute keyid by public key hashing
> +{
> +  if (!pkey) return 0;
> +  uint32_t keyid = 0;
> +  X509_PUBKEY *pk = NULL;
> +  const unsigned char *public_key = NULL;
> +  int len;
> +  if (X509_PUBKEY_set(&pk, pkey) &&
> +      X509_PUBKEY_get0_param(NULL, &public_key, &len, NULL, pk))
> +    {
> +      uint8_t sha1[SHA_DIGEST_LENGTH];
> +      SHA1(public_key, len, sha1);
> +      memcpy(&keyid, sha1 + 16, 4);
> +    }
> +  X509_PUBKEY_free(pk);
> +  return ntohl(keyid);
> +}
> +
> +
> +static uint32_t extract_skid(X509* x509) // compute keyid from cert or its public key
> +  {
> +    if (!x509) return 0;
> +    uint32_t keyid = 0;
> +    // Attempt to get the skid from the certificate
> +    const ASN1_OCTET_STRING *skid_asn1_str = X509_get0_subject_key_id(x509);
> +    if (skid_asn1_str)
> +      {
> +        int skid_len = ASN1_STRING_length(skid_asn1_str);
> +        memcpy(&keyid, ASN1_STRING_get0_data(skid_asn1_str) + skid_len - sizeof(keyid), sizeof(keyid));
> +      }
> +    else // compute keyid ourselves by hashing public key
> +      {
> +        EVP_PKEY *pkey = X509_get0_pubkey(x509);
> +        keyid = htonl(extract_skid_pk(pkey));
> +      }
> +    return ntohl(keyid);
> +  }
> +
> +
> +static void load_ima_public_keys (debuginfod_client *c)
> +{
> +  /* Iterate over the directories in DEBUGINFOD_IMA_CERT_PATH. */
> +  char *cert_paths = getenv(DEBUGINFOD_IMA_CERT_PATH_ENV_VAR);
> +  if (cert_paths == NULL || cert_paths[0] == '\0')
> +    return;
> +  cert_paths = strdup(cert_paths); // Modified during tokenization
> +  if (cert_paths == NULL)
> +    return;
> +
> +  char* cert_dir_path;
> +  DIR *dp;
> +  struct dirent *entry;
> +  int vfd = c->verbose_fd;
> +
> +  char *strtok_context = NULL;
> +  for(cert_dir_path = strtok_r(cert_paths, ":", &strtok_context);
> +      cert_dir_path != NULL;
> +      cert_dir_path = strtok_r(NULL, ":", &strtok_context))
> +    {
> +      dp = opendir(cert_dir_path);
> +      if(!dp) continue;
> +      while((entry = readdir(dp)))
> +        {
> +          // Only consider regular files with common x509 cert extensions
> +          if(entry->d_type != DT_REG || 0 != fnmatch("*.@(der|pem|crt|cer|cert)", entry->d_name, FNM_EXTMATCH)) continue;
> +          char certfile[PATH_MAX];
> +          strncpy(certfile, cert_dir_path, PATH_MAX - 1);
> +          if(certfile[strlen(certfile)-1] != '/') certfile[strlen(certfile)] = '/';
> +          strncat(certfile, entry->d_name, PATH_MAX - strlen(certfile) - 1);
> +          certfile[strlen(certfile)] = '\0';
> +
> +          FILE *cert_fp = fopen(certfile, "r");
> +          if(!cert_fp) continue;
> +
> +          X509 *x509 = NULL;
> +          EVP_PKEY *pkey = NULL;
> +          char *fmt = "";
> +          // Attempt to read the fp as DER
> +          if(d2i_X509_fp(cert_fp, &x509))
> +            fmt = "der ";
> +          // Attempt to read the fp as PEM and assuming the key matches that of the signature add this key to be used
> +          // Note we fseek since this is the second time we read from the fp
> +          else if(0 == fseek(cert_fp, 0, SEEK_SET) && PEM_read_X509(cert_fp, &x509, NULL, NULL))
> +            fmt = "pem "; // PEM with full certificate
> +          else if(0 == fseek(cert_fp, 0, SEEK_SET) && PEM_read_PUBKEY(cert_fp, &pkey, NULL, NULL))
> +            fmt = "pem "; // some PEM files have just a PUBLIC KEY in them
> +          fclose(cert_fp);
> +
> +          if (x509)
> +            {
> +              struct public_key_entry *ne = calloc(1, sizeof(struct public_key_entry));
> +              if (ne)
> +                {
> +                  ne->key = X509_extract_key(x509);
> +                  ne->keyid = extract_skid(x509);
> +                  ne->next = c->ima_public_keys;
> +                  c->ima_public_keys = ne;
> +                  if (vfd >= 0)
> +                    dprintf(vfd, "Loaded %scertificate %s, keyid = %04x\n", fmt, certfile, ne->keyid);
> +                }
> +              X509_free (x509);
> +            }
> +          else if (pkey)
> +            {
> +              struct public_key_entry *ne = calloc(1, sizeof(struct public_key_entry));
> +              if (ne)
> +                {
> +                  ne->key = pkey; // preserve refcount
> +                  ne->keyid = extract_skid_pk(pkey);
> +                  ne->next = c->ima_public_keys;
> +                  c->ima_public_keys = ne;
> +                  if (vfd >= 0)
> +                    dprintf(vfd, "Loaded %spubkey %s, keyid %04x\n", fmt, certfile, ne->keyid);
> +                }
> +            }
> +          else
> +            {
> +              if (vfd >= 0)
> +                dprintf(vfd, "Cannot load certificate %s\n", certfile);
> +            }
> +        } /* for each file in directory */
> +      closedir(dp);
> +    } /* for each directory */
> +
> +  free(cert_paths);
> +}
> +
> +
> +static void free_ima_public_keys (debuginfod_client *c)
> +{
> +  while (c->ima_public_keys)
> +    {
> +      EVP_PKEY_free (c->ima_public_keys->key);
> +      struct public_key_entry *oen = c->ima_public_keys->next;
> +      free (c->ima_public_keys);
> +      c->ima_public_keys = oen;
> +    }
> +}
> +#endif
> +
> +
> +
>  static size_t
>  debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
>  {
> @@ -861,6 +1066,199 @@ cache_find_section (const char *scn_name, const char *target_cache_dir,
>    return rc;
>  }
>
> +
> +#ifdef ENABLE_IMA_VERIFICATION
> +/* Extract the hash algorithm name from the signature header, of which
> +   there are several types.  The name will be used for openssl hashing
> +   of the file content.  The header doesn't need to be super carefully
> +   parsed, because if any part of it is wrong, be it the hash
> +   algorithm number or hash value or whatever, it will fail
> +   computation or verification.  Return NULL in case of error.  */
> +static const char*
> +get_signature_params(debuginfod_client *c, unsigned char *bin_sig)
> +{
> +  int hashalgo = 0;
> +
> +  switch (bin_sig[0])
> +    {
> +    case EVM_IMA_XATTR_DIGSIG:
> +#ifdef IMA_VERITY_DIGSIG /* missing on debian-i386 trybot */
> +    case IMA_VERITY_DIGSIG:
> +#endif
> +      break;
> +    default:
> +      if (c->verbose_fd >= 0)
> +        dprintf (c->verbose_fd, "Unknown ima digsig %d\n", (int)bin_sig[0]);
> +      return NULL;
> +    }
> +
> +  switch (bin_sig[1])
> +    {
> +    case DIGSIG_VERSION_2:
> +      struct signature_v2_hdr hdr_v2;
> +      memcpy(& hdr_v2, & bin_sig[1], sizeof(struct signature_v2_hdr));
> +      hashalgo = hdr_v2.hash_algo;
> +      break;
> +    default:
> +      if (c->verbose_fd >= 0)
> +        dprintf (c->verbose_fd, "Unknown ima signature version %d\n", (int)bin_sig[1]);
> +      return NULL;
> +    }
> +
> +  switch (hashalgo)
> +    {
> +    case PKEY_HASH_SHA1: return "sha1";
> +    case PKEY_HASH_SHA256: return "sha256";
> +      // (could add many others from enum pkey_hash_algo)
> +    default:
> +      if (c->verbose_fd >= 0)
> +        dprintf (c->verbose_fd, "Unknown ima pkey hash %d\n", hashalgo);
> +      return NULL;
> +    }
> +}
> +
> +
> +/* Verify given hash against given signature blob.  Return 0 on ok, -errno otherwise. */
> +static int
> +debuginfod_verify_hash(debuginfod_client *c, const unsigned char *hash, int size,
> +                       const char *hash_algo, unsigned char *sig, int siglen)
> +{
> +  int ret = -EBADMSG;
> +  struct public_key_entry *pkey;
> +  struct signature_v2_hdr hdr;
> +  EVP_PKEY_CTX *ctx;
> +  const EVP_MD *md;
> +
> +  memcpy(&hdr, sig, sizeof(struct signature_v2_hdr)); /* avoid just aliasing */
> +
> +  if (c->verbose_fd >= 0)
> +    dprintf (c->verbose_fd, "Searching for ima keyid %04x\n", ntohl(hdr.keyid));
> +
> +  /* Find the matching public key. */
> +  for (pkey = c->ima_public_keys; pkey != NULL; pkey = pkey->next)
> +    if (pkey->keyid == ntohl(hdr.keyid)) break;
> +  if (!pkey)
> +    return -ENOKEY;
> +
> +  if (!(ctx = EVP_PKEY_CTX_new(pkey->key, NULL)))
> +    goto err;
> +  if (!EVP_PKEY_verify_init(ctx))
> +    goto err;
> +  if (!(md = EVP_get_digestbyname(hash_algo)))
> +    goto err;
> +  if (!EVP_PKEY_CTX_set_signature_md(ctx, md))
> +    goto err;
> +  ret = EVP_PKEY_verify(ctx, sig + sizeof(hdr),
> +                        siglen - sizeof(hdr), hash, size);
> +  if (ret == 1)
> +    ret = 0; // success!
> +  else if (ret == 0)
> +    ret = -EBADMSG;
> + err:
> +  EVP_PKEY_CTX_free(ctx);
> +  return ret;
> +}
> +
> +
> +
> +/* Validate an IMA file signature.
> + * Returns 0 on signature validity, -EINVAL on signature invalidity, -ENOSYS on undefined imaevm machinery,
> + * -ENOKEY on key issues, or other -errno.
> + */
> +
> +static int
> +debuginfod_validate_imasig (debuginfod_client *c, int fd)
> +{
> +  int rc = ENOSYS;
> +
> +  // int vfd = c->verbose_fd;

This line can be removed.

> +    EVP_MD_CTX *ctx = NULL;
> +    if (!c || !c->winning_headers)
> +    {
> +      rc = -ENODATA;
> +      goto exit_validate;
> +    }
> +    // Extract the HEX IMA-signature from the header
> +    char* sig_buf = NULL;
> +    char* hdr_ima_sig = strcasestr(c->winning_headers, "x-debuginfod-imasignature");
> +    if (!hdr_ima_sig || 1 != sscanf(hdr_ima_sig + strlen("x-debuginfod-imasignature:"), "%ms", &sig_buf))
> +    {
> +      rc = -ENODATA;
> +      goto exit_validate;
> +    }
> +    if (strlen(sig_buf) > MAX_SIGNATURE_SIZE) // reject if too long
> +    {
> +      rc = -EBADMSG;
> +      goto exit_validate;
> +    }
> +    // Convert the hex signature to bin
> +    size_t bin_sig_len = strlen(sig_buf)/2;
> +    unsigned char bin_sig[MAX_SIGNATURE_SIZE/2];
> +    for (size_t b = 0; b < bin_sig_len; b++)
> +      bin_sig[b] = (hex2dec(sig_buf[2*b]) << 4) | hex2dec(sig_buf[2*b+1]);
> +
> +    // Compute the binary digest of the cached file (with file descriptor fd)
> +    ctx = EVP_MD_CTX_new();
> +    const char* sighash_name = get_signature_params(c, bin_sig) ?: "";
> +    const EVP_MD *md = EVP_get_digestbyname(sighash_name);
> +    if (!ctx || !md || !EVP_DigestInit(ctx, md))
> +    {
> +      rc = -EBADMSG;
> +      goto exit_validate;
> +    }
> +
> +    long data_len;
> +    char* hdr_data_len = strcasestr(c->winning_headers, "x-debuginfod-size");
> +    if (!hdr_data_len || 1 != sscanf(hdr_data_len + strlen("x-debuginfod-size:") , "%ld", &data_len))
> +    {
> +      rc = -ENODATA;
> +      goto exit_validate;
> +    }
> +
> +    char file_data[DATA_SIZE]; // imaevm.h data chunk hash size
> +    ssize_t n;
> +    for(off_t k = 0; k < data_len; k += n)
> +      {
> +        if (-1 == (n = pread(fd, file_data, DATA_SIZE, k)))
> +          {
> +            rc = -errno;
> +            goto exit_validate;
> +          }
> +
> +        if (!EVP_DigestUpdate(ctx, file_data, n))
> +          {
> +            rc = -EBADMSG;
> +            goto exit_validate;
> +          }
> +      }
> +
> +    uint8_t bin_dig[MAX_DIGEST_SIZE];
> +    unsigned int bin_dig_len;
> +    if (!EVP_DigestFinal(ctx, bin_dig, &bin_dig_len))
> +    {
> +      rc = -EBADMSG;
> +      goto exit_validate;
> +    }
> +
> +    // XXX: in case of DIGSIG_VERSION_3, need to hash the file hash, yo dawg
> +
> +    int res = debuginfod_verify_hash(c,
> +                                     bin_dig, bin_dig_len,
> +                                     sighash_name,
> +                                     & bin_sig[1], bin_sig_len-1); // skip over first byte of signature
> +    if (c->verbose_fd >= 0)
> +      dprintf (c->verbose_fd, "Computed ima signature verification res=%d\n", res);
> +    rc = res;
> +
> + exit_validate:
> +    free (sig_buf);
> +    EVP_MD_CTX_free(ctx);
> +    return rc;
> +}
> +#endif /* ENABLE_IMA_VERIFICATION */
> +
> +
> +
>  /* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
>     with the specified build-id and type (debuginfo, executable, source or
>     section).  If type is source, then type_arg should be a filename.  If
> @@ -1216,12 +1614,39 @@ debuginfod_query_server (debuginfod_client *c,
>    /* Initialize the memory to zero */
>    char *strtok_saveptr;
>    char **server_url_list = NULL;
> -  char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
> +  ima_policy_t* url_ima_policies = NULL;
> +  char* server_url;
>    /* Count number of URLs.  */
>    int num_urls = 0;
>
> -  while (server_url != NULL)
> +  ima_policy_t verification_mode = ignore; // The default mode
> +  for(server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
> +      server_url != NULL; server_url = strtok_r(NULL, url_delim, &strtok_saveptr))
>      {
> +      // When we encounted a (well-formed) token off the form ima:foo, we update the policy
> +      // under which results from that server will be ima verified
> +      if(startswith(server_url, "ima:"))
> +      {
> +#ifdef ENABLE_IMA_VERIFICATION
> +        ima_policy_t m = ima_policy_str2enum(server_url + strlen("ima:"));
> +        if(m != undefined)
> +          verification_mode = m;
> +        else if (vfd >= 0)
> +          dprintf(vfd, "IMA mode not recognized, skipping %s\n", server_url);
> +#else
> +        if (vfd >= 0)
> +            dprintf(vfd, "IMA signature verification is not enabled, skipping %s\n", server_url);
> +#endif
> +        continue; // Not a url, just a mode change so keep going
> +      }
> +
> +      if (verification_mode==enforcing && 0==strcmp(type,"section"))
> +        {
> +          if (vfd >= 0)
> +            dprintf(vfd, "skipping server %s section query in IMA enforcing mode\n", server_url);
> +          continue;
> +        }
> +
>        /* PR 27983: If the url is already set to be used use, skip it */
>        char *slashbuildid;
>        if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
> @@ -1253,21 +1678,28 @@ debuginfod_query_server (debuginfod_client *c,
>        else
>          {
>            num_urls++;
> -          char ** realloc_ptr;
> -          realloc_ptr = reallocarray(server_url_list, num_urls,
> -                                         sizeof(char*));
> -          if (realloc_ptr == NULL)
> +          if (NULL == (server_url_list  = reallocarray(server_url_list, num_urls, sizeof(char*)))
> +#ifdef ENABLE_IMA_VERIFICATION
> +          || NULL == (url_ima_policies = reallocarray(url_ima_policies, num_urls, sizeof(ima_policy_t)))
> +#endif
> +            )
>              {
>                free (tmp_url);
>                rc = -ENOMEM;
>                goto out1;
>              }
> -          server_url_list = realloc_ptr;
>            server_url_list[num_urls-1] = tmp_url;
> +          if(NULL != url_ima_policies) url_ima_policies[num_urls-1] = verification_mode;
>          }
> -      server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
>      }
>
> +  /* No URLs survived parsing / filtering?  Abort abort abort. */
> +  if (num_urls == 0)
> +    {
> +      rc = -ENOSYS;
> +      goto out1;
> +    }
> +
>    int retry_limit = default_retry_limit;
>    const char* retry_limit_envvar = getenv(DEBUGINFOD_RETRY_LIMIT_ENV_VAR);
>    if (retry_limit_envvar != NULL)
> @@ -1334,7 +1766,11 @@ debuginfod_query_server (debuginfod_client *c,
>        if ((server_url = server_url_list[i]) == NULL)
>          break;
>        if (vfd >= 0)
> -       dprintf (vfd, "init server %d %s\n", i, server_url);
> +#ifdef ENABLE_IMA_VERIFICATION
> +        dprintf (vfd, "init server %d %s [IMA verification policy: %s]\n", i, server_url, ima_policy_enum2str(url_ima_policies[i]));
> +#else
> +        dprintf (vfd, "init server %d %s\n", i, server_url);
> +#endif
>
>        data[i].fd = fd;
>        data[i].target_handle = &target_handle;
> @@ -1784,6 +2220,29 @@ debuginfod_query_server (debuginfod_client *c,
>    /* PR31248: lseek back to beginning */
>    (void) lseek(fd, 0, SEEK_SET);
>
> +  if(NULL != url_ima_policies && ignore != url_ima_policies[committed_to])
> +    {
> +#ifdef ENABLE_IMA_VERIFICATION
> +      int result = debuginfod_validate_imasig(c, fd);
> +#else
> +      int result = -ENOSYS;
> +#endif
> +      if(0 == result)
> +        {
> +          if (vfd >= 0) dprintf (vfd, "valid signature\n");
> +        }
> +      else if (enforcing == url_ima_policies[committed_to])
> +        {
> +          // All invalid signatures are rejected.
> +          // Additionally in enforcing mode any non-valid signature is rejected, so by reaching
> +          // this case we do so since we know it is not valid. Note - this not just invalid signatures
> +          // but also signatures that cannot be validated
> +          if (vfd >= 0) dprintf (vfd, "error: invalid or missing signature (%d)\n", result);
> +          rc = result;
> +          goto out2;
> +        }
> +    }
> +
>    /* rename tmp->real */
>    rc = rename (target_cache_tmppath, target_cache_path);
>    if (rc < 0)
> @@ -1804,6 +2263,7 @@ debuginfod_query_server (debuginfod_client *c,
>    for (int i = 0; i < num_urls; ++i)
>      free(server_url_list[i]);
>    free(server_url_list);
> +  free(url_ima_policies);
>    free (data);
>    free (server_urls);
>
> @@ -1837,6 +2297,7 @@ debuginfod_query_server (debuginfod_client *c,
>    for (int i = 0; i < num_urls; ++i)
>      free(server_url_list[i]);
>    free(server_url_list);
> +  free(url_ima_policies);
>
>   out0:
>    free (server_urls);
> @@ -1869,7 +2330,11 @@ debuginfod_query_server (debuginfod_client *c,
>    free (cache_miss_path);
>    free (target_cache_dir);
>    free (target_cache_path);
> +  if (rc < 0 && target_cache_tmppath != NULL)
> +    (void)unlink (target_cache_tmppath);
>    free (target_cache_tmppath);
> +
> +
>    return rc;
>  }
>
> @@ -1901,6 +2366,10 @@ debuginfod_begin (void)
>         goto out1;
>      }
>
> +#ifdef ENABLE_IMA_VERIFICATION
> +  load_ima_public_keys (client);
> +#endif
> +
>    // extra future initialization
>
>    goto out;
> @@ -1948,6 +2417,9 @@ debuginfod_end (debuginfod_client *client)
>    curl_slist_free_all (client->headers);
>    free (client->winning_headers);
>    free (client->url);
> +#ifdef ENABLE_IMA_VERIFICATION
> +  free_ima_public_keys (client);
> +#endif
>    free (client);
>  }
>
> @@ -1987,9 +2459,11 @@ debuginfod_find_section (debuginfod_client *client,
>  {
>    int rc = debuginfod_query_server(client, build_id, build_id_len,
>                                    "section", section, path);
> -  if (rc != -EINVAL)
> +  if (rc != -EINVAL && rc != -ENOSYS)
>      return rc;
> -
> +  /* NB: we fall through in case of ima:enforcing-filtered DEBUGINFOD_URLS servers,
> +     so we can download the entire file, verify it locally, then slice it. */
> +
>    /* The servers may have lacked support for section queries.  Attempt to
>       download the debuginfo or executable containing the section in order
>       to extract it.  */
> diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx
> index ece5031f02f9..d9259ad26bb8 100644
> --- a/debuginfod/debuginfod.cxx
> +++ b/debuginfod/debuginfod.cxx
> @@ -122,6 +122,13 @@ using namespace std;
>  #define MHD_RESULT int
>  #endif
>
> +#ifdef ENABLE_IMA_VERIFICATION
> +  #include <rpm/rpmlib.h>
> +  #include <rpm/rpmfi.h>
> +  #include <rpm/header.h>
> +  #include <glob.h>
> +#endif
> +
>  #include <curl/curl.h>
>  #include <archive.h>
>  #include <archive_entry.h>
> @@ -443,6 +450,10 @@ static const struct argp_option options[] =
>     { "disable-source-scan", ARGP_KEY_DISABLE_SOURCE_SCAN, NULL, 0, "Do not scan dwarf source info.", 0 },
>  #define ARGP_SCAN_CHECKPOINT 0x100A
>     { "scan-checkpoint", ARGP_SCAN_CHECKPOINT, "NUM", 0, "Number of files scanned before a WAL checkpoint.", 0 },
> +#ifdef ENABLE_IMA_VERIFICATION
> +#define ARGP_KEY_KOJI_SIGCACHE 0x100B
> +   { "koji-sigcache", ARGP_KEY_KOJI_SIGCACHE, NULL, 0, "Do a koji specific mapping of rpm paths to get IMA signatures.", 0 },
> +#endif
>     { NULL, 0, NULL, 0, NULL, 0 },
>    };
>
> @@ -495,6 +506,9 @@ static bool scan_source_info = true;
>  static string tmpdir;
>  static bool passive_p = false;
>  static long scan_checkpoint = 256;
> +#ifdef ENABLE_IMA_VERIFICATION
> +static bool requires_koji_sigcache_mapping = false;
> +#endif
>
>  static void set_metric(const string& key, double value);
>  static void inc_metric(const string& key);
> @@ -699,6 +713,11 @@ parse_opt (int key, char *arg,
>        if (scan_checkpoint < 0)
>          argp_failure(state, 1, EINVAL, "scan checkpoint");
>        break;
> +#ifdef ENABLE_IMA_VERIFICATION
> +    case ARGP_KEY_KOJI_SIGCACHE:
> +      requires_koji_sigcache_mapping = true;
> +      break;
> +#endif
>        // case 'h': argp_state_help (state, stderr, ARGP_HELP_LONG|ARGP_HELP_EXIT_OK);
>      default: return ARGP_ERR_UNKNOWN;
>      }
> @@ -1959,6 +1978,145 @@ handle_buildid_r_match (bool internal_req_p,
>        return 0;
>      }
>
> +  // Extract the IMA per-file signature (if it exists)
> +  string ima_sig = "";
> +  #ifdef ENABLE_IMA_VERIFICATION
> +  do
> +    {
> +      FD_t rpm_fd;
> +      if(!(rpm_fd = Fopen(b_source0.c_str(), "r.ufdio"))) // read, uncompressed, rpm/rpmio.h
> +        {
> +          if (verbose) obatched(clog) << "There was an error while opening " << b_source0 << endl;
> +          break; // Exit IMA extraction
> +        }
> +
> +      Header rpm_hdr;
> +      if(RPMRC_FAIL == rpmReadPackageFile(NULL, rpm_fd, b_source0.c_str(), &rpm_hdr))
> +        {
> +          if (verbose) obatched(clog) << "There was an error while reading the header of " << b_source0 << endl;
> +          Fclose(rpm_fd);
> +          break; // Exit IMA extraction
> +        }
> +
> +      // Fill sig_tag_data with an alloc'd copy of the array of IMA signatures (if they exist)
> +      struct rpmtd_s sig_tag_data;
> +      rpmtdReset(&sig_tag_data);
> +      do{ /* A do-while so we can break out of the koji sigcache checking on failure */
> +        if(requires_koji_sigcache_mapping)
> +          {
> +            /* NB: Koji builds result in a directory structure like the following
> +               - PACKAGE/VERSION/RELEASE
> +               - ARCH1
> +               - foo.rpm           // The rpm known by debuginfod
> +               - ...
> +               - ARCHN
> +               - data
> +               - signed            // Periodically purged (and not scanned by debuginfod)
> +               - sigcache
> +               - ARCH1
> +               - foo.rpm.sig   // An empty rpm header
> +               - ...
> +               - ARCHN
> +               - PACKAGE_KEYID1
> +               - ARCH1
> +               - foo.rpm.sig   // The header of the signed rpm. This is the file we need to extract the IMA signatures
> +               - ...
> +               - ARCHN
> +               - ...
> +               - PACKAGE_KEYIDn
> +
> +               We therefore need to do a mapping:
> +
> +               P/V/R/A/N-V-R.A.rpm ->
> +               P/V/R/data/sigcache/KEYID/A/N-V-R.A.rpm.sig
> +
> +               There are 2 key insights here
> +
> +               1. We need to go 2 directories down from sigcache to get to the
> +               rpm header. So to distinguish ARCH1/foo.rpm.sig and
> +               PACKAGE_KEYID1/ARCH1/foo.rpm.sig we can look 2 directories down
> +
> +               2. It's safe to assume that the user will have all of the
> +               required verification certs. So we can pick from any of the
> +               PACKAGE_KEYID* directories.  For simplicity we choose first we
> +               match against
> +
> +               See: https://pagure.io/koji/issue/3670
> +            */
> +
> +            // Do the mapping from b_source0 to the koji path for the signed rpm header
> +            string signed_rpm_path = b_source0;
> +            size_t insert_pos = string::npos;
> +            for(int i = 0; i < 2; i++) insert_pos = signed_rpm_path.rfind("/", insert_pos) - 1;
> +            string globbed_path  = signed_rpm_path.insert(insert_pos + 1, "/data/sigcache/*").append(".sig"); // The globbed path we're seeking
> +            glob_t pglob;
> +            int grc;
> +            if(0 != (grc = glob(globbed_path.c_str(), GLOB_NOSORT, NULL, &pglob)))
> +              {
> +                // Break out, but only report real errors
> +                if (verbose && grc != GLOB_NOMATCH) obatched(clog) << "There was an error (" << strerror(errno) << ") globbing " << globbed_path << endl;
> +                break; // Exit koji sigcache check
> +              }
> +            signed_rpm_path = pglob.gl_pathv[0]; // See insight 2 above
> +            globfree(&pglob);
> +
> +            if (verbose > 2) obatched(clog) << "attempting IMA signature extraction from koji header " << signed_rpm_path << endl;
> +
> +            FD_t sig_rpm_fd;
> +            if(NULL == (sig_rpm_fd = Fopen(signed_rpm_path.c_str(), "r")))
> +              {
> +                if (verbose) obatched(clog) << "There was an error while opening " << signed_rpm_path << endl;
> +                break; // Exit koji sigcache check
> +              }
> +
> +            Header sig_hdr = headerRead(sig_rpm_fd, HEADER_MAGIC_YES /* Validate magic too */ );
> +            if (!sig_hdr || 1 != headerGet(sig_hdr, RPMSIGTAG_FILESIGNATURES, &sig_tag_data, HEADERGET_ALLOC))
> +              {
> +                if (verbose) obatched(clog) << "Unable to extract RPMSIGTAG_FILESIGNATURES from " << signed_rpm_path << endl;
> +              }
> +            headerFree(sig_hdr); // We can free here since sig_tag_data has an alloc'd copy of the data
> +            Fclose(sig_rpm_fd);
> +          }
> +      }while(false);
> +
> +      if(0 == sig_tag_data.count)
> +        {
> +          // In the general case (or a fallback from the koji sigcache mapping not finding signatures)
> +          // we can just (try) extract the signatures from the rpm header
> +          if (1 != headerGet(rpm_hdr, RPMTAG_FILESIGNATURES, &sig_tag_data, HEADERGET_ALLOC))
> +            {
> +              if (verbose) obatched(clog) << "Unable to extract RPMTAG_FILESIGNATURES from " << b_source0 << endl;
> +            }
> +        }
> +      // Search the array for the signature coresponding to b_source1
> +      int idx = -1;
> +      char *sig = NULL;
> +      rpmfi hdr_fi = rpmfiNew(NULL, rpm_hdr, RPMTAG_BASENAMES, RPMFI_FLAGS_QUERY);
> +      do
> +        {
> +          sig = (char*)rpmtdNextString(&sig_tag_data);
> +          idx = rpmfiNext(hdr_fi);
> +        }
> +      while (idx != -1 && 0 != strcmp(b_source1.c_str(), rpmfiFN(hdr_fi)));
> +      rpmfiFree(hdr_fi);
> +
> +      if(sig && 0 != strlen(sig) && idx != -1)
> +        {
> +          if (verbose > 2) obatched(clog) << "Found IMA signature for " << b_source1 << ":\n" << sig << endl;
> +          ima_sig = sig;
> +          inc_metric("http_responses_total","extra","ima-sigs-extracted");
> +        }
> +      else
> +        {
> +          if (verbose > 2) obatched(clog) << "Could not find IMA signature for " << b_source1 << endl;
> +        }
> +
> +      rpmtdFreeData (&sig_tag_data);
> +      headerFree(rpm_hdr);
> +      Fclose(rpm_fd);
> +    } while(false);
> +  #endif
> +
>    // check for a match in the fdcache first
>    int fd = fdcache.lookup(b_source0, b_source1);
>    while (fd >= 0) // got one!; NB: this is really an if() with a possible branch out to the end
> @@ -2016,11 +2174,13 @@ handle_buildid_r_match (bool internal_req_p,
>                                to_string(fs.st_size).c_str());
>        add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE", b_source0.c_str());
>        add_mhd_response_header (r, "X-DEBUGINFOD-FILE", b_source1.c_str());
> +      if(!ima_sig.empty()) add_mhd_response_header(r, "X-DEBUGINFOD-IMASIGNATURE", ima_sig.c_str());
>        add_mhd_last_modified (r, fs.st_mtime);
>        if (verbose > 1)
>         obatched(clog) << "serving fdcache archive " << b_source0
>                        << " file " << b_source1
> -                      << " section=" << section << endl;
> +                      << " section=" << section
> +                      << " IMA signature=" << ima_sig << endl;
>        /* libmicrohttpd will close it. */
>        if (result_fd)
>          *result_fd = fd;
> @@ -2204,11 +2364,13 @@ handle_buildid_r_match (bool internal_req_p,
>                                     to_string(archive_entry_size(e)).c_str());
>            add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE", b_source0.c_str());
>            add_mhd_response_header (r, "X-DEBUGINFOD-FILE", b_source1.c_str());
> +          if(!ima_sig.empty()) add_mhd_response_header(r, "X-DEBUGINFOD-IMASIGNATURE", ima_sig.c_str());
>            add_mhd_last_modified (r, archive_entry_mtime(e));
>            if (verbose > 1)
>             obatched(clog) << "serving archive " << b_source0
>                            << " file " << b_source1
> -                          << " section=" << section << endl;
> +                          << " section=" << section
> +                          << " IMA signature=" << ima_sig << endl;
>            /* libmicrohttpd will close it. */
>            if (result_fd)
>              *result_fd = fd;
> diff --git a/debuginfod/debuginfod.h.in b/debuginfod/debuginfod.h.in
> index 4a256ba9af1f..73f633f0b8e9 100644
> --- a/debuginfod/debuginfod.h.in
> +++ b/debuginfod/debuginfod.h.in
> @@ -39,6 +39,7 @@
>  #define DEBUGINFOD_MAXSIZE_ENV_VAR "DEBUGINFOD_MAXSIZE"
>  #define DEBUGINFOD_MAXTIME_ENV_VAR "DEBUGINFOD_MAXTIME"
>  #define DEBUGINFOD_HEADERS_FILE_ENV_VAR "DEBUGINFOD_HEADERS_FILE"
> +#define DEBUGINFOD_IMA_CERT_PATH_ENV_VAR "DEBUGINFOD_IMA_CERT_PATH"
>
>  /* The libdebuginfod soname.  */
>  #define DEBUGINFOD_SONAME "@LIBDEBUGINFOD_SONAME@"
> diff --git a/doc/debuginfod-client-config.7 b/doc/debuginfod-client-config.7
> index 53d82806d395..f16612084e9b 100644
> --- a/doc/debuginfod-client-config.7
> +++ b/doc/debuginfod-client-config.7
> @@ -27,6 +27,33 @@ debuginfod instances.  Alternate URL prefixes are separated by space.
>  This environment variable may be set by /etc/profile.d scripts
>  reading /etc/debuginfod/*.urls files.
>
> +This environment variable can also contain policy defining tags which
> +dictate the response policy for verifying per-file IMA signatures in
> +RPMs.  As the space seperated list is read left to right, upon
> +encountering a tag, subsequent URLs up to the next tag will be handled
> +using that specified policy.  All URLs before the first tag will use
> +the default policy, \fIima:ignore\fP.  For example:
> +
> +.in +4n
> +.EX
> +DEBUGINFOD_URLS="https://foo.com ima:enforcing https://bar.ca http://localhost:8002/ ima:ignore https://baz.org"
> +.EE
> +.in
> +
> +Where foo.com and baz.org use the default \fIignore\fP policy and
> +bar.ca and localhost use an \fIenforcing\fP policy.  The policy tag
> +may be one of the following:
> +.IP
> +\fIima:enforcing\fP Every downloaded file requires a valid signature,
> +fully protecting integrity.
> +.IP
> +\fIima:ignore\fP Skips verification altogether, providing no
> +protection.
> +.IP
> +
> +Alerts of validation failure will be directed as specified
> +in $DEBUGINFOD_VERBOSE.
> +
>  .TP
>  .B $DEBUGINFOD_CACHE_PATH
>  This environment variable governs the location of the cache where
> @@ -82,6 +109,12 @@ outbound HTTP requests, one per line. The header lines shouldn't end with
>  CRLF, unless that's the system newline convention. Whitespace-only lines
>  are skipped.
>
> +.TP
> +.B $DEBUGINFOD_IMA_CERT_PATH
> +This environment variable contains a list of absolute directory paths
> +holding X.509 certificates for RPM per-file IMA-verification.
> +Alternate paths are separated by colons.
> +
>  .SH CACHE
>
>  Before each query, the debuginfod client library checks for a need to
> diff --git a/doc/debuginfod-find.1 b/doc/debuginfod-find.1
> index 7d577babeb89..d7db1bfdd838 100644
> --- a/doc/debuginfod-find.1
> +++ b/doc/debuginfod-find.1
> @@ -129,10 +129,18 @@ and printing the http response headers from the server.
>
>  .SH "SECURITY"
>
> -debuginfod-find \fBdoes not\fP include any particular security
> -features.  It trusts that the binaries returned by the debuginfod(s)
> -are accurate.  Therefore, the list of servers should include only
> -trustworthy ones.  If accessed across HTTP rather than HTTPS, the
> +If IMA signature(s) are available from the RPMs that contain
> +requested files, then
> +.BR debuginfod
> +will extract those signatures into response headers, and
> +.BR debuginfod-find
> +will perform verification upon the files.
> +Validation policy is controlled via tags inserted into
> +$DEBUGINFOD_URLS.  By default,
> +.BR debuginfod-find
> +acts in ignore mode.
> +
> +If accessed across HTTP rather than HTTPS, the
>  network should be trustworthy.  Authentication information through
>  the internal \fIlibcurl\fP library is not currently enabled, except
>  for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style.
> diff --git a/doc/debuginfod.8 b/doc/debuginfod.8
> index 42e0fc9fbb34..577f58b6ee2e 100644
> --- a/doc/debuginfod.8
> +++ b/doc/debuginfod.8
> @@ -285,6 +285,14 @@ completed archive or file scans.  This may slow down parallel scanning
>  phase somewhat, but generate much smaller "-wal" temporary files on
>  busy servers.  The default is 256.  Disabled if 0.
>
> +.TP
> +.B "\-\-koji\-sigcache"
> +Enable an additional step of RPM path mapping when extracting signatures for use
> +in RPM per-file IMA verification on koji repositories. The signatures are retrieved
> +from the Fedora koji sigcache rpm.sig files as opposed to the original RPM header.
> +If a signature cannot be found in the sigcache rpm.sig file, the RPM will be
> +tried as a fallback.
> +
>  .TP
>  .B "\-v"
>  Increase verbosity of logging to the standard error file descriptor.
> @@ -300,8 +308,15 @@ Unknown buildid / request combinations result in HTTP error codes.
>  This file service resemblance is intentional, so that an installation
>  can take advantage of standard HTTP management infrastructure.
>
> -For most queries, some custom http headers are added to the response,
> -providing additional metadata about the buildid-related response.  For example:
> +Upon finding a file in an archive or simply in the database, some
> +custom http headers are added to the response. For files in the
> +database X-DEBUGINFOD-FILE and X-DEBUGINFOD-SIZE are added.
> +X-DEBUGINFOD-FILE is simply the unescaped filename and
> +X-DEBUGINFOD-SIZE is the size of the file. For files found in archives,
> +in addition to X-DEBUGINFOD-FILE and X-DEBUGINFOD-SIZE,
> +X-DEBUGINFOD-ARCHIVE is added.  X-DEBUGINFOD-ARCHIVE is the name of the
> +archive the file was found in.  X-DEBUGINFOD-IMA-SIGNATURE contains the
> +per-file IMA signature as a hexadecimal blob.
>
>  .SAMPLE
>  % debuginfod-find -v debuginfo /bin/ls |& grep -i x-debuginfo
> diff --git a/doc/debuginfod_find_debuginfo.3 b/doc/debuginfod_find_debuginfo.3
> index 0d553665f42b..4e359c8c4bd4 100644
> --- a/doc/debuginfod_find_debuginfo.3
> +++ b/doc/debuginfod_find_debuginfo.3
> @@ -251,13 +251,21 @@ void *debuginfod_so = dlopen(DEBUGINFOD_SONAME, RTLD_LAZY);
>  .in
>
>  .SH "SECURITY"
> +
> +If IMA signature(s) are available from the RPMs that contain
> +requested files, then
> +.BR debuginfod
> +will extract those signatures into response headers, and
> +.BR debuginfod_find_* ()
> +will perform verification upon the files.
> +Validation policy is controlled via tags inserted into
> +$DEBUGINFOD_URLS.  By default,
>  .BR debuginfod_find_* ()
> -functions \fBdo not\fP include any particular security
> -features.  They trust that the binaries returned by the debuginfod(s)
> -are accurate.  Therefore, the list of servers should include only
> -trustworthy ones.  If accessed across HTTP rather than HTTPS, the
> -network should be trustworthy.  Passing user authentication information
> -through the internal \fIlibcurl\fP library is not currently enabled, except
> +acts in ignore mode.
> +
> +If accessed across HTTP rather than HTTPS, the
> +network should be trustworthy.  Authentication information through
> +the internal \fIlibcurl\fP library is not currently enabled, except
>  for the basic plaintext \%\fIhttp[s]://userid:password@hostname/\fP style.
>  (The debuginfod server does not perform authentication, but a front-end
>  proxy server could.)
> @@ -325,6 +333,18 @@ Query failed due to timeout. \fB$DEBUGINFOD_TIMEOUT\fP and
>  Query aborted due to the file requested being too big.  The
>  \fB$DEBUGINFOD_MAXSIZE\fP controls this.
>
> +.TP
> +.BR EBADMSG
> +File content failed IMA verification against a known signer certificate.
> +
> +.TP
> +.BR ENOKEY
> +File content failed IMA verification due to missing signer certificate.
> +
> +.TP
> +.BR ENODATA
> +File content failed IMA verification because of a missing signature.
> +
>  .nr zZ 1
>  .so man7/debuginfod-client-config.7
>
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 7aae3d8aa0e5..db071186c533 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -278,6 +278,9 @@ if !OLD_LIBMICROHTTPD
>  # Too many open file descriptors confuses libmicrohttpd < 0.9.51
>  TESTS += run-debuginfod-federation-metrics.sh
>  endif
> +if ENABLE_IMA_VERIFICATION
> +TESTS += run-debuginfod-ima-verification.sh
> +endif
>  endif
>
>  if HAVE_CXX11
> @@ -600,6 +603,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
>               run-debuginfod-webapi-concurrency.sh \
>              run-debuginfod-section.sh \
>              run-debuginfod-IXr.sh \
> +                run-debuginfod-ima-verification.sh \
>              debuginfod-rpms/fedora30/hello2-1.0-2.src.rpm \
>              debuginfod-rpms/fedora30/hello2-1.0-2.x86_64.rpm \
>              debuginfod-rpms/fedora30/hello2-debuginfo-1.0-2.x86_64.rpm \
> @@ -623,6 +627,11 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
>              debuginfod-rpms/rhel7/hello2-debuginfo-1.0-2.x86_64.rpm \
>              debuginfod-rpms/rhel7/hello2-two-1.0-2.x86_64.rpm \
>              debuginfod-rpms/rhel7/hello2-two-1.0-2.x86_64.rpm \
> +             debuginfod-ima/koji/arch/hello-2.10-9.fc38.x86_64.rpm \
> +             debuginfod-ima/koji/data/sigcache/keyid/arch/hello-2.10-9.fc38.x86_64.rpm.sig \
> +            debuginfod-ima/koji/fedora-38-ima.pem \
> +            debuginfod-ima/rhel9/hello2-1.0-1.x86_64.rpm \
> +            debuginfod-ima/rhel9/imacert.der \
>              debuginfod-debs/hithere-dbgsym_1.0-1_amd64.ddeb \
>              debuginfod-debs/hithere_1.0-1.debian.tar.xz \
>              debuginfod-debs/hithere_1.0-1.dsc \
> diff --git a/tests/debuginfod-ima/koji/arch/hello-2.10-9.fc38.x86_64.rpm b/tests/debuginfod-ima/koji/arch/hello-2.10-9.fc38.x86_64.rpm
> new file mode 100644
> index 000000000000..b04ad8c2af39
> Binary files /dev/null and b/tests/debuginfod-ima/koji/arch/hello-2.10-9.fc38.x86_64.rpm differ
> diff --git a/tests/debuginfod-ima/koji/data/sigcache/keyid/arch/hello-2.10-9.fc38.x86_64.rpm.sig b/tests/debuginfod-ima/koji/data/sigcache/keyid/arch/hello-2.10-9.fc38.x86_64.rpm.sig
> new file mode 100644
> index 000000000000..ee7eb8e467b4
> Binary files /dev/null and b/tests/debuginfod-ima/koji/data/sigcache/keyid/arch/hello-2.10-9.fc38.x86_64.rpm.sig differ
> diff --git a/tests/debuginfod-ima/koji/fedora-38-ima.pem b/tests/debuginfod-ima/koji/fedora-38-ima.pem
> new file mode 100644
> index 000000000000..e323fa24a6fd
> --- /dev/null
> +++ b/tests/debuginfod-ima/koji/fedora-38-ima.pem
> @@ -0,0 +1,4 @@
> +-----BEGIN PUBLIC KEY-----
> +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj5EVzjUa4PW3I3Y/RTkLgfjP3Elu
> +4AyKdXXxIldW6VVi3QMEpP5eZ7lZmlB2892QFpbWMLNJ4jXlPehMgqNgvg==
> +-----END PUBLIC KEY-----
> diff --git a/tests/debuginfod-ima/rhel9/hello2-1.0-1.x86_64.rpm b/tests/debuginfod-ima/rhel9/hello2-1.0-1.x86_64.rpm
> new file mode 100644
> index 000000000000..0262ae2f0c4c
> Binary files /dev/null and b/tests/debuginfod-ima/rhel9/hello2-1.0-1.x86_64.rpm differ
> diff --git a/tests/debuginfod-ima/rhel9/imacert.der b/tests/debuginfod-ima/rhel9/imacert.der
> new file mode 100644
> index 000000000000..b0250b6c30d5
> Binary files /dev/null and b/tests/debuginfod-ima/rhel9/imacert.der differ
> diff --git a/tests/run-debuginfod-ima-verification.sh b/tests/run-debuginfod-ima-verification.sh
> new file mode 100755
> index 000000000000..d582af5f6a9d
> --- /dev/null
> +++ b/tests/run-debuginfod-ima-verification.sh
> @@ -0,0 +1,181 @@
> +#!/usr/bin/env bash
> +#
> +# Copyright (C) 2023-2024 Red Hat, Inc.
> +# This file is part of elfutils.
> +#
> +# This file is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# elfutils is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +. $srcdir/debuginfod-subr.sh
> +
> +type rpmsign 2>/dev/null || { echo "need rpmsign"; exit 77; }
> +cat << EoF > include.c
> +#include <rpm/rpmlib.h>
> +#include <rpm/rpmfi.h>
> +#include <rpm/header.h>
> +#include <imaevm.h>
> +#include <openssl/evp.h>
> +EoF
> +tempfiles include.c
> +gcc -H -fsyntax-only include.c 2> /dev/null || { echo "one or more devel packages are missing (rpm-devel, ima-evm-utils-devel, openssl-devel)"; exit 77; }
> +
> +set -x
> +export DEBUGINFOD_VERBOSE=1
> +
> +DB=${PWD}/.debuginfod_tmp.sqlite
> +tempfiles $DB
> +export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache
> +IMA_POLICY="enforcing"
> +
> +# This variable is essential and ensures no time-race for claiming ports occurs
> +# set base to a unique multiple of 100 not used in any other 'run-debuginfod-*' test
> +base=14000
> +get_ports
> +mkdir R
> +env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod $VERBOSE -R \
> +    -d $DB -p $PORT1 -t0 -g0 R > vlog$PORT1 2>&1 &
> +PID1=$!
> +tempfiles vlog$PORT1
> +errfiles vlog$PORT1
> +
> +########################################################################
> +cp -pv ${abs_srcdir}/debuginfod-ima/rhel9/hello2-1.0-1.x86_64.rpm signed.rpm
> +tempfiles signed.rpm
> +RPM_BUILDID=460912dbc989106ec7325d243384df20c5ccec0c # /usr/local/bin/hello
> +
> +MIN_IMAEVM_MAJ_VERSION=3
> +MIN_RPM_MAJ_VERSION=4
> +# If the correct programs (and versions) exist sign the rpm in the test
> +if  false && \
> +    (command -v openssl &> /dev/null) && \
> +    (command -v rpmsign &> /dev/null) && \
> +    (command -v gpg &> /dev/null) && \
> +    [ $(ldd `which rpmsign` | grep libimaevm | awk -F'[^0-9]+' '{ print $2 }') -ge $MIN_IMAEVM_MAJ_VERSION ] && \
> +    [ $(rpm --version | awk -F'[^0-9]+' '{ print $2 }') -ge $MIN_RPM_MAJ_VERSION ]
> +then
> +    # SIGN THE RPM
> +    # First remove any old signatures
> +    rpmsign --delsign signed.rpm &> /dev/null
> +    rpmsign --delfilesign signed.rpm &> /dev/null
> +
> +    # Make a gpg keypair (with $PWD as the homedir)
> +    mkdir -m 700 openpgp-revocs.d private-keys-v1.d
> +    gpg --quick-gen-key --yes --homedir ${PWD} --batch --passphrase '' --no-default-keyring --keyring "${PWD}/pubring.kbx" example@elfutils.org 2> /dev/null
> +
> +    # Create a private DER signing key and a public X509 DER format verification key pair
> +    openssl genrsa | openssl pkcs8 -topk8 -nocrypt -outform PEM -out signing.pem
> +    openssl req -x509 -key signing.pem -out imacert.pem -days 365 -keyform PEM \
> +        -subj "/C=CA/ST=ON/L=TO/O=Elfutils/CN=www.sourceware.org\/elfutils"
> +
> +    tempfiles openpgp-revocs.d/* private-keys-v1.d/* * openpgp-revocs.d private-keys-v1.d
> +
> +    rpmsign --addsign --signfiles --fskpath=signing.pem -D "_gpg_name example@elfutils.org" -D "_gpg_path ${PWD}" signed.rpm
> +    cp signed.rpm R/signed.rpm
> +    VERIFICATION_CERT_DIR=${PWD}
> +
> +    # Cleanup
> +    rm -rf openpgp-revocs.d private-keys-v1.d
> +else
> +    # USE A PRESIGNED RPM
> +    cp signed.rpm R/signed.rpm
> +    # Note we test with no trailing /
> +    VERIFICATION_CERT_DIR=${abs_srcdir}/debuginfod-ima/rhel9
> +fi
> +
> +########################################################################
> +# Server must become ready with R fully scanned and indexed
> +wait_ready $PORT1 'ready' 1
> +wait_ready $PORT1 'thread_work_total{role="traverse"}' 1
> +wait_ready $PORT1 'thread_work_pending{role="scan"}' 0
> +wait_ready $PORT1 'thread_busy{role="scan"}' 0
> +
> +export DEBUGINFOD_URLS="ima:$IMA_POLICY http://127.0.0.1:$PORT1"
> +
> +echo Test 1: Without a certificate the verification should fail
> +export DEBUGINFOD_IMA_CERT_PATH=
> +RC=0
> +testrun ${abs_top_builddir}/debuginfod/debuginfod-find -vv executable $RPM_BUILDID || RC=1
> +test $RC -ne 0
> +
> +echo Test 2: It should pass once the certificate is added to the path
> +export DEBUGINFOD_IMA_CERT_PATH=$VERIFICATION_CERT_DIR
> +rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
> +kill -USR1 $PID1
> +wait_ready $PORT1 'thread_work_total{role="traverse"}' 2
> +wait_ready $PORT1 'thread_work_pending{role="scan"}' 0
> +wait_ready $PORT1 'thread_busy{role="scan"}' 0
> +testrun ${abs_top_builddir}/debuginfod/debuginfod-find -vv executable $RPM_BUILDID
> +
> +echo Test 3: Corrupt the data and it should fail
> +dd if=/dev/zero of=R/signed.rpm bs=1 count=128 seek=1024 conv=notrunc
> +rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
> +kill -USR1 $PID1
> +wait_ready $PORT1 'thread_work_total{role="traverse"}' 3
> +wait_ready $PORT1 'thread_work_pending{role="scan"}' 0
> +wait_ready $PORT1 'thread_busy{role="scan"}' 0
> +RC=0
> +testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $RPM_BUILDID || RC=1
> +test $RC -ne 0
> +
> +echo Test 4: A rpm without a signature will fail
> +cp signed.rpm R/signed.rpm
> +rpmsign --delfilesign R/signed.rpm
> +rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
> +kill -USR1 $PID1
> +wait_ready $PORT1 'thread_work_total{role="traverse"}' 4
> +wait_ready $PORT1 'thread_work_pending{role="scan"}' 0
> +wait_ready $PORT1 'thread_busy{role="scan"}' 0
> +RC=0
> +testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $RPM_BUILDID || RC=1
> +test $RC -ne 0
> +
> +echo Test 5: Only tests 1,2 will result in extracted signature
> +[[ $(curl -s http://127.0.0.1:$PORT1/metrics | grep 'http_responses_total{extra="ima-sigs-extracted"}' | awk '{print $NF}') -eq 2 ]]
> +
> +kill $PID1
> +wait $PID1
> +PID1=0
> +
> +#######################################################################
> +# We also test the --koji-sigcache
> +cp -pR ${abs_srcdir}/debuginfod-ima/koji R/koji
> +
> +rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
> +env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod $VERBOSE -R \
> +    -d $DB -p $PORT2 -t0 -g0 -X /data/ --koji-sigcache R/koji > vlog$PORT1 2>&1 &
> +#reuse PID1
> +PID1=$!
> +tempfiles vlog$PORT2
> +errfiles vlog$PORT2
> +
> +RPM_BUILDID=c592a95e45625d7891b90f6b86e63373d540461d #/usr/bin/hello
> +# Note we test with a trailing slash
> +VERIFICATION_CERT_DIR=/not/a/dir:${abs_srcdir}/debuginfod-ima/koji/
> +
> +########################################################################
> +# Server must become ready with koji fully scanned and indexed
> +wait_ready $PORT2 'ready' 1
> +wait_ready $PORT2 'thread_work_total{role="traverse"}' 1
> +wait_ready $PORT2 'thread_work_pending{role="scan"}' 0
> +wait_ready $PORT2 'thread_busy{role="scan"}' 0
> +
> +echo Test 6: The path should be properly mapped and verified using the actual fedora 38 cert
> +export DEBUGINFOD_URLS="ima:$IMA_POLICY http://127.0.0.1:$PORT2"
> +export DEBUGINFOD_IMA_CERT_PATH=$VERIFICATION_CERT_DIR
> +testrun ${abs_top_builddir}/debuginfod/debuginfod-find -vv executable $RPM_BUILDID
> +
> +kill $PID1
> +wait $PID1
> +PID1=0
> +
> +exit 0
>

Aaron

[-- Attachment #2: fish-ima.diff --]
[-- Type: text/x-patch, Size: 1172 bytes --]

diff --git a/config/profile.fish.in b/config/profile.fish.in
index c0a234db..12b5e6f1 100644
--- a/config/profile.fish.in
+++ b/config/profile.fish.in
@@ -4,12 +4,21 @@
 # See also [man debuginfod-client-config] for other environment variables
 # such as $DEBUGINFOD_MAXSIZE, $DEBUGINFOD_MAXTIME, $DEBUGINFOD_PROGRESS.
 
+# Use local variables so we don't need to manually unset them
+set --local prefix "@prefix@"
+
 if not set --query DEBUGINFOD_URLS
-    # Use local variables so we don't need to manually unset them
-    set --local prefix "@prefix@"
     set --local files "@sysconfdir@/debuginfod/"*.urls
     set --local DEBUGINFOD_URLS (cat /dev/null $files 2>/dev/null | string replace '\n' ' ')
     if test -n "$DEBUGINFOD_URLS"
         set --global --export DEBUGINFOD_URLS "$DEBUGINFOD_URLS"
     end
 end
+
+if not set --query DEBUGINFOD_IMA_CERT_PATH
+    set --local files "@sysconfdir@/debuginfod/"*.certpath
+    set --local DEBUGINFOD_IMA_CERT_PATH (cat /dev/null $files 2>/dev/null | string replace '\n' ':')
+    if test -n "$DEBUGINFOD_IMA_CERT_PATH"
+        set --global --export DEBUGINFOD_IMA_CERT_PATH "$DEBUGINFOD_IMA_CERT_PATH"
+    end
+end

  reply	other threads:[~2024-05-09 17:56 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-03 21:04 Frank Ch. Eigler
2024-04-09 12:31 ` Mark Wielaard
2024-04-10 21:01   ` Frank Ch. Eigler
2024-04-11 10:14     ` Mark Wielaard
2024-04-11 14:09       ` Frank Ch. Eigler
2024-04-16 22:15   ` Frank Ch. Eigler
2024-05-05  1:30     ` Frank Ch. Eigler
2024-05-09 17:56       ` Aaron Merey [this message]
2024-05-14 15:18         ` Mark Wielaard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAJDtP-Sq=m0nUwy4B-UsN-mAEp72Hv55HT3vM7VBiJafS4DK7A@mail.gmail.com' \
    --to=amerey@redhat.com \
    --cc=elfutils-devel@sourceware.org \
    --cc=fche@redhat.com \
    --cc=mark@klomp.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).