Hi,

Continued review...

On Thu, 2023-09-07 at 08:55 -0400, Frank Ch. Eigler via Elfutils-devel
wrote:
> diff --git a/debuginfod/debuginfod.cxx b/debuginfod/debuginfod.cxx
> index d72d2ad16960..8c3298586672 100644
> --- a/debuginfod/debuginfod.cxx
> +++ b/debuginfod/debuginfod.cxx
> @@ -113,6 +113,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>

OK.

> @@ -431,6 +438,8 @@ 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 },
> +#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 },
>     { NULL, 0, NULL, 0, NULL, 0 },
>    };

OK. Is actually documented below. But first couldn't find it because of
the escaped '-' (good), which apparently is a real issue:
https://lwn.net/SubscriberLink/947941/b427a4409847108d/

> @@ -486,6 +495,7 @@ static bool scan_source_info = true;
>  static string tmpdir;
>  static bool passive_p = false;
>  static long scan_checkpoint = 256;
> +static bool requires_koji_sigcache_mapping = false;
>  
>  static void set_metric(const string& key, double value);
>  // static void inc_metric(const string& key);
> @@ -692,6 +702,9 @@ parse_opt (int key, char *arg,
>        if (scan_checkpoint < 0)
>          argp_failure(state, 1, EINVAL, "scan checkpoint");        
>        break;
> +    case ARGP_KEY_KOJI_SIGCACHE:
> +      requires_koji_sigcache_mapping = true;
> +      break;
>        // case 'h': argp_state_help (state, stderr, 
> ARGP_HELP_LONG|ARGP_HELP_EXIT_OK);
>      default: return ARGP_ERR_UNKNOWN;
>      }

OK. Maybe produce a warning/error ifndef ENABLE_IMA_VERIFICATION ?

> @@ -1933,6 +1946,131 @@ 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")))
> +    {
> +      if (verbose) obatched(clog) << "There was an error while opening " << 
> b_source0 << endl;
> +      break; // Exit IMA extraction
> +    }

So Fopen is part of rpm devel? Is there documentation?
Specifically what "r.ufdio" means?

> +    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
> +    }

OK.

> +    // 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 of 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. Its safe to assume that the user will have all of the required 
> verification certs. So we can pick from any of the PACKAGE_KEYIDi directories.
> +      For simplicity we choose first we match against
> +      See: https://pagure.io/koji/issue/3670
> +      */

Nice comment, thanks for the bug reference.
Note tiny typo "PACKAGE_KEYIDi" (extra 'i').

> +      // 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;
> +      }
> +    }

OK. A bit convoluted, but that is just rpm being rpm.

> +    // 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);
> +    for(sig = (char*)rpmtdNextString(&sig_tag_data), idx = rpmfiNext(hdr_fi);
> +        idx != -1 && 0 != strcmp(b_source1.c_str(), rpmfiFN(hdr_fi));
> +        sig = (char*)rpmtdNextString(&sig_tag_data), idx = 
> rpmfiNext(hdr_fi));
> +    rpmfiFree(hdr_fi);

That is a complicated for statement, not easily understood. Partially
because of indentation. Advise to at least put ';' on its own line.

> +    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

OK.

BTW. Shouldn't we cache this image_sig somewhere?
It seems a lot of work to do each time handle_buildid_r_match is
called.

>    // 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
> @@ -1990,11 +2128,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;
> @@ -2174,11 +2314,13 @@ handle_buildid_r_match (bool internal_req_p,
>            add_mhd_response_header (r, "X-DEBUGINFOD-ARCHIVE",
>                                     b_source0.c_str());
>            add_mhd_response_header (r, "X-DEBUGINFOD-FILE", file.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;

OK.

> 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@"

OK.

> diff --git a/debuginfod/ima-certs/centosimabeta9.crt 
> b/debuginfod/ima-certs/centosimabeta9.crt
> new file mode 100644
> index 000000000000..534f86a15ac5
> --- /dev/null
> +++ b/debuginfod/ima-certs/centosimabeta9.crt
> @@ -0,0 +1,70 @@
> +Certificate:
> +    Data:
> +        Version: 3 (0x2)
> +        Serial Number:
> +            d6:78:a4:e3:e7:d3:c5:aa
> +    Signature Algorithm: sha256WithRSAEncryption
> +        Issuer: O=RH-IMA-CA, CN=Red Hat IMA 
> CA/emailAddress=secal...@redhat.com
> +        Validity
> +            Not Before: Jul  1 16:14:28 2023 GMT
> +            Not After : Jan 18 16:14:28 2038 GMT
> +        Subject: CN=CentOS IMA beta key (for verification)
> +        Subject Public Key Info:
> +            Public Key Algorithm: id-ecPublicKey
> +                Public-Key: (384 bit)
> +                pub: 
> +                    04:6c:6d:44:6d:eb:55:31:56:11:ab:a7:dc:cc:34:
> +                    72:9c:9b:05:5c:d8:28:36:c8:04:05:5a:c8:33:b5:
> +                    ba:de:ba:f5:0d:a9:1f:02:c1:43:78:e8:0a:2f:05:
> +                    e9:a9:ce:c4:24:5b:d6:b2:ce:cf:06:52:7a:7a:d9:
> +                    3f:dd:b3:09:b1:22:90:e4:95:4e:07:80:f4:9c:73:
> +                    e9:6c:00:09:ed:b1:68:76:c5:59:7e:74:a4:c3:cf:
> +                    a1:84:20:51:f6:29:2e
> +                ASN1 OID: secp384r1
> +                NIST CURVE: P-384
> +        X509v3 extensions:
> +            X509v3 Basic Constraints: critical
> +                CA:FALSE
> +            X509v3 Key Usage: critical
> +                Digital Signature
> +            X509v3 Extended Key Usage: critical
> +                Code Signing
> +            X509v3 Subject Key Identifier: 
> +                61:BA:CE:C0:D7:97:EA:49:9F:D8:92:79:41:57:19:0A:D6:04:63:E1
> +            X509v3 Authority Key Identifier: 
> +                
> keyid:FB:31:82:5D:D0:E0:73:68:5B:26:4E:30:38:96:36:73:F7:53:95:9A
> +
> +    Signature Algorithm: sha256WithRSAEncryption
> +         8b:28:5a:ca:d3:2a:ec:e2:c6:89:f8:4d:e9:ff:c8:31:47:7b:
> +         93:17:c1:55:48:40:ce:be:1c:58:ff:6a:83:55:07:72:70:c7:
> +         a3:25:91:70:90:ab:9d:bc:d3:63:4e:aa:f2:2f:ee:0c:05:a5:
> +         8e:2e:98:7b:41:44:37:66:87:80:94:56:b1:1f:13:99:d0:80:
> +         0b:e8:ec:50:cf:8c:90:53:cb:b1:9c:35:cf:b7:75:1f:ad:77:
> +         52:d2:06:56:7a:37:16:fe:bb:c2:aa:de:72:5d:b3:4b:9f:d3:
> +         05:c6:4a:36:d2:14:6b:9a:b3:64:62:4b:d5:9d:aa:71:ab:11:
> +         c0:08:17:e1:f9:88:49:c7:6f:74:ff:02:04:ee:cd:a2:75:5b:
> +         79:19:ed:ad:14:71:b4:19:56:ed:fe:7b:61:7e:d4:97:b5:c7:
> +         a5:05:41:4c:9f:17:91:71:71:bc:12:2b:d8:6c:2b:c0:02:93:
> +         ed:28:30:9b:7f:a1:93:8c:32:74:fe:6c:c0:ab:68:5e:e5:3e:
> +         22:c9:db:d4:4e:b2:5c:d6:49:e7:1d:91:78:61:3c:ca:5e:72:
> +         63:74:ff:af:0d:0d:32:ae:2a:cd:9b:a1:30:95:ea:33:53:64:
> +         2b:13:2d:96:e3:b6:6d:f2:c2:3c:24:c8:6b:87:f8:b4:7f:72:
> +         30:66:f8:5d
> +-----BEGIN CERTIFICATE-----
> +MIICzzCCAbegAwIBAgIJANZ4pOPn08WqMA0GCSqGSIb3DQEBCwUAMFExEjAQBgNV
> +BAoMCVJILUlNQS1DQTEXMBUGA1UEAwwOUmVkIEhhdCBJTUEgQ0ExIjAgBgkqhkiG
> +9w0BCQEWE3NlY2FsZXJ0QHJlZGhhdC5jb20wHhcNMjMwNzAxMTYxNDI4WhcNMzgw
> +MTE4MTYxNDI4WjAxMS8wLQYDVQQDDCZDZW50T1MgSU1BIGJldGEga2V5IChmb3Ig
> +dmVyaWZpY2F0aW9uKTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGxtRG3rVTFWEaun
> +3Mw0cpybBVzYKDbIBAVayDO1ut669Q2pHwLBQ3joCi8F6anOxCRb1rLOzwZSenrZ
> +P92zCbEikOSVTgeA9Jxz6WwACe2xaHbFWX50pMPPoYQgUfYpLqN4MHYwDAYDVR0T
> +AQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMw
> +HQYDVR0OBBYEFGG6zsDXl+pJn9iSeUFXGQrWBGPhMB8GA1UdIwQYMBaAFPsxgl3Q
> +4HNoWyZOMDiWNnP3U5WaMA0GCSqGSIb3DQEBCwUAA4IBAQCLKFrK0yrs4saJ+E3p
> +/8gxR3uTF8FVSEDOvhxY/2qDVQdycMejJZFwkKudvNNjTqryL+4MBaWOLph7QUQ3
> +ZoeAlFaxHxOZ0IAL6OxQz4yQU8uxnDXPt3UfrXdS0gZWejcW/rvCqt5yXbNLn9MF
> +xko20hRrmrNkYkvVnapxqxHACBfh+YhJx290/wIE7s2idVt5Ge2tFHG0GVbt/nth
> +ftSXtcelBUFMnxeRcXG8EivYbCvAApPtKDCbf6GTjDJ0/mzAq2he5T4iydvUTrJc
> +1knnHZF4YTzKXnJjdP+vDQ0yrirNm6EwleozU2QrEy2W47Zt8sI8JMhrh/i0f3Iw
> +Zvhd
> +-----END CERTIFICATE-----
> diff --git a/debuginfod/ima-certs/centosimarelease9.crt 
> b/debuginfod/ima-certs/centosimarelease9.crt
> new file mode 100644
> index 000000000000..db641453260f
> --- /dev/null
> +++ b/debuginfod/ima-certs/centosimarelease9.crt
> @@ -0,0 +1,70 @@
> +Certificate:
> +    Data:
> +        Version: 3 (0x2)
> +        Serial Number:
> +            d6:78:a4:e3:e7:d3:c5:ab
> +    Signature Algorithm: sha256WithRSAEncryption
> +        Issuer: O=RH-IMA-CA, CN=Red Hat IMA 
> CA/emailAddress=secal...@redhat.com
> +        Validity
> +            Not Before: Jul  1 16:14:51 2023 GMT
> +            Not After : Jan 18 16:14:51 2038 GMT
> +        Subject: CN=CentOS IMA release key (for verification)
> +        Subject Public Key Info:
> +            Public Key Algorithm: id-ecPublicKey
> +                Public-Key: (384 bit)
> +                pub: 
> +                    04:d4:d0:31:08:09:0d:97:d0:5c:c8:49:ff:90:f4:
> +                    3a:16:85:a3:73:a1:d9:c4:28:4c:f7:aa:a8:22:c2:
> +                    cf:0e:8b:d7:9a:ed:e6:f0:89:f8:85:95:72:c3:38:
> +                    27:2a:29:97:6a:6b:2b:01:04:a3:32:ba:f4:75:f9:
> +                    e4:c8:48:2f:f5:36:69:44:27:f9:35:b3:0c:c3:22:
> +                    24:67:51:06:d3:73:f1:56:94:20:a8:8c:82:34:c0:
> +                    10:ef:ce:f9:b4:7a:42
> +                ASN1 OID: secp384r1
> +                NIST CURVE: P-384
> +        X509v3 extensions:
> +            X509v3 Basic Constraints: critical
> +                CA:FALSE
> +            X509v3 Key Usage: critical
> +                Digital Signature
> +            X509v3 Extended Key Usage: critical
> +                Code Signing
> +            X509v3 Subject Key Identifier: 
> +                54:E5:A3:4F:16:2B:32:B7:77:FF:E3:4F:1E:8B:66:12:7C:43:5B:B5
> +            X509v3 Authority Key Identifier: 
> +                
> keyid:FB:31:82:5D:D0:E0:73:68:5B:26:4E:30:38:96:36:73:F7:53:95:9A
> +
> +    Signature Algorithm: sha256WithRSAEncryption
> +         c6:1d:92:0e:92:40:d6:ae:a5:5d:4e:5d:2a:e1:0f:92:42:20:
> +         89:e1:a9:82:87:35:42:c9:7f:77:dd:19:e3:cf:ef:be:8b:39:
> +         4f:99:2e:cd:cc:a3:18:23:7f:81:4b:7d:63:5d:71:b4:4b:9c:
> +         ea:dc:2f:1d:16:da:4c:ed:98:bf:df:88:11:d0:8b:af:01:55:
> +         71:05:fe:d7:ac:78:4e:46:de:48:9f:04:74:42:c2:c8:1a:fc:
> +         c5:46:6a:99:3e:9a:b0:e4:04:07:48:e2:4c:65:e5:01:a8:ad:
> +         3c:8d:c0:ca:c5:73:23:36:88:27:54:8b:90:f8:ea:55:fc:eb:
> +         b8:69:a5:8b:a0:1d:8b:f1:93:dd:71:9e:e9:88:f0:2d:0e:7d:
> +         86:a4:8d:0b:fd:00:c9:c0:73:aa:b1:65:b1:60:6e:a4:09:1b:
> +         3e:30:d9:62:2a:15:d6:50:2a:6a:fd:24:e7:8c:93:78:4a:28:
> +         d5:b1:d9:ba:1b:8d:ef:48:0d:f4:8c:79:90:0f:95:8d:79:39:
> +         8d:41:a5:fc:6f:e4:ef:5c:ee:3b:f4:c3:2c:c3:a0:b7:61:ac:
> +         7e:e9:eb:a0:3a:ba:05:2c:bd:aa:a9:1f:c5:b9:ee:72:f6:c4:
> +         54:1f:71:3b:e1:70:1a:30:f4:04:18:50:60:c4:5a:da:93:cd:
> +         b6:f6:67:c8
> +-----BEGIN CERTIFICATE-----
> +MIIC0jCCAbqgAwIBAgIJANZ4pOPn08WrMA0GCSqGSIb3DQEBCwUAMFExEjAQBgNV
> +BAoMCVJILUlNQS1DQTEXMBUGA1UEAwwOUmVkIEhhdCBJTUEgQ0ExIjAgBgkqhkiG
> +9w0BCQEWE3NlY2FsZXJ0QHJlZGhhdC5jb20wHhcNMjMwNzAxMTYxNDUxWhcNMzgw
> +MTE4MTYxNDUxWjA0MTIwMAYDVQQDDClDZW50T1MgSU1BIHJlbGVhc2Uga2V5IChm
> +b3IgdmVyaWZpY2F0aW9uKTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNTQMQgJDZfQ
> +XMhJ/5D0OhaFo3Oh2cQoTPeqqCLCzw6L15rt5vCJ+IWVcsM4Jyopl2prKwEEozK6
> +9HX55MhIL/U2aUQn+TWzDMMiJGdRBtNz8VaUIKiMgjTAEO/O+bR6QqN4MHYwDAYD
> +VR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUH
> +AwMwHQYDVR0OBBYEFFTlo08WKzK3d//jTx6LZhJ8Q1u1MB8GA1UdIwQYMBaAFPsx
> +gl3Q4HNoWyZOMDiWNnP3U5WaMA0GCSqGSIb3DQEBCwUAA4IBAQDGHZIOkkDWrqVd
> +Tl0q4Q+SQiCJ4amChzVCyX933Rnjz+++izlPmS7NzKMYI3+BS31jXXG0S5zq3C8d
> +FtpM7Zi/34gR0IuvAVVxBf7XrHhORt5InwR0QsLIGvzFRmqZPpqw5AQHSOJMZeUB
> +qK08jcDKxXMjNognVIuQ+OpV/Ou4aaWLoB2L8ZPdcZ7piPAtDn2GpI0L/QDJwHOq
> +sWWxYG6kCRs+MNliKhXWUCpq/STnjJN4SijVsdm6G43vSA30jHmQD5WNeTmNQaX8
> +b+TvXO479MMsw6C3Yax+6eugOroFLL2qqR/Fue5y9sRUH3E74XAaMPQEGFBgxFra
> +k8229mfI
> +-----END CERTIFICATE-----
> diff --git a/debuginfod/ima-certs/fedora-38-ima.der 
> b/debuginfod/ima-certs/fedora-38-ima.der
> new file mode 100644
> index 000000000000..238ae6c30763
> Binary files /dev/null and b/debuginfod/ima-certs/fedora-38-ima.der differ
> diff --git a/debuginfod/ima-certs/fedora-39-ima.der 
> b/debuginfod/ima-certs/fedora-39-ima.der
> new file mode 100644
> index 000000000000..0d13baa62d75
> Binary files /dev/null and b/debuginfod/ima-certs/fedora-39-ima.der differ
> diff --git a/debuginfod/ima-certs/redhatimabeta9.crt 
> b/debuginfod/ima-certs/redhatimabeta9.crt
> new file mode 100644
> index 000000000000..4a3b6701d6c4
> --- /dev/null
> +++ b/debuginfod/ima-certs/redhatimabeta9.crt
> @@ -0,0 +1,70 @@
> +Certificate:
> +    Data:
> +        Version: 3 (0x2)
> +        Serial Number:
> +            d6:78:a4:e3:e7:d3:c5:a8
> +    Signature Algorithm: sha256WithRSAEncryption
> +        Issuer: O=RH-IMA-CA, CN=Red Hat IMA 
> CA/emailAddress=secal...@redhat.com
> +        Validity
> +            Not Before: Jul  1 16:13:38 2023 GMT
> +            Not After : Jan 18 16:13:38 2038 GMT
> +        Subject: CN=Red Hat IMA beta key (for verification)
> +        Subject Public Key Info:
> +            Public Key Algorithm: id-ecPublicKey
> +                Public-Key: (384 bit)
> +                pub: 
> +                    04:af:d0:5e:29:27:3a:26:05:27:74:1b:07:62:20:
> +                    c3:3e:40:43:73:3e:ad:e4:24:3e:b1:24:9d:72:81:
> +                    e3:f0:43:40:7b:82:52:dc:57:66:ed:92:af:79:c2:
> +                    6b:09:31:bf:50:1b:76:24:b5:2f:8f:ce:9c:65:20:
> +                    d4:e2:3d:37:41:0d:5d:b7:78:af:8c:d3:71:b7:10:
> +                    c9:70:15:77:d0:8f:09:97:3f:7e:83:7d:67:98:8f:
> +                    cf:be:16:b5:6d:73:7d
> +                ASN1 OID: secp384r1
> +                NIST CURVE: P-384
> +        X509v3 extensions:
> +            X509v3 Basic Constraints: critical
> +                CA:FALSE
> +            X509v3 Key Usage: critical
> +                Digital Signature
> +            X509v3 Extended Key Usage: critical
> +                Code Signing
> +            X509v3 Subject Key Identifier: 
> +                7D:45:10:63:D2:89:7B:E3:6D:A6:62:D6:6B:36:15:E5:F0:F2:9C:D0
> +            X509v3 Authority Key Identifier: 
> +                
> keyid:FB:31:82:5D:D0:E0:73:68:5B:26:4E:30:38:96:36:73:F7:53:95:9A
> +
> +    Signature Algorithm: sha256WithRSAEncryption
> +         68:9b:7d:29:e2:46:ed:03:6a:5d:d9:9e:f3:7a:0d:49:fa:22:
> +         40:02:3e:cb:af:a3:ee:aa:b5:43:fd:d9:1e:5b:82:89:e7:ad:
> +         48:03:d4:4c:50:7e:03:32:c3:eb:88:b8:b4:57:8c:1d:78:f9:
> +         a4:ca:d2:55:78:a1:ec:67:c8:1b:90:40:be:98:5b:fd:77:91:
> +         6b:7c:ed:5f:e9:b0:af:6e:71:03:bc:03:5f:ad:f0:01:bc:24:
> +         0d:ea:1e:04:9e:10:ee:5c:97:17:49:3e:8e:7b:51:17:1a:e2:
> +         06:09:04:d4:6d:94:ea:30:57:fd:70:d0:56:6c:36:81:9f:81:
> +         75:25:98:34:9b:e8:03:14:b0:7c:8c:13:b5:59:0a:45:15:d8:
> +         75:c9:7a:01:a4:2d:71:a1:6d:53:b6:22:37:ab:98:d3:f0:76:
> +         28:8d:c3:3b:7d:c5:11:b1:89:7b:ac:41:e2:92:e9:38:4d:6d:
> +         07:d9:07:76:53:1a:87:ca:79:2e:5d:20:ec:8b:f2:55:56:dd:
> +         b1:65:ec:4c:d7:86:7b:8e:9b:a7:7f:6d:6e:7a:3f:18:e3:e3:
> +         e7:3f:1c:fb:44:ab:7e:0c:c2:1d:61:7c:92:73:07:a2:b4:5a:
> +         6b:45:64:dd:4d:c9:df:a3:e1:5f:39:db:36:f3:d0:ba:c6:be:
> +         97:3c:2f:e6
> +-----BEGIN CERTIFICATE-----
> +MIIC0DCCAbigAwIBAgIJANZ4pOPn08WoMA0GCSqGSIb3DQEBCwUAMFExEjAQBgNV
> +BAoMCVJILUlNQS1DQTEXMBUGA1UEAwwOUmVkIEhhdCBJTUEgQ0ExIjAgBgkqhkiG
> +9w0BCQEWE3NlY2FsZXJ0QHJlZGhhdC5jb20wHhcNMjMwNzAxMTYxMzM4WhcNMzgw
> +MTE4MTYxMzM4WjAyMTAwLgYDVQQDDCdSZWQgSGF0IElNQSBiZXRhIGtleSAoZm9y
> +IHZlcmlmaWNhdGlvbikwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASv0F4pJzomBSd0
> +GwdiIMM+QENzPq3kJD6xJJ1ygePwQ0B7glLcV2btkq95wmsJMb9QG3YktS+Pzpxl
> +INTiPTdBDV23eK+M03G3EMlwFXfQjwmXP36DfWeYj8++FrVtc32jeDB2MAwGA1Ud
> +EwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMD
> +MB0GA1UdDgQWBBR9RRBj0ol7422mYtZrNhXl8PKc0DAfBgNVHSMEGDAWgBT7MYJd
> +0OBzaFsmTjA4ljZz91OVmjANBgkqhkiG9w0BAQsFAAOCAQEAaJt9KeJG7QNqXdme
> +83oNSfoiQAI+y6+j7qq1Q/3ZHluCieetSAPUTFB+AzLD64i4tFeMHXj5pMrSVXih
> +7GfIG5BAvphb/XeRa3ztX+mwr25xA7wDX63wAbwkDeoeBJ4Q7lyXF0k+jntRFxri
> +BgkE1G2U6jBX/XDQVmw2gZ+BdSWYNJvoAxSwfIwTtVkKRRXYdcl6AaQtcaFtU7Yi
> +N6uY0/B2KI3DO33FEbGJe6xB4pLpOE1tB9kHdlMah8p5Ll0g7IvyVVbdsWXsTNeG
> +e46bp39tbno/GOPj5z8c+0SrfgzCHWF8knMHorRaa0Vk3U3J36PhXznbNvPQusa+
> +lzwv5g==
> +-----END CERTIFICATE-----
> diff --git a/debuginfod/ima-certs/redhatimarelease9.crt 
> b/debuginfod/ima-certs/redhatimarelease9.crt
> new file mode 100644
> index 000000000000..cd0b66494da2
> --- /dev/null
> +++ b/debuginfod/ima-certs/redhatimarelease9.crt
> @@ -0,0 +1,70 @@
> +Certificate:
> +    Data:
> +        Version: 3 (0x2)
> +        Serial Number:
> +            d6:78:a4:e3:e7:d3:c5:a9
> +    Signature Algorithm: sha256WithRSAEncryption
> +        Issuer: O=RH-IMA-CA, CN=Red Hat IMA 
> CA/emailAddress=secal...@redhat.com
> +        Validity
> +            Not Before: Jul  1 16:14:04 2023 GMT
> +            Not After : Jan 18 16:14:04 2038 GMT
> +        Subject: CN=Red Hat IMA release key (for verification)
> +        Subject Public Key Info:
> +            Public Key Algorithm: id-ecPublicKey
> +                Public-Key: (384 bit)
> +                pub: 
> +                    04:4f:0e:ef:bf:e2:23:89:91:27:4e:7c:32:a1:d0:
> +                    c0:26:92:de:37:8d:b0:5d:ea:7f:d6:27:18:9b:b4:
> +                    62:be:06:85:3d:f9:cc:47:7e:c7:bd:91:54:53:62:
> +                    b4:c0:8a:43:48:c2:59:07:2b:88:d7:3d:4b:30:8d:
> +                    6c:32:fb:a5:da:dc:8a:85:e9:61:44:18:fc:d9:8b:
> +                    f5:5e:38:c8:85:77:ca:73:68:ce:48:df:af:3d:06:
> +                    43:2f:4b:6c:0c:cd:88
> +                ASN1 OID: secp384r1
> +                NIST CURVE: P-384
> +        X509v3 extensions:
> +            X509v3 Basic Constraints: critical
> +                CA:FALSE
> +            X509v3 Key Usage: critical
> +                Digital Signature
> +            X509v3 Extended Key Usage: critical
> +                Code Signing
> +            X509v3 Subject Key Identifier: 
> +                22:FA:01:DC:0E:A0:26:9F:69:A8:67:E5:CF:E4:9C:FB:D3:32:04:49
> +            X509v3 Authority Key Identifier: 
> +                
> keyid:FB:31:82:5D:D0:E0:73:68:5B:26:4E:30:38:96:36:73:F7:53:95:9A
> +
> +    Signature Algorithm: sha256WithRSAEncryption
> +         1a:1e:c1:2d:65:ad:f0:24:ec:9e:a7:fd:d4:ea:e1:54:dc:31:
> +         1c:62:8c:29:0b:7a:56:6e:f7:b4:87:92:3e:ff:d5:40:4b:24:
> +         a1:68:6e:ee:9c:35:65:a1:3f:8e:f3:8b:9b:18:b1:03:ed:fb:
> +         50:2e:a3:23:d1:93:1d:d6:82:0a:10:6f:34:be:d6:3a:bd:76:
> +         8c:44:0e:ad:a7:2a:c4:8e:8d:c4:e4:8d:51:d8:26:b7:38:89:
> +         d1:23:a0:23:88:76:fa:f1:27:91:57:3e:b2:0f:cf:73:53:db:
> +         20:40:5d:82:b9:e9:bc:a2:94:09:57:fb:85:0d:56:4b:dc:19:
> +         65:12:2f:6d:6a:3b:be:35:1f:d4:52:ea:e4:72:36:f9:fe:cb:
> +         d4:1b:0f:e3:0e:88:7c:68:58:28:c3:06:5f:bd:d2:f9:2e:1a:
> +         30:f0:63:65:2d:55:e1:a4:fd:97:cf:ff:c0:52:22:1c:24:a3:
> +         6e:de:7a:c9:9d:75:d2:d0:82:b0:7f:6f:db:21:01:69:f0:54:
> +         76:04:19:68:2c:22:72:dd:3b:0d:04:d5:ad:5a:80:30:68:90:
> +         6e:c2:27:f4:28:af:1b:78:f6:0a:70:74:5c:3a:61:42:f5:63:
> +         7c:83:12:5a:1b:43:bc:d4:1b:28:b5:ef:98:c5:14:04:42:80:
> +         dd:54:30:a4
> +-----BEGIN CERTIFICATE-----
> +MIIC0zCCAbugAwIBAgIJANZ4pOPn08WpMA0GCSqGSIb3DQEBCwUAMFExEjAQBgNV
> +BAoMCVJILUlNQS1DQTEXMBUGA1UEAwwOUmVkIEhhdCBJTUEgQ0ExIjAgBgkqhkiG
> +9w0BCQEWE3NlY2FsZXJ0QHJlZGhhdC5jb20wHhcNMjMwNzAxMTYxNDA0WhcNMzgw
> +MTE4MTYxNDA0WjA1MTMwMQYDVQQDDCpSZWQgSGF0IElNQSByZWxlYXNlIGtleSAo
> +Zm9yIHZlcmlmaWNhdGlvbikwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARPDu+/4iOJ
> +kSdOfDKh0MAmkt43jbBd6n/WJxibtGK+BoU9+cxHfse9kVRTYrTAikNIwlkHK4jX
> +PUswjWwy+6Xa3IqF6WFEGPzZi/VeOMiFd8pzaM5I3689BkMvS2wMzYijeDB2MAwG
> +A1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
> +BwMDMB0GA1UdDgQWBBQi+gHcDqAmn2moZ+XP5Jz70zIESTAfBgNVHSMEGDAWgBT7
> +MYJd0OBzaFsmTjA4ljZz91OVmjANBgkqhkiG9w0BAQsFAAOCAQEAGh7BLWWt8CTs
> +nqf91OrhVNwxHGKMKQt6Vm73tIeSPv/VQEskoWhu7pw1ZaE/jvOLmxixA+37UC6j
> +I9GTHdaCChBvNL7WOr12jEQOracqxI6NxOSNUdgmtziJ0SOgI4h2+vEnkVc+sg/P
> +c1PbIEBdgrnpvKKUCVf7hQ1WS9wZZRIvbWo7vjUf1FLq5HI2+f7L1BsP4w6IfGhY
> +KMMGX73S+S4aMPBjZS1V4aT9l8//wFIiHCSjbt56yZ110tCCsH9v2yEBafBUdgQZ
> +aCwict07DQTVrVqAMGiQbsIn9CivG3j2CnB0XDphQvVjfIMSWhtDvNQbKLXvmMUU
> +BEKA3VQwpA==
> +-----END CERTIFICATE-----

So not sure I like distributing this imho random collection of
certificates.

> diff --git a/doc/ChangeLog b/doc/ChangeLog
> index 7f2d6ff4fd31..914f8f649511 100644
> --- a/doc/ChangeLog
> +++ b/doc/ChangeLog
> @@ -1,3 +1,10 @@
> +2023-08-14  Ryan Goldberg  <rgold...@redhat.com>
> +
> +     * debuginfod-client-config.7: Document DEBUGINFOD_IMA_CERT_PATH,
> +     update DEBUGINFOD_URLS.
> +     * debuginfod.8: Document --koji-sigcache
> +     * debuginfod-find.1, debuginfod_find_debuginfo.3: Update SECURITY
> +
>  2023-02-14  Mark Wielaard  <m...@klomp.org>
>  
>       * debuginfod.8: Add .TP before -g.
> diff --git a/doc/debuginfod-client-config.7 b/doc/debuginfod-client-config.7
> index 53d82806d395..11d2465858c5 100644
> --- a/doc/debuginfod-client-config.7
> +++ b/doc/debuginfod-client-config.7
> @@ -27,6 +27,34 @@ 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:permissive\fP.  For example:
> +
> +.in +4n
> +.EX
> +DEBUGINFOD_URLS="https://foo.com ima:enforcing https://bar.ca 
> http://localhost:8002/ ima:permissive https://baz.org";
> +.EE
> +.in
> +
> +Where foo.com and baz.org use the default \fIpermissive\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.
> +.IP
> +\fIima:permissive\fP Every downloaded file accompanied by a signature
> +must be valid, but downloads without signatures are accepted.
> +.IP
> +\fIima:ignore\fP Skips verification altogether.
> +.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 +110,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

OK. Nice documentation. Thanks.

> diff --git a/doc/debuginfod-find.1 b/doc/debuginfod-find.1
> index 7d577babeb89..89a706728cd6 100644
> --- a/doc/debuginfod-find.1
> +++ b/doc/debuginfod-find.1
> @@ -129,10 +129,19 @@ 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 permissive mode, only rejecting incorrect
> +signatures.  See below for details and other policy options.
> +
> +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.

OK.

> diff --git a/doc/debuginfod.8 b/doc/debuginfod.8
> index d4316bec8175..3e73868715d7 100644
> --- a/doc/debuginfod.8
> +++ b/doc/debuginfod.8
> @@ -289,6 +289,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.
> @@ -311,7 +319,8 @@ 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.
> +archive the file was found in.  X-DEBUGINFOD-IMA-SIGNATURE contains the
> +per-file IMA signature as a hexadecimal blob.
>  
>  There are three requests.  In each case, the buildid is encoded as a
>  lowercase hexadecimal string.  For example, for a program \fI/bin/ls\fP,

OK.

> diff --git a/doc/debuginfod_find_debuginfo.3 b/doc/debuginfod_find_debuginfo.3
> index 6469a3dfb2db..e170b94367b5 100644
> --- a/doc/debuginfod_find_debuginfo.3
> +++ b/doc/debuginfod_find_debuginfo.3
> @@ -250,13 +250,22 @@ 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 permissive mode, only rejecting incorrect
> +signatures.  See below for details and other policy options.
> +
> +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.)

OK.

> diff --git a/tests/ChangeLog b/tests/ChangeLog
> index d816873ce083..e52470c04f49 100644
> --- a/tests/ChangeLog
> +++ b/tests/ChangeLog
> @@ -1,3 +1,11 @@
> +2023-08-14  Ryan Goldberg  <rgold...@redhat.com>
> +
> +     * run-debuginfod-ima-verification.sh: New test.
> +     * hello2-1.0-1.x86_64.rpm. New test file.
> +     * debuginfod-ima/koji. New test directory.
> +     * imacert.der. New test file.
> +     * Makefile.am (TESTS, EXTRA_DIST): Add it.
> +
>  2023-04-21  Frank Ch. Eigler <f...@redhat.com>
>  
>       * run-debuginfod-IXr.sh: New test.
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 32b18e6ef633..130cc992c42e 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -273,6 +273,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
> @@ -588,6 +591,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 \
> @@ -611,6 +615,9 @@ 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/rhel9/hello2-1.0-1.x86_64.rpm \
> +              debuginfod-ima/rhel9/imacert.der \
> +              debuginfod-ima/koji \
>            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 \

Should hello-2.10-9.fc38.x86_64.rpm.sig also be added to EXTRA_DIST?

> 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/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..45f3e7d037a6
> --- /dev/null
> +++ b/tests/run-debuginfod-ima-verification.sh
> @@ -0,0 +1,177 @@
> +#!/usr/bin/env bash
> +#
> +# Copyright (C) 2019-2023 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; }

Add rpmsign to elfutils.spec.in as BuildRequires?

> +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; }

Isn't this redundant with the configure.ac checks?

> +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  (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" exam...@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 
> exam...@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

It seems simpler to just add openssl and gpg to BuildRequires and the
at the start of the test. If you really want a presigned RPM add
another test?

> +########################################################################
> +# 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";
> +
> +# 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
> +
> +# 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
> +
> +# 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
> +
> +# 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
> +
> +# 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 and with the required directory as the 
> third in the PATH
> +VERIFICATION_CERT_DIR=/not/a/dir:${abs_srcdir}/debuginfod-ima/rhel9:${abs_srcdir}/../debuginfod/ima-certs/
> +
> +########################################################################
> +# 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
> +
> +# Test 1: 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

I haven't ran the tests but it looks OK.

Thanks,

Mark


Reply via email to