LCOV - code coverage report
Current view: top level - debuginfod - debuginfod-client.c (source / functions) Hit Total Coverage
Test: elfutils-0.192 Lines: 932 1183 78.8 %
Date: 2024-11-07 15:12:49 Functions: 30 32 93.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 495 841 58.9 %

           Branch data     Line data    Source code
       1                 :            : /* Retrieve ELF / DWARF / source files from the debuginfod.
       2                 :            :    Copyright (C) 2019-2024 Red Hat, Inc.
       3                 :            :    Copyright (C) 2021, 2022 Mark J. Wielaard <mark@klomp.org>
       4                 :            :    This file is part of elfutils.
       5                 :            : 
       6                 :            :    This file is free software; you can redistribute it and/or modify
       7                 :            :    it under the terms of either
       8                 :            : 
       9                 :            :      * the GNU Lesser General Public License as published by the Free
      10                 :            :        Software Foundation; either version 3 of the License, or (at
      11                 :            :        your option) any later version
      12                 :            : 
      13                 :            :    or
      14                 :            : 
      15                 :            :      * the GNU General Public License as published by the Free
      16                 :            :        Software Foundation; either version 2 of the License, or (at
      17                 :            :        your option) any later version
      18                 :            : 
      19                 :            :    or both in parallel, as here.
      20                 :            : 
      21                 :            :    elfutils is distributed in the hope that it will be useful, but
      22                 :            :    WITHOUT ANY WARRANTY; without even the implied warranty of
      23                 :            :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      24                 :            :    General Public License for more details.
      25                 :            : 
      26                 :            :    You should have received copies of the GNU General Public License and
      27                 :            :    the GNU Lesser General Public License along with this program.  If
      28                 :            :    not, see <http://www.gnu.org/licenses/>.  */
      29                 :            : 
      30                 :            : 
      31                 :            : /* cargo-cult from libdwfl linux-kernel-modules.c */
      32                 :            : /* In case we have a bad fts we include this before config.h because it
      33                 :            :    can't handle _FILE_OFFSET_BITS.
      34                 :            :    Everything we need here is fine if its declarations just come first.
      35                 :            :    Also, include sys/types.h before fts. On some systems fts.h is not self
      36                 :            :    contained. */
      37                 :            : #ifdef BAD_FTS
      38                 :            :   #include <sys/types.h>
      39                 :            :   #include <fts.h>
      40                 :            : #endif
      41                 :            : 
      42                 :            : #include "config.h"
      43                 :            : #include "debuginfod.h"
      44                 :            : #include "system.h"
      45                 :            : #include <ctype.h>
      46                 :            : #include <errno.h>
      47                 :            : #include <stdlib.h>
      48                 :            : #include <gelf.h>
      49                 :            : 
      50                 :            : #ifdef ENABLE_IMA_VERIFICATION
      51                 :            : #include <openssl/sha.h>
      52                 :            : #include <openssl/pem.h>
      53                 :            : #include <openssl/evp.h>
      54                 :            : #include <openssl/x509v3.h>
      55                 :            : #include <arpa/inet.h>
      56                 :            : #include <imaevm.h>
      57                 :            : #endif
      58                 :            : typedef enum {ignore, enforcing, undefined} ima_policy_t;
      59                 :            : 
      60                 :            : 
      61                 :            : /* We might be building a bootstrap dummy library, which is really simple. */
      62                 :            : #ifdef DUMMY_LIBDEBUGINFOD
      63                 :            : 
      64                 :            : debuginfod_client *debuginfod_begin (void) { errno = ENOSYS; return NULL; }
      65                 :            : int debuginfod_find_debuginfo (debuginfod_client *c, const unsigned char *b,
      66                 :            :                                int s, char **p) { return -ENOSYS; }
      67                 :            : int debuginfod_find_executable (debuginfod_client *c, const unsigned char *b,
      68                 :            :                                 int s, char **p) { return -ENOSYS; }
      69                 :            : int debuginfod_find_source (debuginfod_client *c, const unsigned char *b,
      70                 :            :                             int s, const char *f, char **p)  { return -ENOSYS; }
      71                 :            : int debuginfod_find_section (debuginfod_client *c, const unsigned char *b,
      72                 :            :                              int s, const char *scn, char **p)
      73                 :            :                               { return -ENOSYS; }
      74                 :            : int debuginfod_find_metadata (debuginfod_client *c,
      75                 :            :                               const char *k, const char *v, char **p) { return -ENOSYS; }
      76                 :            : void debuginfod_set_progressfn(debuginfod_client *c,
      77                 :            :                                debuginfod_progressfn_t fn) { }
      78                 :            : void debuginfod_set_verbose_fd(debuginfod_client *c, int fd) { }
      79                 :            : void debuginfod_set_user_data (debuginfod_client *c, void *d) { }
      80                 :            : void* debuginfod_get_user_data (debuginfod_client *c) { return NULL; }
      81                 :            : const char* debuginfod_get_url (debuginfod_client *c) { return NULL; }
      82                 :            : int debuginfod_add_http_header (debuginfod_client *c,
      83                 :            :                                 const char *h) { return -ENOSYS; }
      84                 :            : const char* debuginfod_get_headers (debuginfod_client *c) { return NULL; }
      85                 :            : 
      86                 :            : void debuginfod_end (debuginfod_client *c) { }
      87                 :            : 
      88                 :            : #else /* DUMMY_LIBDEBUGINFOD */
      89                 :            : 
      90                 :            : #include <assert.h>
      91                 :            : #include <dirent.h>
      92                 :            : #include <stdio.h>
      93                 :            : #include <errno.h>
      94                 :            : #include <unistd.h>
      95                 :            : #include <fcntl.h>
      96                 :            : #include <fts.h>
      97                 :            : #include <regex.h>
      98                 :            : #include <string.h>
      99                 :            : #include <stdbool.h>
     100                 :            : #include <linux/limits.h>
     101                 :            : #include <time.h>
     102                 :            : #include <utime.h>
     103                 :            : #include <sys/syscall.h>
     104                 :            : #include <sys/types.h>
     105                 :            : #include <sys/stat.h>
     106                 :            : #include <sys/utsname.h>
     107                 :            : #include <curl/curl.h>
     108                 :            : #include <fnmatch.h>
     109                 :            : #include <json-c/json.h>
     110                 :            : 
     111                 :            : /* If fts.h is included before config.h, its indirect inclusions may not
     112                 :            :    give us the right LFS aliases of these functions, so map them manually.  */
     113                 :            : #ifdef BAD_FTS
     114                 :            :   #ifdef _FILE_OFFSET_BITS
     115                 :            :     #define open open64
     116                 :            :     #define fopen fopen64
     117                 :            :   #endif
     118                 :            : #else
     119                 :            :   #include <sys/types.h>
     120                 :            :   #include <fts.h>
     121                 :            : #endif
     122                 :            : 
     123                 :            : /* Older curl.h don't define CURL_AT_LEAST_VERSION.  */
     124                 :            : #ifndef CURL_AT_LEAST_VERSION
     125                 :            :   #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
     126                 :            :   #define CURL_AT_LEAST_VERSION(x,y,z) \
     127                 :            :     (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
     128                 :            : #endif
     129                 :            : 
     130                 :            : #include <pthread.h>
     131                 :            : 
     132                 :            : static pthread_once_t init_control = PTHREAD_ONCE_INIT;
     133                 :            : static bool curl_has_https; // = false
     134                 :            : 
     135                 :            : static void
     136                 :        518 : libcurl_init(void)
     137                 :            : {
     138                 :        518 :   curl_global_init(CURL_GLOBAL_DEFAULT);
     139                 :            : 
     140                 :        518 :   for (const char *const *protocol = curl_version_info(CURLVERSION_NOW)->protocols;
     141         [ +  + ]:      16058 :        *protocol != NULL; ++protocol)
     142                 :            :     {
     143         [ +  + ]:      15540 :       if(strcmp("https", *protocol) == 0)
     144                 :        518 :         curl_has_https = true;
     145                 :            :     }
     146                 :        518 : }
     147                 :            : 
     148                 :            : 
     149                 :            : #ifdef ENABLE_IMA_VERIFICATION
     150                 :            : struct public_key_entry
     151                 :            : {
     152                 :            :   struct public_key_entry *next; /* singly-linked list */
     153                 :            :   uint32_t keyid; /* last 4 bytes of sha1 of public key */
     154                 :            :   EVP_PKEY *key; /* openssl */
     155                 :            : };
     156                 :            : #endif
     157                 :            : 
     158                 :            : 
     159                 :            : struct debuginfod_client
     160                 :            : {
     161                 :            :   /* Progress/interrupt callback function. */
     162                 :            :   debuginfod_progressfn_t progressfn;
     163                 :            : 
     164                 :            :   /* Stores user data. */
     165                 :            :   void* user_data;
     166                 :            : 
     167                 :            :   /* Stores current/last url, if any. */
     168                 :            :   char* url;
     169                 :            : 
     170                 :            :   /* Accumulates outgoing http header names/values. */
     171                 :            :   int user_agent_set_p; /* affects add_default_headers */
     172                 :            :   struct curl_slist *headers;
     173                 :            : 
     174                 :            :   /* Flags the default_progressfn having printed something that
     175                 :            :      debuginfod_end needs to terminate. */
     176                 :            :   int default_progressfn_printed_p;
     177                 :            : 
     178                 :            :   /* Indicates whether the last query was cancelled by progressfn.  */
     179                 :            :   bool progressfn_cancel;
     180                 :            : 
     181                 :            :   /* File descriptor to output any verbose messages if > 0.  */
     182                 :            :   int verbose_fd;
     183                 :            : 
     184                 :            :   /* Maintain a long-lived curl multi-handle, which keeps a
     185                 :            :      connection/tls/dns cache to recently seen servers. */
     186                 :            :   CURLM *server_mhandle;
     187                 :            :   
     188                 :            :   /* Can contain all other context, like cache_path, server_urls,
     189                 :            :      timeout or other info gotten from environment variables, the
     190                 :            :      handle data, etc. So those don't have to be reparsed and
     191                 :            :      recreated on each request.  */
     192                 :            :   char * winning_headers;
     193                 :            : 
     194                 :            : #ifdef ENABLE_IMA_VERIFICATION
     195                 :            :   /* IMA public keys */
     196                 :            :   struct public_key_entry *ima_public_keys;
     197                 :            : #endif
     198                 :            : };
     199                 :            : 
     200                 :            : 
     201                 :            : /* The cache_clean_interval_s file within the debuginfod cache specifies
     202                 :            :    how frequently the cache should be cleaned. The file's st_mtime represents
     203                 :            :    the time of last cleaning.  */
     204                 :            : static const char *cache_clean_interval_filename = "cache_clean_interval_s";
     205                 :            : static const long cache_clean_default_interval_s = 86400; /* 1 day */
     206                 :            : 
     207                 :            : /* The cache_miss_default_s within the debuginfod cache specifies how
     208                 :            :    frequently the empty file should be released.*/
     209                 :            : static const long cache_miss_default_s = 600; /* 10 min */
     210                 :            : static const char *cache_miss_filename = "cache_miss_s";
     211                 :            : 
     212                 :            : /* The cache_max_unused_age_s file within the debuginfod cache specifies the
     213                 :            :    the maximum time since last access that a file will remain in the cache.  */
     214                 :            : static const char *cache_max_unused_age_filename = "max_unused_age_s";
     215                 :            : static const long cache_default_max_unused_age_s = 604800; /* 1 week */
     216                 :            : 
     217                 :            : /* The metadata_retention_default_s file within the debuginfod cache
     218                 :            :    specifies how long metadata query results should be cached. */
     219                 :            : static const long metadata_retention_default_s = 3600; /* 1 hour */
     220                 :            : static const char *metadata_retention_filename = "metadata_retention_s";
     221                 :            : 
     222                 :            : /* Location of the cache of files downloaded from debuginfods.
     223                 :            :    The default parent directory is $HOME, or '/' if $HOME doesn't exist.  */
     224                 :            : static const char *cache_default_name = ".debuginfod_client_cache";
     225                 :            : static const char *cache_xdg_name = "debuginfod_client";
     226                 :            : 
     227                 :            : /* URLs of debuginfods, separated by url_delim. */
     228                 :            : static const char *url_delim =  " ";
     229                 :            : 
     230                 :            : /* Timeout for debuginfods, in seconds (to get at least 100K). */
     231                 :            : static const long default_timeout = 90;
     232                 :            : 
     233                 :            : /* Default retry count for download error. */
     234                 :            : static const long default_retry_limit = 2;
     235                 :            : 
     236                 :            : /* Data associated with a particular CURL easy handle. Passed to
     237                 :            :    the write callback.  */
     238                 :            : struct handle_data
     239                 :            : {
     240                 :            :   /* Cache file to be written to in case query is successful.  */
     241                 :            :   int fd;
     242                 :            : 
     243                 :            :   /* URL queried by this handle.  */
     244                 :            :   char url[PATH_MAX];
     245                 :            : 
     246                 :            :   /* error buffer for this handle.  */
     247                 :            :   char errbuf[CURL_ERROR_SIZE];
     248                 :            : 
     249                 :            :   /* This handle.  */
     250                 :            :   CURL *handle;
     251                 :            : 
     252                 :            :   /* The client object whom we're serving. */
     253                 :            :   debuginfod_client *client;
     254                 :            : 
     255                 :            :   /* Pointer to handle that should write to fd. Initially points to NULL,
     256                 :            :      then points to the first handle that begins writing the target file
     257                 :            :      to the cache. Used to ensure that a file is not downloaded from
     258                 :            :      multiple servers unnecessarily.  */
     259                 :            :   CURL **target_handle;
     260                 :            : 
     261                 :            :   /* Response http headers for this client handle, sent from the server */
     262                 :            :   char *response_data;
     263                 :            :   size_t response_data_size;
     264                 :            : 
     265                 :            :   /* Response metadata values for this client handle, sent from the server */
     266                 :            :   char *metadata;
     267                 :            :   size_t metadata_size;
     268                 :            : };
     269                 :            : 
     270                 :            : 
     271                 :            : 
     272                 :            : #ifdef ENABLE_IMA_VERIFICATION
     273                 :            :   static inline unsigned char hex2dec(char c)
     274                 :            :   {
     275                 :            :     if (c >= '0' && c <= '9') return (c - '0');
     276                 :            :     if (c >= 'a' && c <= 'f') return (c - 'a') + 10;
     277                 :            :     if (c >= 'A' && c <= 'F') return (c - 'A') + 10;
     278                 :            :     return 0;
     279                 :            :   }
     280                 :            : 
     281                 :            :   static inline ima_policy_t ima_policy_str2enum(const char* ima_pol)
     282                 :            :   {
     283                 :            :     if (NULL == ima_pol)                    return undefined;
     284                 :            :     if (0 == strcmp(ima_pol, "ignore"))     return ignore;
     285                 :            :     if (0 == strcmp(ima_pol, "enforcing"))  return enforcing;
     286                 :            :     return undefined;
     287                 :            :   }
     288                 :            : 
     289                 :            :   static inline const char* ima_policy_enum2str(ima_policy_t ima_pol)
     290                 :            :   {
     291                 :            :     switch (ima_pol)
     292                 :            :     {
     293                 :            :     case ignore:
     294                 :            :       return "ignore";
     295                 :            :     case enforcing:
     296                 :            :       return "enforcing";
     297                 :            :     case undefined:
     298                 :            :       return "undefined";
     299                 :            :     }
     300                 :            :     return "";
     301                 :            :   }
     302                 :            : 
     303                 :            : 
     304                 :            : static uint32_t extract_skid_pk(EVP_PKEY *pkey) // compute keyid by public key hashing
     305                 :            : {
     306                 :            :   if (!pkey) return 0;
     307                 :            :   uint32_t keyid = 0;
     308                 :            :   X509_PUBKEY *pk = NULL;
     309                 :            :   const unsigned char *public_key = NULL;                                                  
     310                 :            :   int len;
     311                 :            :   if (X509_PUBKEY_set(&pk, pkey) &&
     312                 :            :       X509_PUBKEY_get0_param(NULL, &public_key, &len, NULL, pk))
     313                 :            :     {
     314                 :            :       uint8_t sha1[SHA_DIGEST_LENGTH];
     315                 :            :       SHA1(public_key, len, sha1);
     316                 :            :       memcpy(&keyid, sha1 + 16, 4);
     317                 :            :     }
     318                 :            :   X509_PUBKEY_free(pk);
     319                 :            :   return ntohl(keyid);
     320                 :            : }
     321                 :            : 
     322                 :            : 
     323                 :            : static uint32_t extract_skid(X509* x509) // compute keyid from cert or its public key 
     324                 :            :   {
     325                 :            :     if (!x509) return 0;
     326                 :            :     uint32_t keyid = 0;
     327                 :            :     // Attempt to get the skid from the certificate
     328                 :            :     const ASN1_OCTET_STRING *skid_asn1_str = X509_get0_subject_key_id(x509);
     329                 :            :     if (skid_asn1_str)
     330                 :            :       {
     331                 :            :         int skid_len = ASN1_STRING_length(skid_asn1_str);
     332                 :            :         memcpy(&keyid, ASN1_STRING_get0_data(skid_asn1_str) + skid_len - sizeof(keyid), sizeof(keyid));
     333                 :            :       }
     334                 :            :     else // compute keyid ourselves by hashing public key
     335                 :            :       {
     336                 :            :         EVP_PKEY *pkey = X509_get0_pubkey(x509);
     337                 :            :         keyid = htonl(extract_skid_pk(pkey));
     338                 :            :       }
     339                 :            :     return ntohl(keyid);
     340                 :            :   }
     341                 :            : 
     342                 :            : 
     343                 :            : static void load_ima_public_keys (debuginfod_client *c)
     344                 :            : {
     345                 :            :   /* Iterate over the directories in DEBUGINFOD_IMA_CERT_PATH. */
     346                 :            :   char *cert_paths = getenv(DEBUGINFOD_IMA_CERT_PATH_ENV_VAR);
     347                 :            :   if (cert_paths == NULL || cert_paths[0] == '\0')
     348                 :            :     return;
     349                 :            :   cert_paths = strdup(cert_paths); // Modified during tokenization
     350                 :            :   if (cert_paths == NULL)
     351                 :            :     return;
     352                 :            :   
     353                 :            :   char* cert_dir_path;
     354                 :            :   DIR *dp;
     355                 :            :   struct dirent *entry;
     356                 :            :   int vfd = c->verbose_fd;
     357                 :            :   
     358                 :            :   char *strtok_context = NULL;
     359                 :            :   for(cert_dir_path = strtok_r(cert_paths, ":", &strtok_context);
     360                 :            :       cert_dir_path != NULL;
     361                 :            :       cert_dir_path = strtok_r(NULL, ":", &strtok_context))
     362                 :            :     {
     363                 :            :       dp = opendir(cert_dir_path);
     364                 :            :       if(!dp) continue;
     365                 :            :       while((entry = readdir(dp)))
     366                 :            :         {
     367                 :            :           // Only consider regular files with common x509 cert extensions
     368                 :            :           if(entry->d_type != DT_REG || 0 != fnmatch("*.@(der|pem|crt|cer|cert)", entry->d_name, FNM_EXTMATCH)) continue;
     369                 :            :           char certfile[PATH_MAX];
     370                 :            :           strncpy(certfile, cert_dir_path, PATH_MAX - 1);
     371                 :            :           if(certfile[strlen(certfile)-1] != '/') certfile[strlen(certfile)] = '/';
     372                 :            :           strncat(certfile, entry->d_name, PATH_MAX - strlen(certfile) - 1);
     373                 :            :           certfile[strlen(certfile)] = '\0';
     374                 :            :           
     375                 :            :           FILE *cert_fp = fopen(certfile, "r");
     376                 :            :           if(!cert_fp) continue;
     377                 :            : 
     378                 :            :           X509 *x509 = NULL;
     379                 :            :           EVP_PKEY *pkey = NULL;
     380                 :            :           char *fmt = "";
     381                 :            :           // Attempt to read the fp as DER
     382                 :            :           if(d2i_X509_fp(cert_fp, &x509))
     383                 :            :             fmt = "der ";
     384                 :            :           // Attempt to read the fp as PEM and assuming the key matches that of the signature add this key to be used
     385                 :            :           // Note we fseek since this is the second time we read from the fp
     386                 :            :           else if(0 == fseek(cert_fp, 0, SEEK_SET) && PEM_read_X509(cert_fp, &x509, NULL, NULL))
     387                 :            :             fmt = "pem "; // PEM with full certificate
     388                 :            :           else if(0 == fseek(cert_fp, 0, SEEK_SET) && PEM_read_PUBKEY(cert_fp, &pkey, NULL, NULL)) 
     389                 :            :             fmt = "pem "; // some PEM files have just a PUBLIC KEY in them
     390                 :            :           fclose(cert_fp);
     391                 :            : 
     392                 :            :           if (x509)
     393                 :            :             {
     394                 :            :               struct public_key_entry *ne = calloc(1, sizeof(struct public_key_entry));
     395                 :            :               if (ne)
     396                 :            :                 {
     397                 :            :                   ne->key = X509_extract_key(x509);
     398                 :            :                   ne->keyid = extract_skid(x509);
     399                 :            :                   ne->next = c->ima_public_keys;
     400                 :            :                   c->ima_public_keys = ne;
     401                 :            :                   if (vfd >= 0)
     402                 :            :                     dprintf(vfd, "Loaded %scertificate %s, keyid = %04x\n", fmt, certfile, ne->keyid);
     403                 :            :                 }
     404                 :            :               X509_free (x509);
     405                 :            :             }
     406                 :            :           else if (pkey)
     407                 :            :             {
     408                 :            :               struct public_key_entry *ne = calloc(1, sizeof(struct public_key_entry));
     409                 :            :               if (ne)
     410                 :            :                 {
     411                 :            :                   ne->key = pkey; // preserve refcount
     412                 :            :                   ne->keyid = extract_skid_pk(pkey);
     413                 :            :                   ne->next = c->ima_public_keys;
     414                 :            :                   c->ima_public_keys = ne;
     415                 :            :                   if (vfd >= 0)
     416                 :            :                     dprintf(vfd, "Loaded %spubkey %s, keyid %04x\n", fmt, certfile, ne->keyid);
     417                 :            :                 }
     418                 :            :             }
     419                 :            :           else
     420                 :            :             {
     421                 :            :               if (vfd >= 0)
     422                 :            :                 dprintf(vfd, "Cannot load certificate %s\n", certfile);
     423                 :            :             }
     424                 :            :         } /* for each file in directory */
     425                 :            :       closedir(dp);
     426                 :            :     } /* for each directory */
     427                 :            :   
     428                 :            :   free(cert_paths);
     429                 :            : }
     430                 :            : 
     431                 :            : 
     432                 :            : static void free_ima_public_keys (debuginfod_client *c)
     433                 :            : {
     434                 :            :   while (c->ima_public_keys)
     435                 :            :     {
     436                 :            :       EVP_PKEY_free (c->ima_public_keys->key);
     437                 :            :       struct public_key_entry *oen = c->ima_public_keys->next;
     438                 :            :       free (c->ima_public_keys);
     439                 :            :       c->ima_public_keys = oen;
     440                 :            :     }
     441                 :            : }
     442                 :            : #endif
     443                 :            : 
     444                 :            : 
     445                 :            : 
     446                 :            : static size_t
     447                 :      10013 : debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
     448                 :            : {
     449                 :      10013 :   ssize_t count = size * nmemb;
     450                 :            : 
     451                 :      10013 :   struct handle_data *d = (struct handle_data*)data;
     452                 :            : 
     453                 :            :   /* Indicate to other handles that they can abort their transfer.  */
     454         [ +  + ]:      10013 :   if (*d->target_handle == NULL)
     455                 :            :     {
     456                 :       1428 :       *d->target_handle = d->handle;
     457                 :            :       /* update the client object */
     458                 :       1428 :       const char *url = NULL;
     459                 :       1428 :       CURLcode curl_res = curl_easy_getinfo (d->handle,
     460                 :            :                                              CURLINFO_EFFECTIVE_URL, &url);
     461   [ +  -  +  - ]:       1428 :       if (curl_res == CURLE_OK && url)
     462                 :            :         {
     463                 :       1428 :           free (d->client->url);
     464                 :       1428 :           d->client->url = strdup(url); /* ok if fails */
     465                 :            :         }
     466                 :            :     }
     467                 :            : 
     468                 :            :   /* If this handle isn't the target handle, abort transfer.  */
     469         [ +  - ]:      10013 :   if (*d->target_handle != d->handle)
     470                 :            :     return -1;
     471                 :            : 
     472                 :      10013 :   return (size_t) write(d->fd, (void*)ptr, count);
     473                 :            : }
     474                 :            : 
     475                 :            : /* handle config file read and write */
     476                 :            : static int
     477                 :       2082 : debuginfod_config_cache(debuginfod_client *c, char *config_path,
     478                 :            :                         long cache_config_default_s,
     479                 :            :                         struct stat *st)
     480                 :            : {
     481                 :       2082 :   int fd = open(config_path, O_CREAT | O_RDWR, DEFFILEMODE);
     482         [ -  + ]:       2082 :   if (fd < 0)
     483                 :          0 :     return -errno;
     484                 :            : 
     485         [ -  + ]:       2082 :   if (fstat (fd, st) < 0)
     486                 :            :     {
     487                 :          0 :       int ret = -errno;
     488                 :          0 :       close (fd);
     489                 :          0 :       return ret;
     490                 :            :     }
     491                 :            : 
     492         [ +  + ]:       2082 :   if (st->st_size == 0)
     493                 :            :     {
     494         [ -  + ]:         82 :       if (dprintf(fd, "%ld", cache_config_default_s) < 0)
     495                 :            :         {
     496                 :          0 :           int ret = -errno;
     497                 :          0 :           close (fd);
     498                 :          0 :           return ret;
     499                 :            :         }
     500                 :            : 
     501                 :         82 :       close (fd);
     502                 :         82 :       return cache_config_default_s;
     503                 :            :     }
     504                 :            : 
     505                 :       2000 :   long cache_config;
     506                 :            :   /* PR29696 - NB: When using fdopen, the file descriptor is NOT
     507                 :            :      dup'ed and will be closed when the stream is closed. Manually
     508                 :            :      closing fd after fclose is called will lead to a race condition
     509                 :            :      where, if reused, the file descriptor will compete for its
     510                 :            :      regular use before being incorrectly closed here.  */
     511                 :       2000 :   FILE *config_file = fdopen(fd, "r");
     512         [ +  - ]:       2000 :   if (config_file)
     513                 :            :     {
     514         [ -  + ]:       2000 :       if (fscanf(config_file, "%ld", &cache_config) != 1)
     515                 :          0 :         cache_config = cache_config_default_s;
     516   [ -  +  -  - ]:       2000 :       if (0 != fclose (config_file) && c->verbose_fd >= 0)
     517                 :          0 :         dprintf (c->verbose_fd, "fclose failed with %s (err=%d)\n",
     518                 :          0 :                  strerror (errno), errno);
     519                 :            :     }
     520                 :            :   else
     521                 :            :     {
     522                 :          0 :       cache_config = cache_config_default_s;
     523   [ #  #  #  # ]:          0 :       if (0 != close (fd) && c->verbose_fd >= 0)
     524                 :          0 :         dprintf (c->verbose_fd, "close failed with %s (err=%d)\n",
     525                 :          0 :                  strerror (errno), errno);
     526                 :            :     }
     527                 :       2000 :   return cache_config;
     528                 :            : }
     529                 :            : 
     530                 :            : /* Delete any files that have been unmodied for a period
     531                 :            :    longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S.  */
     532                 :            : static int
     533                 :       2066 : debuginfod_clean_cache(debuginfod_client *c,
     534                 :            :                        char *cache_path, char *interval_path,
     535                 :            :                        char *max_unused_path)
     536                 :            : {
     537                 :       2066 :   time_t clean_interval, max_unused_age;
     538                 :       2066 :   int rc = -1;
     539                 :       2066 :   struct stat st;
     540                 :            : 
     541                 :            :   /* Create new interval file.  */
     542                 :       2066 :   rc = debuginfod_config_cache(c, interval_path,
     543                 :            :                                cache_clean_default_interval_s, &st);
     544         [ +  - ]:       2066 :   if (rc < 0)
     545                 :            :     return rc;
     546                 :       2066 :   clean_interval = (time_t)rc;
     547                 :            : 
     548                 :            :   /* Check timestamp of interval file to see whether cleaning is necessary.  */
     549         [ +  + ]:       2066 :   if (time(NULL) - st.st_mtime < clean_interval)
     550                 :            :     /* Interval has not passed, skip cleaning.  */
     551                 :            :     return 0;
     552                 :            : 
     553                 :            :   /* Update timestamp representing when the cache was last cleaned.
     554                 :            :      Do it at the start to reduce the number of threads trying to do a
     555                 :            :      cleanup simultaneously.  */
     556                 :          2 :   utime (interval_path, NULL);
     557                 :            : 
     558                 :            :   /* Read max unused age value from config file.  */
     559                 :          2 :   rc = debuginfod_config_cache(c, max_unused_path,
     560                 :            :                                cache_default_max_unused_age_s, &st);
     561         [ +  - ]:          2 :   if (rc < 0)
     562                 :            :     return rc;
     563                 :          2 :   max_unused_age = (time_t)rc;
     564                 :            : 
     565                 :          2 :   char * const dirs[] = { cache_path, NULL, };
     566                 :            : 
     567                 :          2 :   FTS *fts = fts_open(dirs, 0, NULL);
     568         [ -  + ]:          2 :   if (fts == NULL)
     569                 :          0 :     return -errno;
     570                 :            : 
     571                 :          2 :   regex_t re;
     572                 :          2 :   const char * pattern = ".*/(metadata.*|[a-f0-9]+(/debuginfo|/executable|/source.*|))$"; /* include dirs */
     573                 :            :   /* NB: also matches .../section/ subdirs, so extracted section files also get cleaned. */
     574         [ +  - ]:          2 :   if (regcomp (&re, pattern, REG_EXTENDED | REG_NOSUB) != 0)
     575                 :            :     return -ENOMEM;
     576                 :            : 
     577                 :          2 :   FTSENT *f;
     578                 :          2 :   long files = 0;
     579                 :          2 :   time_t now = time(NULL);
     580         [ +  + ]:         28 :   while ((f = fts_read(fts)) != NULL)
     581                 :            :     {
     582                 :            :       /* ignore any files that do not match the pattern.  */
     583         [ +  + ]:         24 :       if (regexec (&re, f->fts_path, 0, NULL, 0) != 0)
     584                 :         16 :         continue;
     585                 :            : 
     586                 :          8 :       files++;
     587         [ -  + ]:          8 :       if (c->progressfn) /* inform/check progress callback */
     588         [ #  # ]:          0 :         if ((c->progressfn) (c, files, 0))
     589                 :            :           break;
     590                 :            : 
     591      [ +  -  + ]:          8 :       switch (f->fts_info)
     592                 :            :         {
     593                 :          0 :         case FTS_F:
     594                 :            :           /* delete file if max_unused_age has been met or exceeded w.r.t. atime.  */
     595         [ #  # ]:          0 :           if (now - f->fts_statp->st_atime >= max_unused_age)
     596                 :          0 :             (void) unlink (f->fts_path);
     597                 :            :           break;
     598                 :            : 
     599                 :          4 :         case FTS_DP:
     600                 :            :           /* Remove if old & empty.  Weaken race against concurrent creation by 
     601                 :            :              checking mtime. */
     602         [ -  + ]:          4 :           if (now - f->fts_statp->st_mtime >= max_unused_age)
     603                 :          4 :             (void) rmdir (f->fts_path);
     604                 :            :           break;
     605                 :            : 
     606                 :            :         default:
     607                 :         26 :           ;
     608                 :            :         }
     609                 :            :     }
     610                 :          2 :   fts_close (fts);
     611                 :          2 :   regfree (&re);
     612                 :            : 
     613                 :          2 :   return 0;
     614                 :            : }
     615                 :            : 
     616                 :            : 
     617                 :            : #define MAX_BUILD_ID_BYTES 64
     618                 :            : 
     619                 :            : 
     620                 :            : static void
     621                 :       2084 : add_default_headers(debuginfod_client *client)
     622                 :            : {
     623         [ +  + ]:       2084 :   if (client->user_agent_set_p)
     624                 :        556 :     return;
     625                 :            : 
     626                 :            :   /* Compute a User-Agent: string to send.  The more accurately this
     627                 :            :      describes this host, the likelier that the debuginfod servers
     628                 :            :      might be able to locate debuginfo for us. */
     629                 :            : 
     630                 :       1528 :   char* utspart = NULL;
     631                 :       1528 :   struct utsname uts;
     632                 :       1528 :   int rc = 0;
     633                 :       1528 :   rc = uname (&uts);
     634         [ +  - ]:       1528 :   if (rc == 0)
     635                 :       1528 :     rc = asprintf(& utspart, "%s/%s", uts.sysname, uts.machine);
     636         [ -  + ]:       1528 :   if (rc < 0)
     637                 :          0 :     utspart = NULL;
     638                 :            : 
     639                 :       1528 :   FILE *f = fopen ("/etc/os-release", "r");
     640         [ -  + ]:       1528 :   if (f == NULL)
     641                 :          0 :     f = fopen ("/usr/lib/os-release", "r");
     642                 :       1528 :   char *id = NULL;
     643                 :       1528 :   char *version = NULL;
     644         [ +  - ]:       1528 :   if (f != NULL)
     645                 :            :     {
     646         [ +  + ]:      10696 :       while (id == NULL || version == NULL)
     647                 :            :         {
     648                 :       9168 :           char buf[128];
     649                 :       9168 :           char *s = &buf[0];
     650         [ +  - ]:       9168 :           if (fgets (s, sizeof(buf), f) == NULL)
     651                 :            :             break;
     652                 :            : 
     653                 :       9168 :           int len = strlen (s);
     654         [ -  + ]:       9168 :           if (len < 3)
     655                 :          0 :             continue;
     656         [ +  - ]:       9168 :           if (s[len - 1] == '\n')
     657                 :            :             {
     658                 :       9168 :               s[len - 1] = '\0';
     659                 :       9168 :               len--;
     660                 :            :             }
     661                 :            : 
     662                 :       9168 :           char *v = strchr (s, '=');
     663   [ +  -  -  + ]:       9168 :           if (v == NULL || strlen (v) < 2)
     664                 :          0 :             continue;
     665                 :            : 
     666                 :            :           /* Split var and value. */
     667                 :       9168 :           *v = '\0';
     668                 :       9168 :           v++;
     669                 :            : 
     670                 :            :           /* Remove optional quotes around value string. */
     671         [ +  + ]:       9168 :           if (*v == '"' || *v == '\'')
     672                 :            :             {
     673                 :       6112 :               v++;
     674                 :       6112 :               s[len - 1] = '\0';
     675                 :            :             }
     676   [ +  -  +  + ]:       9168 :           if (id == NULL && strcmp (s, "ID") == 0)
     677                 :       1528 :             id = strdup (v);
     678   [ +  +  +  + ]:       9168 :           if (version == NULL && strcmp (s, "VERSION_ID") == 0)
     679                 :       1528 :             version = strdup (v);
     680                 :            :         }
     681                 :       1528 :       fclose (f);
     682                 :            :     }
     683                 :            : 
     684                 :       1528 :   char *ua = NULL;
     685   [ +  -  +  - ]:       3056 :   rc = asprintf(& ua, "User-Agent: %s/%s,%s,%s/%s",
     686                 :            :                 PACKAGE_NAME, PACKAGE_VERSION,
     687         [ -  + ]:       1528 :                 utspart ?: "",
     688                 :            :                 id ?: "",
     689                 :            :                 version ?: "");
     690         [ -  + ]:       1528 :   if (rc < 0)
     691                 :          0 :     ua = NULL;
     692                 :            : 
     693         [ +  - ]:       1528 :   if (ua)
     694                 :       1528 :     (void) debuginfod_add_http_header (client, ua);
     695                 :            : 
     696                 :       1528 :   free (ua);
     697                 :       1528 :   free (id);
     698                 :       1528 :   free (version);
     699                 :       1528 :   free (utspart);
     700                 :            : }
     701                 :            : 
     702                 :            : /* Add HTTP headers found in the given file, one per line. Blank lines or invalid
     703                 :            :  * headers are ignored.
     704                 :            :  */
     705                 :            : static void
     706                 :          0 : add_headers_from_file(debuginfod_client *client, const char* filename)
     707                 :            : {
     708                 :          0 :   int vds = client->verbose_fd;
     709                 :          0 :   FILE *f = fopen (filename, "r");
     710         [ #  # ]:          0 :   if (f == NULL)
     711                 :            :     {
     712         [ #  # ]:          0 :       if (vds >= 0)
     713                 :          0 :         dprintf(vds, "header file %s: %s\n", filename, strerror(errno));
     714                 :          0 :       return;
     715                 :            :     }
     716                 :            : 
     717                 :          0 :   while (1)
     718                 :          0 :     {
     719                 :          0 :       char buf[8192];
     720                 :          0 :       char *s = &buf[0];
     721         [ #  # ]:          0 :       if (feof(f))
     722                 :            :         break;
     723         [ #  # ]:          0 :       if (fgets (s, sizeof(buf), f) == NULL)
     724                 :            :         break;
     725         [ #  # ]:          0 :       for (char *c = s; *c != '\0'; ++c)
     726         [ #  # ]:          0 :         if (!isspace(*c))
     727                 :          0 :           goto nonempty;
     728                 :          0 :       continue;
     729                 :          0 :     nonempty:
     730                 :          0 :       ;
     731                 :          0 :       size_t last = strlen(s)-1;
     732         [ #  # ]:          0 :       if (s[last] == '\n')
     733                 :          0 :         s[last] = '\0';
     734                 :          0 :       int rc = debuginfod_add_http_header(client, s);
     735         [ #  # ]:          0 :       if (rc < 0 && vds >= 0)
     736                 :          0 :         dprintf(vds, "skipping bad header: %s\n", strerror(-rc));
     737                 :            :     }
     738                 :          0 :   fclose (f);
     739                 :            : }
     740                 :            : 
     741                 :            : 
     742                 :            : #define xalloc_str(p, fmt, args...)        \
     743                 :            :   do                                       \
     744                 :            :     {                                      \
     745                 :            :       if (asprintf (&p, fmt, args) < 0)    \
     746                 :            :         {                                  \
     747                 :            :           p = NULL;                        \
     748                 :            :           rc = -ENOMEM;                    \
     749                 :            :           goto out;                        \
     750                 :            :         }                                  \
     751                 :            :     } while (0)
     752                 :            : 
     753                 :            : 
     754                 :            : /* Offer a basic form of progress tracing */
     755                 :            : static int
     756                 :          2 : default_progressfn (debuginfod_client *c, long a, long b)
     757                 :            : {
     758                 :          2 :   const char* url = debuginfod_get_url (c);
     759                 :          2 :   int len = 0;
     760                 :            : 
     761                 :            :   /* We prefer to print the host part of the URL to keep the
     762                 :            :      message short. */
     763                 :          2 :   if (url != NULL)
     764                 :            :     {
     765                 :          2 :       const char* buildid = strstr(url, "buildid/");
     766         [ +  - ]:          2 :       if (buildid != NULL)
     767                 :          2 :         len = (buildid - url);
     768                 :            :       else
     769                 :          0 :         len = strlen(url);
     770                 :            :     }
     771                 :            : 
     772         [ -  + ]:          2 :   if (b == 0 || url==NULL) /* early stage */
     773                 :          2 :     dprintf(STDERR_FILENO,
     774                 :          0 :             "\rDownloading %c", "-/|\\"[a % 4]);
     775         [ -  + ]:          2 :   else if (b < 0) /* download in progress but unknown total length */
     776                 :          0 :     dprintf(STDERR_FILENO,
     777                 :            :             "\rDownloading from %.*s %ld",
     778                 :            :             len, url, a);
     779                 :            :   else /* download in progress, and known total length */
     780                 :          2 :     dprintf(STDERR_FILENO,
     781                 :            :             "\rDownloading from %.*s %ld/%ld",
     782                 :            :             len, url, a, b);
     783                 :          2 :   c->default_progressfn_printed_p = 1;
     784                 :            : 
     785                 :          2 :   return 0;
     786                 :            : }
     787                 :            : 
     788                 :            : /* This is a callback function that receives http response headers in buffer for use
     789                 :            :  * in this program. https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html is the
     790                 :            :  * online documentation.
     791                 :            :  */
     792                 :            : static size_t
     793                 :      13479 : header_callback (char * buffer, size_t size, size_t numitems, void * userdata)
     794                 :            : {
     795                 :      13479 :   struct handle_data *data = (struct handle_data *) userdata;
     796         [ -  + ]:      13479 :   if (size != 1)
     797                 :            :     return 0;
     798         [ +  - ]:      13479 :   if (data->client
     799         [ +  + ]:      13479 :       && data->client->verbose_fd >= 0
     800         [ +  + ]:       9730 :       && numitems > 2)
     801                 :       8646 :     dprintf (data->client->verbose_fd, "header %.*s", (int)numitems, buffer);
     802                 :            :   // Some basic checks to ensure the headers received are of the expected format
     803         [ +  + ]:      13479 :   if (strncasecmp(buffer, "X-DEBUGINFOD", 11)
     804         [ +  + ]:       3154 :       || buffer[numitems-2] != '\r'
     805         [ +  - ]:       3152 :       || buffer[numitems-1] != '\n'
     806         [ -  + ]:       3152 :       || (buffer == strstr(buffer, ":")) ){
     807                 :            :     return numitems;
     808                 :            :   }
     809                 :            :   /* Temporary buffer for realloc */
     810                 :       3152 :   char *temp = NULL;
     811                 :       3152 :   temp = realloc(data->response_data, data->response_data_size + numitems);
     812         [ -  + ]:       3152 :   if (temp == NULL)
     813                 :            :     return 0;
     814                 :            : 
     815                 :       3152 :   memcpy(temp + data->response_data_size, buffer, numitems-1);
     816                 :       3152 :   data->response_data = temp;
     817                 :       3152 :   data->response_data_size += numitems-1;
     818                 :       3152 :   data->response_data[data->response_data_size-1] = '\n';
     819                 :       3152 :   data->response_data[data->response_data_size] = '\0';
     820                 :       3152 :   return numitems;
     821                 :            : }
     822                 :            : 
     823                 :            : 
     824                 :            : static size_t
     825                 :         14 : metadata_callback (char * buffer, size_t size, size_t numitems, void * userdata)
     826                 :            : {
     827         [ -  + ]:         14 :   if (size != 1)
     828                 :            :     return 0;
     829                 :            :   /* Temporary buffer for realloc */
     830                 :         14 :   char *temp = NULL;
     831                 :         14 :   struct handle_data *data = (struct handle_data *) userdata;
     832                 :         14 :   temp = realloc(data->metadata, data->metadata_size + numitems + 1);
     833         [ -  + ]:         14 :   if (temp == NULL)
     834                 :            :     return 0;
     835                 :            :   
     836                 :         14 :   memcpy(temp + data->metadata_size, buffer, numitems);
     837                 :         14 :   data->metadata = temp;
     838                 :         14 :   data->metadata_size += numitems;
     839                 :         14 :   data->metadata[data->metadata_size] = '\0';
     840                 :         14 :   return numitems;
     841                 :            : }
     842                 :            : 
     843                 :            : 
     844                 :            : /* This function takes a copy of DEBUGINFOD_URLS, server_urls, and
     845                 :            :  * separates it into an array of urls to query, each with a
     846                 :            :  * corresponding IMA policy. The url_subdir is either 'buildid' or
     847                 :            :  * 'metadata', corresponding to the query type. Returns 0 on success
     848                 :            :  * and -Posix error on failure.
     849                 :            :  */
     850                 :            : int
     851                 :       2022 : init_server_urls(char* url_subdir, const char* type,
     852                 :            :                  char *server_urls, char ***server_url_list, ima_policy_t **url_ima_policies,
     853                 :            :                  int *num_urls, int vfd)
     854                 :            : {
     855                 :            :   /* Initialize the memory to zero */
     856                 :       2022 :   char *strtok_saveptr;
     857                 :       2022 :   ima_policy_t verification_mode = ignore; // The default mode  
     858                 :       2022 :   char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
     859                 :            :   /* Count number of URLs.  */
     860                 :       2022 :   int n = 0;
     861                 :            : 
     862         [ +  + ]:       4106 :   while (server_url != NULL)
     863                 :            :     {
     864                 :            :       // When we encountered a (well-formed) token off the form
     865                 :            :       // ima:foo, we update the policy under which results from that
     866                 :            :       // server will be ima verified
     867         [ -  + ]:       2084 :       if (startswith(server_url, "ima:"))
     868                 :            :         {
     869                 :            : #ifdef ENABLE_IMA_VERIFICATION
     870                 :            :           ima_policy_t m = ima_policy_str2enum(server_url + strlen("ima:"));
     871                 :            :           if(m != undefined)
     872                 :            :             verification_mode = m;
     873                 :            :           else if (vfd >= 0)
     874                 :            :             dprintf(vfd, "IMA mode not recognized, skipping %s\n", server_url);
     875                 :            : #else
     876         [ #  # ]:          0 :           if (vfd >= 0)
     877                 :          0 :             dprintf(vfd, "IMA signature verification is not enabled, treating %s as ima:ignore\n", server_url);
     878                 :            : #endif
     879                 :          0 :           goto continue_next_url;
     880                 :            :         }
     881                 :            : 
     882                 :       2084 :       if (verification_mode==enforcing &&
     883                 :            :           0==strcmp(url_subdir, "buildid") &&
     884                 :            :           0==strcmp(type,"section")) // section queries are unsecurable
     885                 :            :         {
     886                 :            :           if (vfd >= 0)
     887                 :            :             dprintf(vfd, "skipping server %s section query in IMA enforcing mode\n", server_url);
     888                 :            :           goto continue_next_url;
     889                 :            :         }
     890                 :            : 
     891                 :            :       // Construct actual URL for libcurl
     892                 :       2084 :       int r;
     893                 :       2084 :       char *tmp_url;
     894   [ +  -  +  + ]:       2084 :       if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
     895                 :       1082 :         r = asprintf(&tmp_url, "%s%s", server_url, url_subdir);
     896                 :            :       else
     897                 :       1002 :         r = asprintf(&tmp_url, "%s/%s", server_url, url_subdir);
     898                 :            : 
     899         [ +  - ]:       2084 :       if (r == -1)
     900                 :          0 :         return -ENOMEM;
     901                 :            :       
     902                 :            :       /* PR 27983: If the url is duplicate, skip it */
     903                 :            :       int url_index;
     904         [ +  + ]:       2214 :       for (url_index = 0; url_index < n; ++url_index)
     905                 :            :         {
     906         [ +  + ]:        134 :           if(strcmp(tmp_url, (*server_url_list)[url_index]) == 0)
     907                 :            :             {
     908                 :            :               url_index = -1;
     909                 :            :               break;
     910                 :            :             }
     911                 :            :         }
     912         [ +  + ]:       2084 :       if (url_index == -1)
     913                 :            :         {
     914         [ +  - ]:          4 :           if (vfd >= 0)
     915                 :          4 :             dprintf(vfd, "duplicate url: %s, skipping\n", tmp_url);
     916                 :          4 :           free(tmp_url);
     917                 :            :         }
     918                 :            :       else
     919                 :            :         {
     920                 :            :           /* Have unique URL, save it, along with its IMA verification tag. */
     921                 :       2080 :           n ++;
     922         [ +  - ]:       2080 :           if (NULL == (*server_url_list = reallocarray(*server_url_list, n, sizeof(char*)))
     923         [ -  + ]:       2080 :               || NULL == (*url_ima_policies = reallocarray(*url_ima_policies, n, sizeof(ima_policy_t))))
     924                 :            :             {
     925                 :          0 :               free (tmp_url);
     926                 :          0 :               return -ENOMEM;
     927                 :            :             }
     928                 :       2080 :           (*server_url_list)[n-1] = tmp_url;
     929                 :       2080 :           if(NULL != url_ima_policies) (*url_ima_policies)[n-1] = verification_mode;
     930                 :            :         }
     931                 :            : 
     932                 :       2084 :     continue_next_url:
     933                 :       2084 :       server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
     934                 :            :     }
     935                 :       2022 :   *num_urls = n;
     936                 :       2022 :   return 0;
     937                 :            : }
     938                 :            : 
     939                 :            : /* Some boilerplate for checking curl_easy_setopt.  */
     940                 :            : #define curl_easy_setopt_ck(H,O,P) do {                 \
     941                 :            :       CURLcode curl_res = curl_easy_setopt (H,O,P);     \
     942                 :            :       if (curl_res != CURLE_OK)                         \
     943                 :            :             {                                           \
     944                 :            :               if (vfd >= 0)                          \
     945                 :            :                 dprintf (vfd,                           \
     946                 :            :                          "Bad curl_easy_setopt: %s\n",        \
     947                 :            :                          curl_easy_strerror(curl_res)); \
     948                 :            :               return -EINVAL;                           \
     949                 :            :             }                                           \
     950                 :            :       } while (0)
     951                 :            : 
     952                 :            : 
     953                 :            : /*
     954                 :            :  * This function initializes a CURL handle. It takes optional callbacks for the write
     955                 :            :  * function and the header function, which if defined will use userdata of type struct handle_data*.
     956                 :            :  * Specifically the data[i] within an array of struct handle_data's.
     957                 :            :  * Returns 0 on success and -Posix error on failure.
     958                 :            :  */
     959                 :            : int
     960                 :       3172 : init_handle(debuginfod_client *client,
     961                 :            :   size_t (*w_callback)(char *buffer, size_t size, size_t nitems, void *userdata),
     962                 :            :   size_t (*h_callback)(char *buffer, size_t size, size_t nitems, void *userdata),
     963                 :            :   struct handle_data *data, int i, long timeout,
     964                 :            :   int vfd)
     965                 :            : {
     966                 :       3172 :   data->handle = curl_easy_init();
     967         [ +  - ]:       3172 :   if (data->handle == NULL)
     968                 :            :     return -ENETUNREACH;
     969                 :            : 
     970         [ +  + ]:       3172 :   if (vfd >= 0)
     971                 :       1114 :     dprintf (vfd, "url %d %s\n", i, data->url);
     972                 :            : 
     973                 :            :   /* Only allow http:// + https:// + file:// so we aren't being
     974                 :            :     redirected to some unsupported protocol.
     975                 :            :     libcurl will fail if we request a single protocol that is not
     976                 :            :     available. https missing is the most likely issue  */
     977                 :            : #if CURL_AT_LEAST_VERSION(7, 85, 0)
     978   [ -  +  -  +  :       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_PROTOCOLS_STR,
                   -  - ]
     979                 :            :                       curl_has_https ? "https,http,file" : "http,file");
     980                 :            : #else
     981                 :            :   curl_easy_setopt_ck(data->handle, CURLOPT_PROTOCOLS,
     982                 :            :                       ((curl_has_https ? CURLPROTO_HTTPS : 0) | CURLPROTO_HTTP | CURLPROTO_FILE));
     983                 :            : #endif
     984   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_URL, data->url);
     985         [ +  + ]:       3172 :   if (vfd >= 0)
     986         [ -  + ]:       1114 :     curl_easy_setopt_ck(data->handle, CURLOPT_ERRORBUFFER,
     987                 :            :       data->errbuf);
     988         [ +  - ]:       3172 :   if (w_callback)
     989                 :            :     {
     990   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck(data->handle,
     991                 :            :                           CURLOPT_WRITEFUNCTION, w_callback);
     992   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck(data->handle, CURLOPT_WRITEDATA, data);
     993                 :            :     }
     994         [ +  - ]:       3172 :   if (timeout > 0)
     995                 :            :     {
     996                 :            :       /* Make sure there is at least some progress,
     997                 :            :          try to get at least 100K per timeout seconds.  */
     998   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck (data->handle, CURLOPT_LOW_SPEED_TIME,
     999                 :            :                            timeout);
    1000   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck (data->handle, CURLOPT_LOW_SPEED_LIMIT,
    1001                 :            :                            100 * 1024L);
    1002                 :            :     }
    1003   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_FILETIME, (long) 1);
    1004   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_FOLLOWLOCATION, (long) 1);
    1005   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_FAILONERROR, (long) 1);
    1006   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_NOSIGNAL, (long) 1);
    1007         [ +  - ]:       3172 :   if (h_callback)
    1008                 :            :     {
    1009   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck(data->handle,
    1010                 :            :                           CURLOPT_HEADERFUNCTION, h_callback);
    1011   [ -  +  -  - ]:       3172 :       curl_easy_setopt_ck(data->handle, CURLOPT_HEADERDATA, data);
    1012                 :            :     }
    1013                 :            :   #if LIBCURL_VERSION_NUM >= 0x072a00 /* 7.42.0 */
    1014   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_PATH_AS_IS, (long) 1);
    1015                 :            :   #else
    1016                 :            :   /* On old curl; no big deal, canonicalization here is almost the
    1017                 :            :       same, except perhaps for ? # type decorations at the tail. */
    1018                 :            :   #endif
    1019   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_AUTOREFERER, (long) 1);
    1020   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_ACCEPT_ENCODING, "");
    1021   [ -  +  -  - ]:       3172 :   curl_easy_setopt_ck(data->handle, CURLOPT_HTTPHEADER, client->headers);
    1022                 :            : 
    1023                 :            :   return 0;
    1024                 :            : }
    1025                 :            : 
    1026                 :            : 
    1027                 :            : /*
    1028                 :            :  * This function busy-waits on one or more curl queries to complete. This can
    1029                 :            :  * be controlled via only_one, which, if true, will find the first winner and exit
    1030                 :            :  * once found. If positive maxtime and maxsize dictate the maximum allowed wait times
    1031                 :            :  * and download sizes respectively. Returns 0 on success and -Posix error on failure.
    1032                 :            :  */
    1033                 :            : int
    1034                 :       3110 : perform_queries(CURLM *curlm, CURL **target_handle, struct handle_data *data, debuginfod_client *c,
    1035                 :            :                 int num_urls, long maxtime, long maxsize, bool only_one, int vfd, int *committed_to)
    1036                 :            : {
    1037                 :       3110 :   int still_running = -1;
    1038                 :       3110 :   long loops = 0;
    1039                 :       3110 :   *committed_to = -1;
    1040                 :       3110 :   bool verbose_reported = false;
    1041                 :       3110 :   struct timespec start_time, cur_time;
    1042         [ -  + ]:       3110 :   if (c->winning_headers != NULL)
    1043                 :            :     {
    1044                 :          0 :       free (c->winning_headers);
    1045                 :          0 :       c->winning_headers = NULL;
    1046                 :            :     }
    1047   [ +  +  -  + ]:       3110 :   if (maxtime > 0 && clock_gettime(CLOCK_MONOTONIC_RAW, &start_time) == -1)
    1048                 :          0 :     return -errno;
    1049                 :       6548 :   long delta = 0;
    1050                 :       6548 :   do
    1051                 :            :     {
    1052                 :            :       /* Check to see how long querying is taking. */
    1053         [ +  + ]:       6548 :       if (maxtime > 0)
    1054                 :            :         {
    1055         [ -  + ]:          5 :           if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time) == -1)
    1056                 :          0 :             return -errno;
    1057                 :          5 :           delta = cur_time.tv_sec - start_time.tv_sec;
    1058         [ -  + ]:          5 :           if ( delta >  maxtime)
    1059                 :            :             {
    1060                 :          0 :               dprintf(vfd, "Timeout with max time=%lds and transfer time=%lds\n", maxtime, delta );
    1061                 :          0 :               return -ETIME;
    1062                 :            :             }
    1063                 :            :         }
    1064                 :            :       /* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT.  */
    1065                 :       6548 :       curl_multi_wait(curlm, NULL, 0, 1000, NULL);
    1066                 :       6548 :       CURLMcode curlm_res = curl_multi_perform(curlm, &still_running);
    1067                 :            :       
    1068         [ +  + ]:       6548 :       if (only_one)
    1069                 :            :         {
    1070                 :            :           /* If the target file has been found, abort the other queries.  */
    1071   [ +  -  +  + ]:       6496 :           if (target_handle && *target_handle != NULL)
    1072                 :            :             {
    1073         [ +  + ]:       3096 :               for (int i = 0; i < num_urls; i++)
    1074         [ +  + ]:       1620 :                 if (data[i].handle != *target_handle)
    1075                 :        144 :                   curl_multi_remove_handle(curlm, data[i].handle);
    1076                 :            :                 else
    1077                 :            :                   {
    1078                 :       1476 :                     *committed_to = i;
    1079         [ +  + ]:       1476 :                     if (c->winning_headers == NULL)
    1080                 :            :                       {
    1081                 :       1428 :                         c->winning_headers = data[*committed_to].response_data;
    1082   [ +  +  +  - ]:       1428 :                         if (vfd >= 0 && c->winning_headers != NULL)
    1083                 :       1074 :                           dprintf(vfd, "\n%s", c->winning_headers);
    1084                 :       1428 :                         data[*committed_to].response_data = NULL;
    1085                 :       1428 :                         data[*committed_to].response_data_size = 0;
    1086                 :            :                       }
    1087                 :            :                   }
    1088                 :            :             }
    1089                 :            :           
    1090   [ +  +  +  + ]:       6496 :           if (vfd >= 0 && !verbose_reported && *committed_to >= 0)
    1091                 :            :             {
    1092   [ -  +  -  - ]:       1074 :               bool pnl = (c->default_progressfn_printed_p && vfd == STDERR_FILENO);
    1093         [ +  - ]:       1074 :               dprintf (vfd, "%scommitted to url %d\n", pnl ? "\n" : "",
    1094                 :            :                        *committed_to);
    1095         [ -  + ]:       1074 :               if (pnl)
    1096                 :          0 :                 c->default_progressfn_printed_p = 0;
    1097                 :            :               verbose_reported = true;
    1098                 :            :             }
    1099                 :            :         }
    1100                 :            :       
    1101         [ -  + ]:       6548 :       if (curlm_res != CURLM_OK)
    1102                 :            :         {
    1103      [ #  #  # ]:          0 :           switch (curlm_res)
    1104                 :            :             {
    1105                 :          0 :             case CURLM_CALL_MULTI_PERFORM: continue;
    1106                 :            :             case CURLM_OUT_OF_MEMORY: return -ENOMEM;
    1107                 :          0 :             default: return -ENETUNREACH;
    1108                 :            :             }
    1109                 :            :         }
    1110                 :            :       
    1111                 :       6548 :       long dl_size = -1;
    1112   [ +  +  +  +  :       6548 :       if (target_handle && *target_handle && (c->progressfn || maxsize > 0))
             +  +  -  + ]
    1113                 :            :         {
    1114                 :            :           /* Get size of file being downloaded. NB: If going through
    1115                 :            :              deflate-compressing proxies, this number is likely to be
    1116                 :            :              unavailable, so -1 may show. */
    1117                 :         30 :           CURLcode curl_res;
    1118                 :            : #if CURL_AT_LEAST_VERSION(7, 55, 0)
    1119                 :         30 :           curl_off_t cl;
    1120                 :         30 :           curl_res = curl_easy_getinfo(*target_handle,
    1121                 :            :                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
    1122                 :            :                                        &cl);
    1123   [ +  -  -  + ]:         30 :           if (curl_res == CURLE_OK && cl >= 0)
    1124                 :            :             dl_size = (cl > LONG_MAX ? LONG_MAX : (long)cl);
    1125                 :            : #else
    1126                 :            :           double cl;
    1127                 :            :           curl_res = curl_easy_getinfo(*target_handle,
    1128                 :            :                                        CURLINFO_CONTENT_LENGTH_DOWNLOAD,
    1129                 :            :                                        &cl);
    1130                 :            :           if (curl_res == CURLE_OK && cl >= 0)
    1131                 :            :             dl_size = (cl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)cl);
    1132                 :            : #endif
    1133                 :            :           /* If Content-Length is -1, try to get the size from
    1134                 :            :              X-Debuginfod-Size */
    1135         [ #  # ]:          0 :           if (dl_size == -1 && c->winning_headers != NULL)
    1136                 :            :             {
    1137                 :          0 :               long xdl;
    1138                 :          0 :               char *hdr = strcasestr(c->winning_headers, "x-debuginfod-size");
    1139                 :          0 :               size_t off = strlen("x-debuginfod-size:");
    1140                 :            :               
    1141   [ #  #  #  # ]:          0 :               if (hdr != NULL && sscanf(hdr + off, "%ld", &xdl) == 1)
    1142                 :          0 :                 dl_size = xdl;
    1143                 :            :             }
    1144                 :            :         }
    1145                 :            :       
    1146         [ +  + ]:       6548 :       if (c->progressfn) /* inform/check progress callback */
    1147                 :            :         {
    1148                 :       3332 :           loops ++;
    1149                 :       3332 :           long pa = loops; /* default param for progress callback */
    1150   [ +  -  +  + ]:       3332 :           if (target_handle && *target_handle) /* we've committed to a server; report its download progress */
    1151                 :            :             {
    1152                 :            :               /* PR30809: Check actual size of cached file.  This same
    1153                 :            :                  fd is shared by all the multi-curl handles (but only
    1154                 :            :                  one will end up writing to it).  Another way could be
    1155                 :            :                  to tabulate totals in debuginfod_write_callback(). */
    1156                 :         30 :               struct stat cached;
    1157                 :         30 :               int statrc = fstat(data[*committed_to].fd, &cached);
    1158         [ +  - ]:         30 :               if (statrc == 0)
    1159                 :         30 :                 pa = (long) cached.st_size;
    1160                 :            :               else
    1161                 :            :                 {
    1162                 :            :                   /* Otherwise, query libcurl for its tabulated total.
    1163                 :            :                      However, that counts http body length, not
    1164                 :            :                      decoded/decompressed content length, so does not
    1165                 :            :                      measure quite the same thing as dl. */
    1166                 :          0 :                   CURLcode curl_res;
    1167                 :            : #if CURL_AT_LEAST_VERSION(7, 55, 0)
    1168                 :          0 :                   curl_off_t dl;
    1169                 :          0 :                   curl_res = curl_easy_getinfo(target_handle,
    1170                 :            :                                                CURLINFO_SIZE_DOWNLOAD_T,
    1171                 :            :                                                &dl);
    1172   [ #  #  #  # ]:          0 :                   if (curl_res == 0 && dl >= 0)
    1173                 :          0 :                     pa = (dl > LONG_MAX ? LONG_MAX : (long)dl);
    1174                 :            : #else
    1175                 :            :                   double dl;
    1176                 :            :                   curl_res = curl_easy_getinfo(target_handle,
    1177                 :            :                                                CURLINFO_SIZE_DOWNLOAD,
    1178                 :            :                                                &dl);
    1179                 :            :                   if (curl_res == 0)
    1180                 :            :                     pa = (dl >= (double)(LONG_MAX+1UL) ? LONG_MAX : (long)dl);
    1181                 :            : #endif
    1182                 :            :                 }
    1183                 :            :               
    1184   [ -  +  +  - ]:         30 :               if ((*c->progressfn) (c, pa, dl_size == -1 ? 0 : dl_size))
    1185                 :            :                 break;
    1186                 :            :             }
    1187                 :            :         }
    1188                 :            :       /* Check to see if we are downloading something which exceeds maxsize, if set.*/
    1189   [ +  +  +  +  :       6548 :       if (target_handle && *target_handle && dl_size > maxsize && maxsize > 0)
                   -  + ]
    1190                 :            :         {
    1191         [ #  # ]:          0 :           if (vfd >=0)
    1192                 :          0 :             dprintf(vfd, "Content-Length too large.\n");
    1193                 :          0 :           return -EFBIG;
    1194                 :            :         }
    1195         [ +  + ]:       6548 :     } while (still_running);
    1196                 :            :   
    1197                 :            :   return 0;
    1198                 :            : }
    1199                 :            : 
    1200                 :            : 
    1201                 :            : /* Copy SRC to DEST, s,/,#,g */
    1202                 :            : 
    1203                 :            : static void
    1204                 :       1128 : path_escape (const char *src, char *dest, size_t dest_len)
    1205                 :            : {
    1206                 :            :   /* PR32218: Reversibly-escaping src character-by-character, for
    1207                 :            :      large enough strings, risks ENAMETOOLONG errors.  For long names,
    1208                 :            :      a simple hash based generated name instead, while still
    1209                 :            :      attempting to preserve the as much of the content as possible.
    1210                 :            :      It's possible that absurd choices of incoming names will collide
    1211                 :            :      or still get truncated, but c'est la vie.
    1212                 :            :   */
    1213                 :            :   
    1214                 :            :   /* Compute a three-way min() for the actual output string generated. */
    1215         [ -  + ]:       1128 :   assert (dest_len > 10); /* Space enough for degenerate case of
    1216                 :            :                              "HASHHASH-\0". NB: dest_len is not
    1217                 :            :                              user-controlled. */
    1218                 :            :   /* Use only NAME_MAX/2 characters in the output file name.
    1219                 :            :      ENAMETOOLONG has been observed even on 300-ish character names on
    1220                 :            :      some filesystems. */
    1221                 :       1128 :   const size_t max_dest_len = NAME_MAX/2;
    1222                 :       1128 :   dest_len = dest_len > max_dest_len ? max_dest_len : dest_len;
    1223                 :            :   /* Use only strlen(src)+10 bytes, if that's smaller.  Yes, we could
    1224                 :            :      just fit an entire escaped name in there in theory, without the
    1225                 :            :      hash+etc.  But then again the hashing protects against #-escape
    1226                 :            :      aliasing collisions: "foo[bar" "foo]bar" both escape to
    1227                 :            :      "foo#bar", thus aliasing, but have different "HASH-foo#bar".
    1228                 :            :   */
    1229                 :       1128 :   const size_t hash_prefix_destlen = strlen(src)+10; /* DEADBEEF-src\0 */
    1230                 :       1128 :   dest_len = dest_len > hash_prefix_destlen ? hash_prefix_destlen : dest_len;
    1231                 :            : 
    1232                 :       1128 :   char *dest_write = dest + dest_len - 1;
    1233                 :       1128 :   (*dest_write--) = '\0'; /* Ensure a \0 there. */
    1234                 :            : 
    1235                 :            :   /* Copy from back toward front, preferring to keep the .extension. */
    1236         [ +  + ]:      84596 :   for (int fi=strlen(src)-1; fi >= 0 && dest_write >= dest; fi--)
    1237                 :            :     {
    1238                 :      83468 :       char src_char = src[fi];
    1239         [ +  + ]:      83468 :       switch (src_char)
    1240                 :            :         {
    1241                 :            :           /* Pass through ordinary identifier chars. */
    1242                 :      74668 :         case '.': case '-': case '_':
    1243                 :            :         case 'a'...'z':
    1244                 :            :         case 'A'...'Z':
    1245                 :            :         case '0'...'9':
    1246                 :      74668 :           *dest_write-- = src_char;
    1247                 :      74668 :           break;
    1248                 :            :           
    1249                 :            :           /* Replace everything else, esp. security-sensitive /. */
    1250                 :       8800 :         default:
    1251                 :       8800 :           *dest_write-- = '#';
    1252                 :       8800 :           break;
    1253                 :            :         }
    1254                 :            :     }
    1255                 :            : 
    1256                 :            :   /* djb2 hash algorithm: DJBX33A */
    1257                 :            :   unsigned long hash = 5381;
    1258                 :            :   const char *c = src;
    1259         [ +  + ]:      85398 :   while (*c)
    1260                 :      84270 :     hash = ((hash << 5) + hash) + *c++;
    1261                 :       1128 :   char name_hash_str [9];
    1262                 :            :   /* Truncate to 4 bytes; along with the remaining hundredish bytes of text,
    1263                 :            :      should be ample against accidental collisions. */
    1264                 :       1128 :   snprintf (name_hash_str, sizeof(name_hash_str), "%08x", (unsigned int) hash);
    1265                 :       1128 :   memcpy (&dest[0], name_hash_str, 8); /* Overwrite the first few characters */
    1266                 :       1128 :   dest[8] = '-'; /* Add a bit of punctuation to make hash stand out */
    1267                 :       1128 : }
    1268                 :            : 
    1269                 :            : /* Attempt to update the atime */
    1270                 :            : static void
    1271                 :         60 : update_atime (int fd)
    1272                 :            : {
    1273                 :         60 :   struct timespec tvs[2];
    1274                 :            : 
    1275                 :         60 :   tvs[0].tv_sec = tvs[1].tv_sec = 0;
    1276                 :         60 :   tvs[0].tv_nsec = UTIME_NOW;
    1277                 :         60 :   tvs[1].tv_nsec = UTIME_OMIT;
    1278                 :            : 
    1279                 :         60 :   (void) futimens (fd, tvs);  /* best effort */
    1280                 :         60 : }
    1281                 :            : 
    1282                 :            : /* Attempt to read an ELF/DWARF section with name SECTION from FD and write
    1283                 :            :    it to a separate file in the debuginfod cache.  If successful the absolute
    1284                 :            :    path of the separate file containing SECTION will be stored in USR_PATH.
    1285                 :            :    FD_PATH is the absolute path for FD.
    1286                 :            : 
    1287                 :            :    If the section cannot be extracted, then return a negative error code.
    1288                 :            :    -ENOENT indicates that the parent file was able to be read but the
    1289                 :            :    section name was not found.  -EEXIST indicates that the section was
    1290                 :            :    found but had type SHT_NOBITS.  */
    1291                 :            : 
    1292                 :            : static int
    1293                 :         12 : extract_section (int fd, const char *section, char *fd_path, char **usr_path)
    1294                 :            : {
    1295                 :         12 :   elf_version (EV_CURRENT);
    1296                 :            : 
    1297                 :         12 :   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
    1298         [ +  - ]:         12 :   if (elf == NULL)
    1299                 :            :     return -EIO;
    1300                 :            : 
    1301                 :         12 :   size_t shstrndx;
    1302                 :         12 :   int rc = elf_getshdrstrndx (elf, &shstrndx);
    1303         [ -  + ]:         12 :   if (rc < 0)
    1304                 :            :     {
    1305                 :          0 :       rc = -EIO;
    1306                 :          0 :       goto out;
    1307                 :            :     }
    1308                 :            : 
    1309                 :         12 :   int sec_fd = -1;
    1310                 :         12 :   char *sec_path_tmp = NULL;
    1311                 :         12 :   Elf_Scn *scn = NULL;
    1312                 :            : 
    1313                 :            :   /* Try to find the target section and copy the contents into a
    1314                 :            :      separate file.  */
    1315                 :        432 :   while (true)
    1316                 :        210 :     {
    1317                 :        222 :       scn = elf_nextscn (elf, scn);
    1318         [ -  + ]:        222 :       if (scn == NULL)
    1319                 :            :         {
    1320                 :          0 :           rc = -ENOENT;
    1321                 :          4 :           goto out;
    1322                 :            :         }
    1323                 :        222 :       GElf_Shdr shdr_storage;
    1324                 :        222 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_storage);
    1325         [ -  + ]:        222 :       if (shdr == NULL)
    1326                 :            :         {
    1327                 :          0 :           rc = -EIO;
    1328                 :          0 :           goto out;
    1329                 :            :         }
    1330                 :            : 
    1331                 :        222 :       const char *scn_name = elf_strptr (elf, shstrndx, shdr->sh_name);
    1332         [ -  + ]:        222 :       if (scn_name == NULL)
    1333                 :            :         {
    1334                 :          0 :           rc = -EIO;
    1335                 :          0 :           goto out;
    1336                 :            :         }
    1337         [ +  + ]:        222 :       if (strcmp (scn_name, section) == 0)
    1338                 :            :         {
    1339                 :            :           /* We found the desired section.  */
    1340         [ +  + ]:         12 :           if (shdr->sh_type == SHT_NOBITS)
    1341                 :            :             {
    1342                 :          4 :               rc = -EEXIST;
    1343                 :          4 :               goto out;
    1344                 :            :             }
    1345                 :            : 
    1346                 :          8 :           Elf_Data *data = NULL;
    1347                 :          8 :           data = elf_rawdata (scn, NULL);
    1348         [ -  + ]:          8 :           if (data == NULL)
    1349                 :            :             {
    1350                 :          0 :               rc = -EIO;
    1351                 :          0 :               goto out;
    1352                 :            :             }
    1353                 :            : 
    1354         [ -  + ]:          8 :           if (data->d_buf == NULL)
    1355                 :            :             {
    1356                 :          0 :               rc = -EIO;
    1357                 :          0 :               goto out;
    1358                 :            :             }
    1359                 :            : 
    1360                 :            :           /* Compute the absolute filename we'll write the section to.
    1361                 :            :              Replace the last component of FD_PATH with the path-escaped
    1362                 :            :              section filename.  */
    1363                 :          8 :           int i = strlen (fd_path);
    1364         [ +  - ]:         92 :           while (i >= 0)
    1365                 :            :             {
    1366         [ +  + ]:         92 :               if (fd_path[i] == '/')
    1367                 :            :                 {
    1368                 :          8 :                   fd_path[i] = '\0';
    1369                 :          8 :                   break;
    1370                 :            :                 }
    1371                 :         84 :               --i;
    1372                 :            :             }
    1373                 :            : 
    1374                 :          8 :           char suffix[NAME_MAX];
    1375                 :          8 :           path_escape (section, suffix, sizeof(suffix));
    1376                 :          8 :           rc = asprintf (&sec_path_tmp, "%s/section-%s.XXXXXX",
    1377                 :            :                          fd_path, suffix);
    1378         [ -  + ]:          8 :           if (rc == -1)
    1379                 :            :             {
    1380                 :          0 :               rc = -ENOMEM;
    1381                 :          0 :               goto out1;
    1382                 :            :             }
    1383                 :            : 
    1384                 :          8 :           sec_fd = mkstemp (sec_path_tmp);
    1385         [ -  + ]:          8 :           if (sec_fd < 0)
    1386                 :            :             {
    1387                 :          0 :               rc = -EIO;
    1388                 :          8 :               goto out2;
    1389                 :            :             }
    1390                 :            : 
    1391                 :          8 :           ssize_t res = write_retry (sec_fd, data->d_buf, data->d_size);
    1392   [ +  -  -  + ]:          8 :           if (res < 0 || (size_t) res != data->d_size)
    1393                 :            :             {
    1394                 :          0 :               rc = -EIO;
    1395                 :          0 :               goto out3;
    1396                 :            :             }
    1397                 :            : 
    1398                 :            :           /* Success.  Rename tmp file and update USR_PATH.  */
    1399                 :          8 :           char *sec_path;
    1400         [ -  + ]:          8 :           if (asprintf (&sec_path, "%s/section-%s", fd_path, section) == -1)
    1401                 :            :             {
    1402                 :          0 :               rc = -ENOMEM;
    1403                 :          0 :               goto out3;
    1404                 :            :             }
    1405                 :            : 
    1406                 :          8 :           rc = rename (sec_path_tmp, sec_path);
    1407         [ -  + ]:          8 :           if (rc < 0)
    1408                 :            :             {
    1409                 :          0 :               free (sec_path);
    1410                 :          0 :               rc = -EIO;
    1411                 :          0 :               goto out3;
    1412                 :            :             }
    1413                 :            : 
    1414         [ +  - ]:          8 :           if (usr_path != NULL)
    1415                 :          8 :             *usr_path = sec_path;
    1416                 :            :           else
    1417                 :          0 :             free (sec_path);
    1418                 :          8 :           update_atime(fd);
    1419                 :          8 :           rc = sec_fd;
    1420                 :          8 :           goto out2;
    1421                 :            :         }
    1422                 :            :     }
    1423                 :            : 
    1424                 :          0 : out3:
    1425                 :          0 :   close (sec_fd);
    1426                 :          0 :   unlink (sec_path_tmp);
    1427                 :            : 
    1428                 :          8 : out2:
    1429                 :          8 :   free (sec_path_tmp);
    1430                 :            : 
    1431                 :         12 : out1:
    1432                 :         12 : out:
    1433                 :         12 :   elf_end (elf);
    1434                 :         12 :   return rc;
    1435                 :            : }
    1436                 :            : 
    1437                 :            : /* Search TARGET_CACHE_DIR for a debuginfo or executable file containing
    1438                 :            :    an ELF/DWARF section with name SCN_NAME.  If found, extract the section
    1439                 :            :    to a separate file in TARGET_CACHE_DIR and return a file descriptor
    1440                 :            :    for the section file. The path for this file will be stored in USR_PATH.
    1441                 :            :    Return a negative errno if unsuccessful.  -ENOENT indicates that SCN_NAME
    1442                 :            :    is confirmed to not exist.  */
    1443                 :            : 
    1444                 :            : static int
    1445                 :         16 : cache_find_section (const char *scn_name, const char *target_cache_dir,
    1446                 :            :                     char **usr_path)
    1447                 :            : {
    1448                 :         16 :   int debug_fd;
    1449                 :         16 :   int rc = -EEXIST;
    1450                 :         16 :   char parent_path[PATH_MAX];
    1451                 :            : 
    1452                 :            :   /* Check the debuginfo first.  */
    1453                 :         16 :   snprintf (parent_path, PATH_MAX, "%s/debuginfo", target_cache_dir);
    1454                 :         16 :   debug_fd = open (parent_path, O_RDONLY);
    1455         [ +  + ]:         16 :   if (debug_fd >= 0)
    1456                 :            :     {
    1457                 :          8 :       rc = extract_section (debug_fd, scn_name, parent_path, usr_path);
    1458                 :          8 :       close (debug_fd);
    1459                 :            :     }
    1460                 :            : 
    1461                 :            :   /* If the debuginfo file couldn't be found or the section type was
    1462                 :            :      SHT_NOBITS, check the executable.  */
    1463         [ +  + ]:          8 :   if (rc == -EEXIST)
    1464                 :            :     {
    1465                 :         12 :       snprintf (parent_path, PATH_MAX, "%s/executable", target_cache_dir);
    1466                 :         12 :       int exec_fd = open (parent_path, O_RDONLY);
    1467                 :            : 
    1468         [ +  + ]:         12 :       if (exec_fd >= 0)
    1469                 :            :         {
    1470                 :          4 :           rc = extract_section (exec_fd, scn_name, parent_path, usr_path);
    1471                 :          4 :           close (exec_fd);
    1472                 :            : 
    1473                 :            :           /* Don't return -ENOENT if the debuginfo wasn't opened.  The
    1474                 :            :              section may exist in the debuginfo but not the executable.  */
    1475         [ -  + ]:          4 :           if (debug_fd < 0 && rc == -ENOENT)
    1476                 :          0 :             rc = -EREMOTE;
    1477                 :            :         }
    1478                 :            :     }
    1479                 :            : 
    1480                 :         16 :   return rc;
    1481                 :            : }
    1482                 :            : 
    1483                 :            : 
    1484                 :            : #ifdef ENABLE_IMA_VERIFICATION
    1485                 :            : /* Extract the hash algorithm name from the signature header, of which
    1486                 :            :    there are several types.  The name will be used for openssl hashing
    1487                 :            :    of the file content.  The header doesn't need to be super carefully
    1488                 :            :    parsed, because if any part of it is wrong, be it the hash
    1489                 :            :    algorithm number or hash value or whatever, it will fail
    1490                 :            :    computation or verification.  Return NULL in case of error.  */
    1491                 :            : static const char*
    1492                 :            : get_signature_params(debuginfod_client *c, unsigned char *bin_sig)
    1493                 :            : {
    1494                 :            :   int hashalgo = 0;
    1495                 :            :   
    1496                 :            :   switch (bin_sig[0])
    1497                 :            :     {
    1498                 :            :     case EVM_IMA_XATTR_DIGSIG:
    1499                 :            : #ifdef IMA_VERITY_DIGSIG /* missing on debian-i386 trybot */
    1500                 :            :     case IMA_VERITY_DIGSIG:
    1501                 :            : #endif
    1502                 :            :       break;
    1503                 :            :     default:
    1504                 :            :       if (c->verbose_fd >= 0)
    1505                 :            :         dprintf (c->verbose_fd, "Unknown ima digsig %d\n", (int)bin_sig[0]);
    1506                 :            :       return NULL;
    1507                 :            :     }
    1508                 :            : 
    1509                 :            :   switch (bin_sig[1])
    1510                 :            :     {
    1511                 :            :     case DIGSIG_VERSION_2:
    1512                 :            :       {
    1513                 :            :         struct signature_v2_hdr hdr_v2;
    1514                 :            :         memcpy(& hdr_v2, & bin_sig[1], sizeof(struct signature_v2_hdr));
    1515                 :            :         hashalgo = hdr_v2.hash_algo;
    1516                 :            :         break;
    1517                 :            :       }
    1518                 :            :     default:
    1519                 :            :       if (c->verbose_fd >= 0)
    1520                 :            :         dprintf (c->verbose_fd, "Unknown ima signature version %d\n", (int)bin_sig[1]);
    1521                 :            :       return NULL;
    1522                 :            :     }
    1523                 :            : 
    1524                 :            :   switch (hashalgo)
    1525                 :            :     {
    1526                 :            :     case PKEY_HASH_SHA1: return "sha1";
    1527                 :            :     case PKEY_HASH_SHA256: return "sha256";
    1528                 :            :       // (could add many others from enum pkey_hash_algo)
    1529                 :            :     default:
    1530                 :            :       if (c->verbose_fd >= 0)
    1531                 :            :         dprintf (c->verbose_fd, "Unknown ima pkey hash %d\n", hashalgo);
    1532                 :            :       return NULL;
    1533                 :            :     }
    1534                 :            : }
    1535                 :            : 
    1536                 :            : 
    1537                 :            : /* Verify given hash against given signature blob.  Return 0 on ok, -errno otherwise. */
    1538                 :            : static int
    1539                 :            : debuginfod_verify_hash(debuginfod_client *c, const unsigned char *hash, int size,
    1540                 :            :                        const char *hash_algo, unsigned char *sig, int siglen)
    1541                 :            : {
    1542                 :            :   int ret = -EBADMSG;
    1543                 :            :   struct public_key_entry *pkey;
    1544                 :            :   struct signature_v2_hdr hdr;
    1545                 :            :   EVP_PKEY_CTX *ctx;
    1546                 :            :   const EVP_MD *md;
    1547                 :            : 
    1548                 :            :   memcpy(&hdr, sig, sizeof(struct signature_v2_hdr)); /* avoid just aliasing */
    1549                 :            : 
    1550                 :            :   if (c->verbose_fd >= 0)
    1551                 :            :     dprintf (c->verbose_fd, "Searching for ima keyid %04x\n", ntohl(hdr.keyid));
    1552                 :            :         
    1553                 :            :   /* Find the matching public key. */
    1554                 :            :   for (pkey = c->ima_public_keys; pkey != NULL; pkey = pkey->next)
    1555                 :            :     if (pkey->keyid == ntohl(hdr.keyid)) break;
    1556                 :            :   if (!pkey)
    1557                 :            :     return -ENOKEY;
    1558                 :            : 
    1559                 :            :   if (!(ctx = EVP_PKEY_CTX_new(pkey->key, NULL)))
    1560                 :            :     goto err;
    1561                 :            :   if (!EVP_PKEY_verify_init(ctx))
    1562                 :            :     goto err;
    1563                 :            :   if (!(md = EVP_get_digestbyname(hash_algo)))
    1564                 :            :     goto err;
    1565                 :            :   if (!EVP_PKEY_CTX_set_signature_md(ctx, md))
    1566                 :            :     goto err;
    1567                 :            :   ret = EVP_PKEY_verify(ctx, sig + sizeof(hdr),
    1568                 :            :                         siglen - sizeof(hdr), hash, size);
    1569                 :            :   if (ret == 1)
    1570                 :            :     ret = 0; // success!
    1571                 :            :   else if (ret == 0)
    1572                 :            :     ret = -EBADMSG;
    1573                 :            :  err:
    1574                 :            :   EVP_PKEY_CTX_free(ctx);
    1575                 :            :   return ret;
    1576                 :            : }
    1577                 :            : 
    1578                 :            : 
    1579                 :            : 
    1580                 :            : /* Validate an IMA file signature.
    1581                 :            :  * Returns 0 on signature validity, -EINVAL on signature invalidity, -ENOSYS on undefined imaevm machinery,
    1582                 :            :  * -ENOKEY on key issues, or other -errno.
    1583                 :            :  */
    1584                 :            : 
    1585                 :            : static int
    1586                 :            : debuginfod_validate_imasig (debuginfod_client *c, int fd)
    1587                 :            : {
    1588                 :            :   int rc = ENOSYS;
    1589                 :            : 
    1590                 :            :     EVP_MD_CTX *ctx = NULL;
    1591                 :            :     if (!c || !c->winning_headers)
    1592                 :            :     {
    1593                 :            :       rc = -ENODATA;
    1594                 :            :       goto exit_validate;
    1595                 :            :     }
    1596                 :            :     // Extract the HEX IMA-signature from the header
    1597                 :            :     char* sig_buf = NULL;
    1598                 :            :     char* hdr_ima_sig = strcasestr(c->winning_headers, "x-debuginfod-imasignature");
    1599                 :            :     if (!hdr_ima_sig || 1 != sscanf(hdr_ima_sig + strlen("x-debuginfod-imasignature:"), "%ms", &sig_buf))
    1600                 :            :     {
    1601                 :            :       rc = -ENODATA;
    1602                 :            :       goto exit_validate;
    1603                 :            :     }
    1604                 :            :     if (strlen(sig_buf) > MAX_SIGNATURE_SIZE) // reject if too long
    1605                 :            :     {
    1606                 :            :       rc = -EBADMSG;
    1607                 :            :       goto exit_validate;
    1608                 :            :     }
    1609                 :            :     // Convert the hex signature to bin
    1610                 :            :     size_t bin_sig_len = strlen(sig_buf)/2;
    1611                 :            :     unsigned char bin_sig[MAX_SIGNATURE_SIZE/2];
    1612                 :            :     for (size_t b = 0; b < bin_sig_len; b++)
    1613                 :            :       bin_sig[b] = (hex2dec(sig_buf[2*b]) << 4) | hex2dec(sig_buf[2*b+1]);
    1614                 :            : 
    1615                 :            :     // Compute the binary digest of the cached file (with file descriptor fd)
    1616                 :            :     ctx = EVP_MD_CTX_new();
    1617                 :            :     const char* sighash_name = get_signature_params(c, bin_sig) ?: "";
    1618                 :            :     const EVP_MD *md = EVP_get_digestbyname(sighash_name);
    1619                 :            :     if (!ctx || !md || !EVP_DigestInit(ctx, md))
    1620                 :            :     {
    1621                 :            :       rc = -EBADMSG;
    1622                 :            :       goto exit_validate;
    1623                 :            :     }
    1624                 :            : 
    1625                 :            :     long data_len;
    1626                 :            :     char* hdr_data_len = strcasestr(c->winning_headers, "x-debuginfod-size");
    1627                 :            :     if (!hdr_data_len || 1 != sscanf(hdr_data_len + strlen("x-debuginfod-size:") , "%ld", &data_len))
    1628                 :            :     {
    1629                 :            :       rc = -ENODATA;
    1630                 :            :       goto exit_validate;
    1631                 :            :     }
    1632                 :            : 
    1633                 :            :     char file_data[DATA_SIZE]; // imaevm.h data chunk hash size 
    1634                 :            :     ssize_t n;
    1635                 :            :     for(off_t k = 0; k < data_len; k += n)
    1636                 :            :       {
    1637                 :            :         if (-1 == (n = pread(fd, file_data, DATA_SIZE, k)))
    1638                 :            :           {
    1639                 :            :             rc = -errno;
    1640                 :            :             goto exit_validate;
    1641                 :            :           }
    1642                 :            :         
    1643                 :            :         if (!EVP_DigestUpdate(ctx, file_data, n))
    1644                 :            :           {
    1645                 :            :             rc = -EBADMSG;
    1646                 :            :             goto exit_validate;
    1647                 :            :           }
    1648                 :            :       }
    1649                 :            :     
    1650                 :            :     uint8_t bin_dig[MAX_DIGEST_SIZE];
    1651                 :            :     unsigned int bin_dig_len;
    1652                 :            :     if (!EVP_DigestFinal(ctx, bin_dig, &bin_dig_len))
    1653                 :            :     {
    1654                 :            :       rc = -EBADMSG;
    1655                 :            :       goto exit_validate;
    1656                 :            :     }
    1657                 :            : 
    1658                 :            :     // XXX: in case of DIGSIG_VERSION_3, need to hash the file hash, yo dawg
    1659                 :            :     
    1660                 :            :     int res = debuginfod_verify_hash(c,
    1661                 :            :                                      bin_dig, bin_dig_len,
    1662                 :            :                                      sighash_name,
    1663                 :            :                                      & bin_sig[1], bin_sig_len-1); // skip over first byte of signature
    1664                 :            :     if (c->verbose_fd >= 0)
    1665                 :            :       dprintf (c->verbose_fd, "Computed ima signature verification res=%d\n", res);
    1666                 :            :     rc = res;
    1667                 :            : 
    1668                 :            :  exit_validate:
    1669                 :            :     free (sig_buf);
    1670                 :            :     EVP_MD_CTX_free(ctx);
    1671                 :            :     return rc;
    1672                 :            : }
    1673                 :            : #endif /* ENABLE_IMA_VERIFICATION */
    1674                 :            : 
    1675                 :            : 
    1676                 :            : 
    1677                 :            : 
    1678                 :            : /* Helper function to create client cache directory.
    1679                 :            :    $XDG_CACHE_HOME takes priority over $HOME/.cache.
    1680                 :            :    $DEBUGINFOD_CACHE_PATH takes priority over $HOME/.cache and $XDG_CACHE_HOME.
    1681                 :            : 
    1682                 :            :    Return resulting path name or NULL on error.  Caller must free resulting string.
    1683                 :            :  */
    1684                 :            : static char *
    1685                 :       2092 : make_cache_path(void)
    1686                 :            : {
    1687                 :       2092 :   char* cache_path = NULL;
    1688                 :       2092 :   int rc = 0;
    1689                 :            :   /* Determine location of the cache. The path specified by the debuginfod
    1690                 :            :      cache environment variable takes priority.  */
    1691                 :       2092 :   char *cache_var = getenv(DEBUGINFOD_CACHE_PATH_ENV_VAR);
    1692   [ +  -  +  + ]:       2092 :   if (cache_var != NULL && strlen (cache_var) > 0)
    1693         [ -  + ]:       2084 :     xalloc_str (cache_path, "%s", cache_var);
    1694                 :            :   else
    1695                 :            :     {
    1696                 :            :       /* If a cache already exists in $HOME ('/' if $HOME isn't set), then use
    1697                 :            :          that. Otherwise use the XDG cache directory naming format.  */
    1698   [ -  +  -  + ]:         16 :       xalloc_str (cache_path, "%s/%s", getenv ("HOME") ?: "/", cache_default_name);
    1699                 :            : 
    1700                 :          8 :       struct stat st;
    1701         [ +  + ]:          8 :       if (stat (cache_path, &st) < 0)
    1702                 :            :         {
    1703                 :          6 :           char cachedir[PATH_MAX];
    1704                 :          6 :           char *xdg = getenv ("XDG_CACHE_HOME");
    1705                 :            : 
    1706   [ +  -  +  + ]:          6 :           if (xdg != NULL && strlen (xdg) > 0)
    1707                 :          2 :             snprintf (cachedir, PATH_MAX, "%s", xdg);
    1708                 :            :           else
    1709         [ -  + ]:          4 :             snprintf (cachedir, PATH_MAX, "%s/.cache", getenv ("HOME") ?: "/");
    1710                 :            : 
    1711                 :            :           /* Create XDG cache directory if it doesn't exist.  */
    1712         [ +  + ]:          6 :           if (stat (cachedir, &st) == 0)
    1713                 :            :             {
    1714         [ -  + ]:          2 :               if (! S_ISDIR (st.st_mode))
    1715                 :            :                 {
    1716                 :          0 :                   rc = -EEXIST;
    1717                 :          0 :                   goto out1;
    1718                 :            :                 }
    1719                 :            :             }
    1720                 :            :           else
    1721                 :            :             {
    1722                 :          4 :               rc = mkdir (cachedir, 0700);
    1723                 :            : 
    1724                 :            :               /* Also check for EEXIST and S_ISDIR in case another client just
    1725                 :            :                  happened to create the cache.  */
    1726         [ -  + ]:          4 :               if (rc < 0
    1727         [ #  # ]:          0 :                   && (errno != EEXIST
    1728         [ #  # ]:          0 :                       || stat (cachedir, &st) != 0
    1729         [ #  # ]:          0 :                       || ! S_ISDIR (st.st_mode)))
    1730                 :            :                 {
    1731                 :          0 :                   rc = -errno;
    1732                 :          0 :                   goto out1;
    1733                 :            :                 }
    1734                 :            :             }
    1735                 :            : 
    1736                 :          6 :           free (cache_path);
    1737         [ -  + ]:          6 :           xalloc_str (cache_path, "%s/%s", cachedir, cache_xdg_name);
    1738                 :            :         }
    1739                 :            :     }
    1740                 :            : 
    1741                 :       2092 :   goto out;
    1742                 :            :   
    1743                 :          0 :  out1:
    1744                 :          0 :   (void) rc;
    1745                 :          0 :   free (cache_path);
    1746                 :          0 :   cache_path = NULL;
    1747                 :            : 
    1748                 :       2092 :  out:
    1749         [ +  - ]:       2092 :   if (cache_path != NULL)
    1750                 :       2092 :     (void) mkdir (cache_path, 0700); // failures with this mkdir would be caught later too
    1751                 :       2092 :   return cache_path;
    1752                 :            : }
    1753                 :            : 
    1754                 :            : 
    1755                 :            : /* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
    1756                 :            :    with the specified build-id and type (debuginfo, executable, source or
    1757                 :            :    section).  If type is source, then type_arg should be a filename.  If
    1758                 :            :    type is section, then type_arg should be the name of an ELF/DWARF
    1759                 :            :    section.  Otherwise type_arg may be NULL.  Return a file descriptor
    1760                 :            :    for the target if successful, otherwise return an error code.
    1761                 :            : */
    1762                 :            : static int
    1763                 :       6364 : debuginfod_query_server_by_buildid (debuginfod_client *c,
    1764                 :            :                          const unsigned char *build_id,
    1765                 :            :                          int build_id_len,
    1766                 :            :                          const char *type,
    1767                 :            :                          const char *type_arg,
    1768                 :            :                          char **path)
    1769                 :            : {
    1770                 :       6364 :   char *server_urls;
    1771                 :       6364 :   char *urls_envvar;
    1772                 :       6364 :   const char *section = NULL;
    1773                 :       6364 :   const char *filename = NULL;
    1774                 :       6364 :   char *cache_path = NULL;
    1775                 :       6364 :   char *maxage_path = NULL;
    1776                 :       6364 :   char *interval_path = NULL;
    1777                 :       6364 :   char *cache_miss_path = NULL;
    1778                 :       6364 :   char *target_cache_dir = NULL;
    1779                 :       6364 :   char *target_cache_path = NULL;
    1780                 :       6364 :   char *target_cache_tmppath = NULL;
    1781                 :       6364 :   char suffix[NAME_MAX];
    1782                 :       6364 :   char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
    1783                 :       6364 :   int vfd = c->verbose_fd;
    1784                 :       6364 :   int rc, r;
    1785                 :            : 
    1786                 :       6364 :   c->progressfn_cancel = false;
    1787                 :            : 
    1788         [ +  + ]:       6364 :   if (strcmp (type, "source") == 0)
    1789                 :            :     filename = type_arg;
    1790         [ +  + ]:       1048 :   else if (strcmp (type, "section") == 0)
    1791                 :            :     {
    1792                 :         16 :       section = type_arg;
    1793         [ +  - ]:         16 :       if (section == NULL)
    1794                 :            :         return -EINVAL;
    1795                 :            :     }
    1796                 :            : 
    1797         [ +  + ]:       6364 :   if (vfd >= 0)
    1798                 :            :     {
    1799                 :       1092 :       dprintf (vfd, "debuginfod_find_%s ", type);
    1800         [ +  + ]:       1092 :       if (build_id_len == 0) /* expect clean hexadecimal */
    1801                 :         30 :         dprintf (vfd, "%s", (const char *) build_id);
    1802                 :            :       else
    1803         [ +  + ]:      22302 :         for (int i = 0; i < build_id_len; i++)
    1804                 :      21240 :           dprintf (vfd, "%02x", build_id[i]);
    1805         [ +  + ]:       1092 :       if (filename != NULL)
    1806                 :       1058 :         dprintf (vfd, " %s\n", filename);
    1807                 :       1092 :       dprintf (vfd, "\n");
    1808                 :            :     }
    1809                 :            : 
    1810                 :            :   /* Is there any server we can query?  If not, don't do any work,
    1811                 :            :      just return with ENOSYS.  Don't even access the cache.  */
    1812                 :       6364 :   urls_envvar = getenv(DEBUGINFOD_URLS_ENV_VAR);
    1813         [ +  + ]:       6364 :   if (vfd >= 0)
    1814         [ -  + ]:       1092 :     dprintf (vfd, "server urls \"%s\"\n",
    1815                 :            :              urls_envvar != NULL ? urls_envvar : "");
    1816   [ +  +  +  + ]:       6364 :   if (urls_envvar == NULL || urls_envvar[0] == '\0')
    1817                 :            :     {
    1818                 :       4298 :       rc = -ENOSYS;
    1819                 :       4298 :       goto out;
    1820                 :            :     }
    1821                 :            : 
    1822                 :            :   /* Clear the obsolete data from a previous _find operation. */
    1823                 :       2066 :   free (c->url);
    1824                 :       2066 :   c->url = NULL;
    1825                 :       2066 :   free (c->winning_headers);
    1826                 :       2066 :   c->winning_headers = NULL;
    1827                 :            : 
    1828                 :            :   /* PR 27982: Add max size if DEBUGINFOD_MAXSIZE is set. */
    1829                 :       2066 :   long maxsize = 0;
    1830                 :       2066 :   const char *maxsize_envvar;
    1831                 :       2066 :   maxsize_envvar = getenv(DEBUGINFOD_MAXSIZE_ENV_VAR);
    1832         [ +  + ]:       2066 :   if (maxsize_envvar != NULL)
    1833                 :          2 :     maxsize = atol (maxsize_envvar);
    1834                 :            : 
    1835                 :            :   /* PR 27982: Add max time if DEBUGINFOD_MAXTIME is set. */
    1836                 :       2066 :   long maxtime = 0;
    1837                 :       2066 :   const char *maxtime_envvar;
    1838                 :       2066 :   maxtime_envvar = getenv(DEBUGINFOD_MAXTIME_ENV_VAR);
    1839         [ +  + ]:       2066 :   if (maxtime_envvar != NULL)
    1840                 :          2 :     maxtime = atol (maxtime_envvar);
    1841         [ +  + ]:       2066 :   if (maxtime && vfd >= 0)
    1842                 :          2 :     dprintf(vfd, "using max time %lds\n", maxtime);
    1843                 :            : 
    1844                 :       2066 :   const char *headers_file_envvar;
    1845                 :       2066 :   headers_file_envvar = getenv(DEBUGINFOD_HEADERS_FILE_ENV_VAR);
    1846         [ -  + ]:       2066 :   if (headers_file_envvar != NULL)
    1847                 :          0 :     add_headers_from_file(c, headers_file_envvar);
    1848                 :            : 
    1849                 :            :   /* Maxsize is valid*/
    1850         [ +  + ]:       2066 :   if (maxsize > 0)
    1851                 :            :     {
    1852         [ +  - ]:          2 :       if (vfd)
    1853                 :          2 :         dprintf (vfd, "using max size %ldB\n", maxsize);
    1854                 :          2 :       char *size_header = NULL;
    1855                 :          2 :       rc = asprintf (&size_header, "X-DEBUGINFOD-MAXSIZE: %ld", maxsize);
    1856         [ -  + ]:          2 :       if (rc < 0)
    1857                 :            :         {
    1858                 :          0 :           rc = -ENOMEM;
    1859                 :          0 :           goto out;
    1860                 :            :         }
    1861                 :          2 :       rc = debuginfod_add_http_header(c, size_header);
    1862                 :          2 :       free(size_header);
    1863         [ -  + ]:          2 :       if (rc < 0)
    1864                 :          0 :         goto out;
    1865                 :            :     }
    1866                 :       2066 :   add_default_headers(c);
    1867                 :            : 
    1868                 :            :   /* Copy lowercase hex representation of build_id into buf.  */
    1869         [ +  + ]:       2066 :   if (vfd >= 0)
    1870                 :       1092 :     dprintf (vfd, "checking build-id\n");
    1871   [ +  -  +  + ]:       2066 :   if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
    1872                 :       1002 :       (build_id_len == 0 &&
    1873         [ -  + ]:       1002 :        strlen ((const char *) build_id) > MAX_BUILD_ID_BYTES*2))
    1874                 :            :     {
    1875                 :          0 :       rc = -EINVAL;
    1876                 :          0 :       goto out;
    1877                 :            :     }
    1878                 :            : 
    1879         [ +  + ]:       2066 :   if (build_id_len == 0) /* expect clean hexadecimal */
    1880                 :       1002 :     strcpy (build_id_bytes, (const char *) build_id);
    1881                 :            :   else
    1882         [ +  + ]:      22344 :     for (int i = 0; i < build_id_len; i++)
    1883                 :      21280 :       sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]);
    1884                 :            : 
    1885         [ +  + ]:       2066 :   if (filename != NULL)
    1886                 :            :     {
    1887         [ +  + ]:       1104 :       if (vfd >= 0)
    1888                 :       1058 :         dprintf (vfd, "checking filename\n");
    1889         [ -  + ]:       1104 :       if (filename[0] != '/') // must start with /
    1890                 :            :         {
    1891                 :          0 :           rc = -EINVAL;
    1892                 :          0 :           goto out;
    1893                 :            :         }
    1894                 :            : 
    1895                 :       1104 :       path_escape (filename, suffix, sizeof(suffix));
    1896                 :            :       /* If the DWARF filenames are super long, this could exceed
    1897                 :            :          PATH_MAX and truncate/collide.  Oh well, that'll teach
    1898                 :            :          them! */
    1899                 :            :     }
    1900         [ +  + ]:        962 :   else if (section != NULL)
    1901                 :         16 :     path_escape (section, suffix, sizeof(suffix));
    1902                 :            :   else
    1903                 :        946 :     suffix[0] = '\0';
    1904                 :            : 
    1905   [ +  +  +  + ]:       2066 :   if (suffix[0] != '\0' && vfd >= 0)
    1906                 :       1074 :     dprintf (vfd, "suffix %s\n", suffix);
    1907                 :            : 
    1908                 :            :   /* set paths needed to perform the query
    1909                 :            :      example format:
    1910                 :            :      cache_path:        $HOME/.cache
    1911                 :            :      target_cache_dir:  $HOME/.cache/0123abcd
    1912                 :            :      target_cache_path: $HOME/.cache/0123abcd/debuginfo
    1913                 :            :      target_cache_path: $HOME/.cache/0123abcd/executable-.debug_info
    1914                 :            :      target_cache_path: $HOME/.cache/0123abcd/source-HASH-#PATH#TO#SOURCE
    1915                 :            :   */
    1916                 :            : 
    1917                 :       2066 :   cache_path = make_cache_path();
    1918         [ -  + ]:       2066 :   if (!cache_path)
    1919                 :            :     {
    1920                 :          0 :       rc = -ENOMEM;
    1921                 :          0 :       goto out;
    1922                 :            :     }
    1923         [ -  + ]:       2066 :   xalloc_str (target_cache_dir, "%s/%s", cache_path, build_id_bytes);
    1924                 :       2066 :   (void) mkdir (target_cache_dir, 0700); // failures with this mkdir would be caught later too
    1925                 :            : 
    1926         [ +  + ]:       2066 :   if (suffix[0] != '\0') /* section, source queries */
    1927         [ -  + ]:       1120 :     xalloc_str (target_cache_path, "%s/%s-%s", target_cache_dir, type, suffix);
    1928                 :            :   else
    1929         [ -  + ]:        946 :     xalloc_str (target_cache_path, "%s/%s", target_cache_dir, type);
    1930         [ -  + ]:       2066 :   xalloc_str (target_cache_tmppath, "%s.XXXXXX", target_cache_path);
    1931                 :            : 
    1932                 :            :   /* XXX combine these */
    1933         [ -  + ]:       2066 :   xalloc_str (interval_path, "%s/%s", cache_path, cache_clean_interval_filename);
    1934         [ -  + ]:       2066 :   xalloc_str (cache_miss_path, "%s/%s", cache_path, cache_miss_filename);
    1935         [ -  + ]:       2066 :   xalloc_str (maxage_path, "%s/%s", cache_path, cache_max_unused_age_filename);
    1936                 :            : 
    1937         [ +  + ]:       2066 :   if (vfd >= 0)
    1938                 :       1092 :     dprintf (vfd, "checking cache dir %s\n", cache_path);
    1939                 :            : 
    1940                 :            :   /* Make sure cache dir exists. debuginfo_clean_cache will then make
    1941                 :            :      sure the interval, cache_miss and maxage files exist.  */
    1942         [ +  - ]:       2066 :   if (mkdir (cache_path, ACCESSPERMS) != 0
    1943         [ -  + ]:       2066 :       && errno != EEXIST)
    1944                 :            :     {
    1945                 :          0 :       rc = -errno;
    1946                 :          0 :       goto out;
    1947                 :            :     }
    1948                 :            : 
    1949                 :       2066 :   rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path);
    1950         [ -  + ]:       2066 :   if (rc != 0)
    1951                 :          0 :     goto out;
    1952                 :            : 
    1953                 :            :   /* Check if the target is already in the cache. */
    1954                 :       2066 :   int fd = open(target_cache_path, O_RDONLY);
    1955         [ +  + ]:       2066 :   if (fd >= 0)
    1956                 :            :     {
    1957                 :         56 :       struct stat st;
    1958         [ -  + ]:         56 :       if (fstat(fd, &st) != 0)
    1959                 :            :         {
    1960                 :          0 :           rc = -errno;
    1961                 :          0 :           close (fd);
    1962                 :         54 :           goto out;
    1963                 :            :         }
    1964                 :            : 
    1965                 :            :       /* If the file is non-empty, then we are done. */
    1966         [ +  + ]:         56 :       if (st.st_size > 0)
    1967                 :            :         {
    1968         [ +  - ]:         52 :           if (path != NULL)
    1969                 :            :             {
    1970                 :         52 :               *path = strdup(target_cache_path);
    1971         [ -  + ]:         52 :               if (*path == NULL)
    1972                 :            :                 {
    1973                 :          0 :                   rc = -errno;
    1974                 :          0 :                   close (fd);
    1975                 :          0 :                   goto out;
    1976                 :            :                 }
    1977                 :            :             }
    1978                 :            :           /* Success!!!! */
    1979                 :         52 :           update_atime(fd);
    1980                 :         52 :           rc = fd;
    1981                 :         52 :           goto out;
    1982                 :            :         }
    1983                 :            :       else
    1984                 :            :         {
    1985                 :            :           /* The file is empty. Attempt to download only if enough time
    1986                 :            :              has passed since the last attempt. */
    1987                 :          4 :           time_t cache_miss;
    1988                 :          4 :           time_t target_mtime = st.st_mtime;
    1989                 :            : 
    1990                 :          4 :           close(fd); /* no need to hold onto the negative-hit file descriptor */
    1991                 :            :           
    1992                 :          4 :           rc = debuginfod_config_cache(c, cache_miss_path,
    1993                 :            :                                        cache_miss_default_s, &st);
    1994         [ -  + ]:          4 :           if (rc < 0)
    1995                 :          0 :             goto out;
    1996                 :            : 
    1997                 :          4 :           cache_miss = (time_t)rc;
    1998         [ +  + ]:          4 :           if (time(NULL) - target_mtime <= cache_miss)
    1999                 :            :             {
    2000                 :          2 :               rc = -ENOENT;
    2001                 :          2 :               goto out;
    2002                 :            :             }
    2003                 :            :           else
    2004                 :            :             /* TOCTOU non-problem: if another task races, puts a working
    2005                 :            :                download or an empty file in its place, unlinking here just
    2006                 :            :                means WE will try to download again as uncached. */
    2007                 :          2 :             unlink(target_cache_path);
    2008                 :            :         }
    2009                 :            :     }
    2010         [ -  + ]:       2010 :   else if (errno == EACCES)
    2011                 :            :     /* Ensure old 000-permission files are not lingering in the cache. */
    2012                 :          0 :     unlink(target_cache_path);
    2013                 :            : 
    2014         [ +  + ]:       2012 :   if (section != NULL)
    2015                 :            :     {
    2016                 :            :       /* Try to extract the section from a cached file before querying
    2017                 :            :          any servers.  */
    2018                 :         16 :       rc = cache_find_section (section, target_cache_dir, path);
    2019                 :            : 
    2020                 :            :       /* If the section was found or confirmed to not exist, then we
    2021                 :            :          are done.  */
    2022         [ +  + ]:         16 :       if (rc >= 0 || rc == -ENOENT)
    2023                 :          8 :         goto out;
    2024                 :            :     }
    2025                 :            : 
    2026                 :       2004 :   long timeout = default_timeout;
    2027                 :       2004 :   const char* timeout_envvar = getenv(DEBUGINFOD_TIMEOUT_ENV_VAR);
    2028         [ -  + ]:       2004 :   if (timeout_envvar != NULL)
    2029                 :          0 :     timeout = atoi (timeout_envvar);
    2030                 :            : 
    2031         [ +  + ]:       2004 :   if (vfd >= 0)
    2032                 :       1084 :     dprintf (vfd, "using timeout %ld\n", timeout);
    2033                 :            : 
    2034                 :            :   /* make a copy of the envvar so it can be safely modified.  */
    2035                 :       2004 :   server_urls = strdup(urls_envvar);
    2036         [ -  + ]:       2004 :   if (server_urls == NULL)
    2037                 :            :     {
    2038                 :          0 :       rc = -ENOMEM;
    2039                 :          0 :       goto out;
    2040                 :            :     }
    2041                 :            :   /* thereafter, goto out0 on error*/
    2042                 :            : 
    2043                 :            :   /* Because of a race with cache cleanup / rmdir, try to mkdir/mkstemp up to twice. */
    2044         [ +  - ]:       2004 :   for(int i=0; i<2; i++)
    2045                 :            :     {
    2046                 :            :       /* (re)create target directory in cache */
    2047                 :       2004 :       (void) mkdir(target_cache_dir, 0700); /* files will be 0400 later */
    2048                 :            :       
    2049                 :            :       /* NB: write to a temporary file first, to avoid race condition of
    2050                 :            :          multiple clients checking the cache, while a partially-written or empty
    2051                 :            :          file is in there, being written from libcurl. */
    2052                 :       2004 :       fd = mkstemp (target_cache_tmppath);
    2053         [ -  + ]:       2004 :       if (fd >= 0) break;
    2054                 :            :     }
    2055         [ -  + ]:       2004 :   if (fd < 0) /* Still failed after two iterations. */
    2056                 :            :     {
    2057                 :          0 :       rc = -errno;
    2058                 :          0 :       goto out0;
    2059                 :            :     }
    2060                 :            : 
    2061                 :       2004 :   char **server_url_list = NULL;
    2062                 :       2004 :   ima_policy_t* url_ima_policies = NULL;
    2063                 :       2004 :   char *server_url;
    2064                 :       2004 :   int num_urls;
    2065                 :       2004 :   r = init_server_urls("buildid", type, server_urls, &server_url_list, &url_ima_policies, &num_urls, vfd);
    2066         [ -  + ]:       2004 :   if (0 != r)
    2067                 :            :     {
    2068                 :          0 :       rc = r;
    2069                 :          0 :       goto out1;
    2070                 :            :     }
    2071                 :            : 
    2072                 :            :   /* No URLs survived parsing / filtering?  Abort abort abort. */
    2073         [ -  + ]:       2004 :   if (num_urls == 0)
    2074                 :            :     {
    2075                 :          0 :       rc = -ENOSYS;
    2076                 :          0 :       goto out1;
    2077                 :            :     }
    2078                 :            :   
    2079                 :       2004 :   int retry_limit = default_retry_limit;
    2080                 :       2004 :   const char* retry_limit_envvar = getenv(DEBUGINFOD_RETRY_LIMIT_ENV_VAR);
    2081         [ +  + ]:       2004 :   if (retry_limit_envvar != NULL)
    2082                 :          4 :     retry_limit = atoi (retry_limit_envvar);
    2083                 :            : 
    2084                 :       2004 :   CURLM *curlm = c->server_mhandle;
    2085                 :            : 
    2086                 :            :   /* Tracks which handle should write to fd. Set to the first
    2087                 :            :      handle that is ready to write the target file to the cache.  */
    2088                 :       2004 :   CURL *target_handle = NULL;
    2089                 :       2004 :   struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls);
    2090         [ -  + ]:       2004 :   if (data == NULL)
    2091                 :            :     {
    2092                 :          0 :       rc = -ENOMEM;
    2093                 :          0 :       goto out1;
    2094                 :            :     }
    2095                 :            : 
    2096                 :            :   /* thereafter, goto out2 on error.  */
    2097                 :            : 
    2098                 :            :  /*The beginning of goto block query_in_parallel.*/
    2099                 :       2004 :  query_in_parallel:
    2100                 :       3092 :   rc = -ENOENT; /* Reset rc to default.*/
    2101                 :            : 
    2102                 :            :   /* Initialize handle_data with default values. */
    2103         [ +  + ]:       6238 :   for (int i = 0; i < num_urls; i++)
    2104                 :            :     {
    2105                 :       3146 :       data[i].handle = NULL;
    2106                 :       3146 :       data[i].fd = -1;
    2107                 :       3146 :       data[i].errbuf[0] = '\0';
    2108                 :       3146 :       data[i].response_data = NULL;
    2109                 :       3146 :       data[i].response_data_size = 0;
    2110                 :            :     }
    2111                 :            : 
    2112                 :       3092 :   char *escaped_string = NULL;
    2113                 :       3092 :   size_t escaped_strlen = 0;
    2114         [ +  + ]:       3092 :   if (filename)
    2115                 :            :     {
    2116                 :       1104 :       escaped_string = curl_easy_escape(&target_handle, filename+1, 0);
    2117         [ -  + ]:       1104 :       if (!escaped_string)
    2118                 :            :         {
    2119                 :          0 :           rc = -ENOMEM;
    2120                 :          0 :           goto out2;
    2121                 :            :         }
    2122                 :       1104 :       char *loc = escaped_string;
    2123                 :       1104 :       escaped_strlen = strlen(escaped_string);
    2124         [ +  + ]:       8568 :       while ((loc = strstr(loc, "%2F")))
    2125                 :            :         {
    2126                 :       7464 :           loc[0] = '/';
    2127                 :            :           //pull the string back after replacement
    2128                 :            :           // loc-escaped_string finds the distance from the origin to the new location
    2129                 :            :           // - 2 accounts for the 2F which remain and don't need to be measured.
    2130                 :            :           // The two above subtracted from escaped_strlen yields the remaining characters
    2131                 :            :           // in the string which we want to pull back
    2132                 :       7464 :           memmove(loc+1, loc+3,escaped_strlen - (loc-escaped_string) - 2);
    2133                 :            :           //Because the 2F was overwritten in the memmove (as desired) escaped_strlen is
    2134                 :            :           // now two shorter.
    2135                 :       7464 :           escaped_strlen -= 2;
    2136                 :            :         }
    2137                 :            :     }
    2138                 :            :   /* Initialize each handle.  */
    2139         [ +  + ]:       6238 :   for (int i = 0; i < num_urls; i++)
    2140                 :            :     {
    2141         [ +  - ]:       3146 :       if ((server_url = server_url_list[i]) == NULL)
    2142                 :            :         break;
    2143         [ +  + ]:       3146 :       if (vfd >= 0)
    2144                 :            : #ifdef ENABLE_IMA_VERIFICATION
    2145                 :            :         dprintf (vfd, "init server %d %s [IMA verification policy: %s]\n", i, server_url, ima_policy_enum2str(url_ima_policies[i]));
    2146                 :            : #else
    2147                 :       1114 :         dprintf (vfd, "init server %d %s\n", i, server_url);
    2148                 :            : #endif
    2149                 :            : 
    2150                 :       3146 :       data[i].fd = fd;
    2151                 :       3146 :       data[i].target_handle = &target_handle;
    2152                 :       3146 :       data[i].client = c;
    2153                 :            : 
    2154         [ +  + ]:       3146 :       if (filename) /* must start with / */
    2155                 :            :         {
    2156                 :            :           /* PR28034 escape characters in completed url to %hh format. */
    2157                 :       1104 :           snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
    2158                 :            :                    build_id_bytes, type, escaped_string);
    2159                 :            :         }
    2160         [ +  + ]:       2042 :       else if (section)
    2161                 :          8 :         snprintf(data[i].url, PATH_MAX, "%s/%s/%s/%s", server_url,
    2162                 :            :                  build_id_bytes, type, section);
    2163                 :            :       else
    2164                 :       2034 :         snprintf(data[i].url, PATH_MAX, "%s/%s/%s", server_url, build_id_bytes, type);
    2165                 :            : 
    2166                 :       3146 :       r = init_handle(c, debuginfod_write_callback, header_callback, &data[i], i, timeout, vfd);
    2167         [ -  + ]:       3146 :       if (0 != r)
    2168                 :            :         {
    2169                 :          0 :           rc = r;
    2170         [ #  # ]:          0 :           if (filename) curl_free (escaped_string);
    2171                 :          0 :           goto out2;
    2172                 :            :         }
    2173                 :            : 
    2174                 :       3146 :       curl_multi_add_handle(curlm, data[i].handle);
    2175                 :            :     }
    2176                 :            : 
    2177         [ +  + ]:       3092 :   if (filename) curl_free(escaped_string);
    2178                 :            : 
    2179                 :            :   /* Query servers in parallel.  */
    2180         [ +  + ]:       3092 :   if (vfd >= 0)
    2181                 :       1108 :     dprintf (vfd, "query %d urls in parallel\n", num_urls);
    2182                 :       3092 :   int committed_to;
    2183                 :       3092 :   r = perform_queries(curlm, &target_handle, data, c, num_urls, maxtime, maxsize, true,  vfd, &committed_to);
    2184         [ -  + ]:       3092 :   if (0 != r)
    2185                 :            :     {
    2186                 :          0 :       rc = r;
    2187                 :          0 :       goto out2;
    2188                 :            :     }
    2189                 :            : 
    2190                 :            :   /* Check whether a query was successful. If so, assign its handle
    2191                 :            :      to verified_handle.  */
    2192                 :            :   int num_msg;
    2193                 :            :   rc = -ENOENT;
    2194                 :       3098 :   CURL *verified_handle = NULL;
    2195                 :       3098 :   do
    2196                 :            :     {
    2197                 :       3098 :       CURLMsg *msg;
    2198                 :            : 
    2199                 :       3098 :       msg = curl_multi_info_read(curlm, &num_msg);
    2200   [ +  -  +  - ]:       3098 :       if (msg != NULL && msg->msg == CURLMSG_DONE)
    2201                 :            :         {
    2202         [ +  + ]:       3098 :           if (vfd >= 0)
    2203                 :            :             {
    2204                 :       2228 :               bool pnl = (c->default_progressfn_printed_p
    2205   [ -  +  -  - ]:       1114 :                           && vfd == STDERR_FILENO);
    2206         [ +  - ]:       1114 :               dprintf (vfd, "%sserver response %s\n", pnl ? "\n" : "",
    2207                 :            :                        curl_easy_strerror (msg->data.result));
    2208         [ -  + ]:       1114 :               if (pnl)
    2209                 :          0 :                 c->default_progressfn_printed_p = 0;
    2210         [ +  - ]:       1120 :               for (int i = 0; i < num_urls; i++)
    2211         [ +  + ]:       1120 :                 if (msg->easy_handle == data[i].handle)
    2212                 :            :                   {
    2213         [ +  + ]:       1114 :                     if (strlen (data[i].errbuf) > 0)
    2214                 :         38 :                       dprintf (vfd, "url %d %s\n", i, data[i].errbuf);
    2215                 :            :                     break;
    2216                 :            :                   }
    2217                 :            :             }
    2218                 :            : 
    2219         [ +  + ]:       3098 :           if (msg->data.result != CURLE_OK)
    2220                 :            :             {
    2221                 :       1670 :               long resp_code;
    2222                 :       1670 :               CURLcode ok0;
    2223                 :            :               /* Unsuccessful query, determine error code.  */
    2224   [ -  +  -  -  :       1670 :               switch (msg->data.result)
          -  -  -  -  +  
                   +  - ]
    2225                 :            :                 {
    2226                 :            :                 case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN
    2227                 :          0 :                 case CURLE_URL_MALFORMAT: rc = -EINVAL; break;
    2228                 :       1624 :                 case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break;
    2229                 :       1624 :                 case CURLE_PEER_FAILED_VERIFICATION: rc = -ECONNREFUSED; break;
    2230                 :          0 :                 case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break;
    2231                 :          0 :                 case CURLE_WRITE_ERROR: rc = -EIO; break;
    2232                 :          0 :                 case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break;
    2233                 :          0 :                 case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break;
    2234                 :          0 :                 case CURLE_SEND_ERROR: rc = -ECONNRESET; break;
    2235                 :          0 :                 case CURLE_RECV_ERROR: rc = -ECONNRESET; break;
    2236                 :          0 :                 case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break;
    2237                 :            :                 case CURLE_HTTP_RETURNED_ERROR:
    2238                 :         44 :                   ok0 = curl_easy_getinfo (msg->easy_handle,
    2239                 :            :                                           CURLINFO_RESPONSE_CODE,
    2240                 :            :                                           &resp_code);
    2241                 :            :                   /* 406 signals that the requested file was too large */
    2242   [ +  -  +  + ]:         44 :                   if ( ok0 == CURLE_OK && resp_code == 406)
    2243                 :            :                     rc = -EFBIG;
    2244   [ -  +  -  - ]:         42 :                   else if (section != NULL && resp_code == 503)
    2245                 :            :                     rc = -EINVAL;
    2246                 :            :                   else
    2247                 :         44 :                     rc = -ENOENT;
    2248                 :            :                   break;
    2249                 :         44 :                 default: rc = -ENOENT; break;
    2250                 :            :                 }
    2251                 :            :             }
    2252                 :            :           else
    2253                 :            :             {
    2254                 :            :               /* Query completed without an error. Confirm that the
    2255                 :            :                  response code is 200 when using HTTP/HTTPS and 0 when
    2256                 :            :                  using file:// and set verified_handle.  */
    2257                 :            : 
    2258         [ +  - ]:       1428 :               if (msg->easy_handle != NULL)
    2259                 :            :                 {
    2260                 :       1428 :                   char *effective_url = NULL;
    2261                 :       1428 :                   long resp_code = 500;
    2262                 :       1428 :                   CURLcode ok1 = curl_easy_getinfo (target_handle,
    2263                 :            :                                                     CURLINFO_EFFECTIVE_URL,
    2264                 :            :                                                     &effective_url);
    2265                 :       1428 :                   CURLcode ok2 = curl_easy_getinfo (target_handle,
    2266                 :            :                                                     CURLINFO_RESPONSE_CODE,
    2267                 :            :                                                     &resp_code);
    2268   [ +  -  +  - ]:       1428 :                   if(ok1 == CURLE_OK && ok2 == CURLE_OK && effective_url)
    2269                 :            :                     {
    2270         [ +  + ]:       1428 :                       if (strncasecmp (effective_url, "HTTP", 4) == 0)
    2271         [ +  - ]:       1426 :                         if (resp_code == 200)
    2272                 :            :                           {
    2273                 :       1426 :                             verified_handle = msg->easy_handle;
    2274                 :       1428 :                             break;
    2275                 :            :                           }
    2276         [ +  - ]:          2 :                       if (strncasecmp (effective_url, "FILE", 4) == 0)
    2277         [ +  - ]:          2 :                         if (resp_code == 0)
    2278                 :            :                           {
    2279                 :          2 :                             verified_handle = msg->easy_handle;
    2280                 :          2 :                             break;
    2281                 :            :                           }
    2282                 :            :                     }
    2283                 :            :                   /* - libcurl since 7.52.0 version start to support
    2284                 :            :                        CURLINFO_SCHEME;
    2285                 :            :                      - before 7.61.0, effective_url would give us a
    2286                 :            :                        url with upper case SCHEME added in the front;
    2287                 :            :                      - effective_url between 7.61 and 7.69 can be lack
    2288                 :            :                        of scheme if the original url doesn't include one;
    2289                 :            :                      - since version 7.69 effective_url will be provide
    2290                 :            :                        a scheme in lower case.  */
    2291                 :            :                   #if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */
    2292                 :            :                   #if LIBCURL_VERSION_NUM <= 0x074500 /* 7.69.0 */
    2293                 :            :                   char *scheme = NULL;
    2294                 :            :                   CURLcode ok3 = curl_easy_getinfo (target_handle,
    2295                 :            :                                                     CURLINFO_SCHEME,
    2296                 :            :                                                     &scheme);
    2297                 :            :                   if(ok3 == CURLE_OK && scheme)
    2298                 :            :                     {
    2299                 :            :                       if (startswith (scheme, "HTTP"))
    2300                 :            :                         if (resp_code == 200)
    2301                 :            :                           {
    2302                 :            :                             verified_handle = msg->easy_handle;
    2303                 :            :                             break;
    2304                 :            :                           }
    2305                 :            :                     }
    2306                 :            :                   #endif
    2307                 :            :                   #endif
    2308                 :            :                 }
    2309                 :            :             }
    2310                 :            :         }
    2311         [ +  + ]:       1670 :     } while (num_msg > 0);
    2312                 :            : 
    2313                 :            :   /* Create an empty file in the cache if the query fails with ENOENT and
    2314                 :            :      it wasn't cancelled early.  */
    2315   [ +  +  +  - ]:       3092 :   if (rc == -ENOENT && !c->progressfn_cancel)
    2316                 :            :     {
    2317                 :       1466 :       int efd = open (target_cache_path, O_CREAT|O_EXCL, DEFFILEMODE);
    2318         [ +  + ]:       1466 :       if (efd >= 0)
    2319                 :       1464 :         close(efd);
    2320                 :            :     }
    2321         [ +  + ]:       1626 :   else if (rc == -EFBIG)
    2322                 :          2 :     goto out2;
    2323                 :            : 
    2324                 :            :   /* If the verified_handle is NULL and rc != -ENOENT, the query fails with
    2325                 :            :    * an error code other than 404, then do several retry within the retry_limit.
    2326                 :            :    * Clean up all old handles and jump back to the beginning of query_in_parallel,
    2327                 :            :    * reinitialize handles and query again.*/
    2328         [ +  + ]:       3090 :   if (verified_handle == NULL)
    2329                 :            :     {
    2330   [ +  +  +  + ]:       1662 :       if (rc != -ENOENT && retry_limit-- > 0)
    2331                 :            :         {
    2332         [ +  + ]:       1088 :           if (vfd >= 0)
    2333                 :         24 :             dprintf (vfd, "Retry failed query, %d attempt(s) remaining\n", retry_limit);
    2334                 :            :           /* remove all handles from multi */
    2335         [ +  + ]:       2180 :           for (int i = 0; i < num_urls; i++)
    2336                 :            :             {
    2337                 :       1092 :               curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    2338                 :       1092 :               curl_easy_cleanup (data[i].handle);
    2339                 :       1092 :               free(data[i].response_data);
    2340                 :       1092 :               data[i].response_data = NULL;
    2341                 :            :             }
    2342                 :       1088 :             free(c->winning_headers);
    2343                 :       1088 :             c->winning_headers = NULL;
    2344                 :       1088 :             goto query_in_parallel;
    2345                 :            :         }
    2346                 :            :       else
    2347                 :        574 :         goto out2;
    2348                 :            :     }
    2349                 :            : 
    2350         [ +  + ]:       1428 :   if (vfd >= 0)
    2351                 :            :     {
    2352   [ -  +  -  - ]:       2148 :       bool pnl = c->default_progressfn_printed_p && vfd == STDERR_FILENO;
    2353                 :       1074 :       dprintf (vfd, "%sgot file from server\n", pnl ? "\n" : "");
    2354         [ -  + ]:       1074 :       if (pnl)
    2355                 :          0 :         c->default_progressfn_printed_p = 0;
    2356                 :            :     }
    2357                 :            : 
    2358                 :            :   /* we've got one!!!! */
    2359                 :       1428 :   time_t mtime;
    2360                 :            : #if defined(_TIME_BITS) && _TIME_BITS == 64
    2361                 :            :   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME_T, (void*) &mtime);
    2362                 :            : #else
    2363                 :       1428 :   CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
    2364                 :            : #endif
    2365         [ +  - ]:       1428 :   if (curl_res == CURLE_OK)
    2366                 :            :     {
    2367                 :       1428 :       struct timespec tvs[2];
    2368                 :       1428 :       tvs[0].tv_sec = 0;
    2369                 :       1428 :       tvs[0].tv_nsec = UTIME_OMIT;
    2370                 :       1428 :       tvs[1].tv_sec = mtime;
    2371                 :       1428 :       tvs[1].tv_nsec = 0;
    2372                 :       1428 :       (void) futimens (fd, tvs);  /* best effort */
    2373                 :            :     }
    2374                 :            : 
    2375                 :            :   /* PR27571: make cache files casually unwriteable; dirs are already 0700 */
    2376                 :       1428 :   (void) fchmod(fd, 0400);
    2377                 :            :   /* PR31248: lseek back to beginning */
    2378                 :       1428 :   (void) lseek(fd, 0, SEEK_SET);
    2379                 :            :                 
    2380   [ +  -  -  + ]:       1428 :   if(NULL != url_ima_policies && ignore != url_ima_policies[committed_to])
    2381                 :            :     {
    2382                 :            : #ifdef ENABLE_IMA_VERIFICATION
    2383                 :            :       int result = debuginfod_validate_imasig(c, fd);
    2384                 :            : #else
    2385                 :          0 :       int result = -ENOSYS;
    2386                 :            : #endif
    2387                 :          0 :       if(0 == result)
    2388                 :            :         {
    2389                 :            :           if (vfd >= 0) dprintf (vfd, "valid signature\n");
    2390                 :            :         }
    2391         [ #  # ]:          0 :       else if (enforcing == url_ima_policies[committed_to])
    2392                 :            :         {
    2393                 :            :           // All invalid signatures are rejected.
    2394                 :            :           // Additionally in enforcing mode any non-valid signature is rejected, so by reaching
    2395                 :            :           // this case we do so since we know it is not valid. Note - this not just invalid signatures
    2396                 :            :           // but also signatures that cannot be validated
    2397         [ #  # ]:          0 :           if (vfd >= 0) dprintf (vfd, "error: invalid or missing signature (%d)\n", result);
    2398                 :          0 :           rc = result;
    2399                 :          0 :           goto out2;
    2400                 :            :         }
    2401                 :            :     }
    2402                 :            : 
    2403                 :            :   /* rename tmp->real */
    2404                 :       1428 :   rc = rename (target_cache_tmppath, target_cache_path);
    2405         [ -  + ]:       1428 :   if (rc < 0)
    2406                 :            :     {
    2407                 :          0 :       rc = -errno;
    2408                 :          0 :       goto out2;
    2409                 :            :       /* Perhaps we need not give up right away; could retry or something ... */
    2410                 :            :     }
    2411                 :            : 
    2412                 :            :   /* remove all handles from multi */
    2413         [ +  + ]:       2904 :   for (int i = 0; i < num_urls; i++)
    2414                 :            :     {
    2415                 :       1476 :       curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    2416                 :       1476 :       curl_easy_cleanup (data[i].handle);
    2417                 :       1476 :       free (data[i].response_data);
    2418                 :            :     }
    2419                 :            : 
    2420         [ +  + ]:       2904 :   for (int i = 0; i < num_urls; ++i)
    2421                 :       1476 :     free(server_url_list[i]);
    2422                 :       1428 :   free(server_url_list);
    2423                 :       1428 :   free(url_ima_policies);
    2424                 :       1428 :   free (data);
    2425                 :       1428 :   free (server_urls);
    2426                 :            : 
    2427                 :            :   /* don't close fd - we're returning it */
    2428                 :            :   /* don't unlink the tmppath; it's already been renamed. */
    2429         [ +  + ]:       1428 :   if (path != NULL)
    2430                 :        372 :    *path = strdup(target_cache_path);
    2431                 :            : 
    2432                 :       1428 :   rc = fd;
    2433                 :       1428 :   goto out;
    2434                 :            : 
    2435                 :            : /* error exits */
    2436                 :        576 :  out2:
    2437                 :            :   /* remove all handles from multi */
    2438         [ +  + ]:       1154 :   for (int i = 0; i < num_urls; i++)
    2439                 :            :     {
    2440         [ +  - ]:        578 :       if (data[i].handle != NULL)
    2441                 :            :         {
    2442                 :        578 :           curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    2443                 :        578 :           curl_easy_cleanup (data[i].handle);
    2444                 :        578 :           free (data[i].response_data);
    2445                 :            :         }
    2446                 :            :     }
    2447                 :            : 
    2448                 :        576 :   unlink (target_cache_tmppath);
    2449                 :        576 :   close (fd); /* before the rmdir, otherwise it'll fail */
    2450                 :        576 :   (void) rmdir (target_cache_dir); /* nop if not empty */
    2451                 :        576 :   free(data);
    2452                 :            : 
    2453                 :        576 :  out1:
    2454         [ +  + ]:       1154 :   for (int i = 0; i < num_urls; ++i)
    2455                 :        578 :     free(server_url_list[i]);
    2456                 :        576 :   free(server_url_list);
    2457                 :        576 :   free(url_ima_policies);
    2458                 :            : 
    2459                 :        576 :  out0:
    2460                 :        576 :   free (server_urls);
    2461                 :            : 
    2462                 :            : /* general purpose exit */
    2463                 :       6364 :  out:
    2464                 :            :   /* Reset sent headers */
    2465                 :       6364 :   curl_slist_free_all (c->headers);
    2466                 :       6364 :   c->headers = NULL;
    2467                 :       6364 :   c->user_agent_set_p = 0;
    2468                 :            :   
    2469                 :            :   /* Conclude the last \r status line */
    2470                 :            :   /* Another possibility is to use the ANSI CSI n K EL "Erase in Line"
    2471                 :            :      code.  That way, the previously printed messages would be erased,
    2472                 :            :      and without a newline. */
    2473         [ +  + ]:       6364 :   if (c->default_progressfn_printed_p)
    2474                 :          2 :     dprintf(STDERR_FILENO, "\n");
    2475                 :            : 
    2476         [ +  + ]:       6364 :   if (vfd >= 0)
    2477                 :            :     {
    2478         [ +  + ]:       1092 :       if (rc < 0)
    2479                 :         10 :         dprintf (vfd, "not found %s (err=%d)\n", strerror (-rc), rc);
    2480                 :            :       else
    2481                 :       1082 :         dprintf (vfd, "found %s (fd=%d)\n", target_cache_path, rc);
    2482                 :            :     }
    2483                 :            : 
    2484                 :       6364 :   free (cache_path);
    2485                 :       6364 :   free (maxage_path);
    2486                 :       6364 :   free (interval_path);
    2487                 :       6364 :   free (cache_miss_path);
    2488                 :       6364 :   free (target_cache_dir);
    2489                 :       6364 :   free (target_cache_path);
    2490   [ +  +  +  + ]:       6364 :   if (rc < 0 && target_cache_tmppath != NULL)
    2491                 :        578 :     (void)unlink (target_cache_tmppath);
    2492                 :       6364 :   free (target_cache_tmppath);
    2493                 :            : 
    2494                 :            :   
    2495                 :       6364 :   return rc;
    2496                 :            : }
    2497                 :            : 
    2498                 :            : 
    2499                 :            : 
    2500                 :            : /* See debuginfod.h  */
    2501                 :            : debuginfod_client  *
    2502                 :        522 : debuginfod_begin (void)
    2503                 :            : {
    2504                 :            :   /* Initialize libcurl lazily, but only once.  */
    2505                 :        522 :   pthread_once (&init_control, libcurl_init);
    2506                 :            : 
    2507                 :        522 :   debuginfod_client *client;
    2508                 :        522 :   size_t size = sizeof (struct debuginfod_client);
    2509                 :        522 :   client = calloc (1, size);
    2510                 :            : 
    2511         [ +  - ]:        522 :   if (client != NULL)
    2512                 :            :     {
    2513         [ +  + ]:        522 :       if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR))
    2514                 :          2 :         client->progressfn = default_progressfn;
    2515         [ +  + ]:        522 :       if (getenv(DEBUGINFOD_VERBOSE_ENV_VAR))
    2516                 :          4 :         client->verbose_fd = STDERR_FILENO;
    2517                 :            :       else
    2518                 :        518 :         client->verbose_fd = -1;
    2519                 :            : 
    2520                 :            :       // allocate 1 curl multi handle
    2521                 :        522 :       client->server_mhandle = curl_multi_init ();
    2522         [ -  + ]:        522 :       if (client->server_mhandle == NULL)
    2523                 :          0 :         goto out1;
    2524                 :            :     }
    2525                 :            : 
    2526                 :            : #ifdef ENABLE_IMA_VERIFICATION
    2527                 :            :   load_ima_public_keys (client);
    2528                 :            : #endif
    2529                 :            : 
    2530                 :            :   // extra future initialization
    2531                 :            :   
    2532                 :        522 :   goto out;
    2533                 :            : 
    2534                 :          0 :  out1:
    2535                 :          0 :   free (client);
    2536                 :          0 :   client = NULL;
    2537                 :            : 
    2538                 :        522 :  out:  
    2539                 :        522 :   return client;
    2540                 :            : }
    2541                 :            : 
    2542                 :            : void
    2543                 :        480 : debuginfod_set_user_data(debuginfod_client *client,
    2544                 :            :                          void *data)
    2545                 :            : {
    2546                 :        480 :   client->user_data = data;
    2547                 :        480 : }
    2548                 :            : 
    2549                 :            : void *
    2550                 :          0 : debuginfod_get_user_data(debuginfod_client *client)
    2551                 :            : {
    2552                 :          0 :   return client->user_data;
    2553                 :            : }
    2554                 :            : 
    2555                 :            : const char *
    2556                 :         42 : debuginfod_get_url(debuginfod_client *client)
    2557                 :            : {
    2558         [ +  - ]:          2 :   return client->url;
    2559                 :            : }
    2560                 :            : 
    2561                 :            : const char *
    2562                 :         44 : debuginfod_get_headers(debuginfod_client *client)
    2563                 :            : {
    2564                 :         44 :   return client->winning_headers;
    2565                 :            : }
    2566                 :            : 
    2567                 :            : void
    2568                 :        520 : debuginfod_end (debuginfod_client *client)
    2569                 :            : {
    2570         [ +  - ]:        520 :   if (client == NULL)
    2571                 :            :     return;
    2572                 :            : 
    2573                 :        520 :   curl_multi_cleanup (client->server_mhandle);
    2574                 :        520 :   curl_slist_free_all (client->headers);
    2575                 :        520 :   free (client->winning_headers);
    2576                 :        520 :   free (client->url);
    2577                 :            : #ifdef ENABLE_IMA_VERIFICATION
    2578                 :            :   free_ima_public_keys (client);
    2579                 :            : #endif
    2580                 :        520 :   free (client);
    2581                 :            : }
    2582                 :            : 
    2583                 :            : int
    2584                 :        298 : debuginfod_find_debuginfo (debuginfod_client *client,
    2585                 :            :                            const unsigned char *build_id, int build_id_len,
    2586                 :            :                            char **path)
    2587                 :            : {
    2588                 :        298 :   return debuginfod_query_server_by_buildid(client, build_id, build_id_len,
    2589                 :            :                                  "debuginfo", NULL, path);
    2590                 :            : }
    2591                 :            : 
    2592                 :            : 
    2593                 :            : /* See debuginfod.h  */
    2594                 :            : int
    2595                 :        734 : debuginfod_find_executable(debuginfod_client *client,
    2596                 :            :                            const unsigned char *build_id, int build_id_len,
    2597                 :            :                            char **path)
    2598                 :            : {
    2599                 :        734 :   return debuginfod_query_server_by_buildid(client, build_id, build_id_len,
    2600                 :            :                                  "executable", NULL, path);
    2601                 :            : }
    2602                 :            : 
    2603                 :            : /* See debuginfod.h  */
    2604                 :       5316 : int debuginfod_find_source(debuginfod_client *client,
    2605                 :            :                            const unsigned char *build_id, int build_id_len,
    2606                 :            :                            const char *filename, char **path)
    2607                 :            : {
    2608                 :       5316 :   return debuginfod_query_server_by_buildid(client, build_id, build_id_len,
    2609                 :            :                                  "source", filename, path);
    2610                 :            : }
    2611                 :            : 
    2612                 :            : int
    2613                 :         16 : debuginfod_find_section (debuginfod_client *client,
    2614                 :            :                          const unsigned char *build_id, int build_id_len,
    2615                 :            :                          const char *section, char **path)
    2616                 :            : {
    2617                 :         16 :   int rc = debuginfod_query_server_by_buildid(client, build_id, build_id_len,
    2618                 :            :                                               "section", section, path);
    2619         [ -  + ]:         16 :   if (rc != -EINVAL && rc != -ENOSYS)
    2620                 :            :     return rc;
    2621                 :            :   /* NB: we fall through in case of ima:enforcing-filtered DEBUGINFOD_URLS servers,
    2622                 :            :      so we can download the entire file, verify it locally, then slice it. */
    2623                 :            :   
    2624                 :            :   /* The servers may have lacked support for section queries.  Attempt to
    2625                 :            :      download the debuginfo or executable containing the section in order
    2626                 :            :      to extract it.  */
    2627                 :          0 :   rc = -EEXIST;
    2628                 :          0 :   int fd = -1;
    2629                 :          0 :   char *tmp_path = NULL;
    2630                 :            : 
    2631                 :          0 :   fd = debuginfod_find_debuginfo (client, build_id, build_id_len, &tmp_path);
    2632         [ #  # ]:          0 :   if (client->progressfn_cancel)
    2633                 :            :     {
    2634         [ #  # ]:          0 :       if (fd >= 0)
    2635                 :            :         {
    2636                 :            :           /* This shouldn't happen, but we'll check this condition
    2637                 :            :              just in case.  */
    2638                 :          0 :           close (fd);
    2639                 :          0 :           free (tmp_path);
    2640                 :            :         }
    2641                 :          0 :       return -ENOENT;
    2642                 :            :     }
    2643         [ #  # ]:          0 :   if (fd >= 0)
    2644                 :            :     {
    2645                 :          0 :       rc = extract_section (fd, section, tmp_path, path);
    2646                 :          0 :       close (fd);
    2647                 :            :     }
    2648                 :            : 
    2649         [ #  # ]:          0 :   if (rc == -EEXIST)
    2650                 :            :     {
    2651                 :            :       /* Either the debuginfo couldn't be found or the section should
    2652                 :            :          be in the executable.  */
    2653                 :          0 :       fd = debuginfod_find_executable (client, build_id,
    2654                 :            :                                        build_id_len, &tmp_path);
    2655         [ #  # ]:          0 :       if (fd >= 0)
    2656                 :            :         {
    2657                 :          0 :           rc = extract_section (fd, section, tmp_path, path);
    2658                 :          0 :           close (fd);
    2659                 :            :         }
    2660                 :            :       else
    2661                 :            :         /* Update rc so that we return the most recent error code.  */
    2662                 :            :         rc = fd;
    2663                 :            :     }
    2664                 :            : 
    2665                 :          0 :   free (tmp_path);
    2666                 :          0 :   return rc;
    2667                 :            : }
    2668                 :            : 
    2669                 :            : 
    2670                 :         36 : int debuginfod_find_metadata (debuginfod_client *client,
    2671                 :            :                               const char* key, const char* value, char **path)
    2672                 :            : {
    2673                 :         36 :   char *server_urls = NULL;
    2674                 :         36 :   char *urls_envvar = NULL;
    2675                 :         36 :   char *cache_path = NULL;
    2676                 :         36 :   char *target_cache_dir = NULL;
    2677                 :         36 :   char *target_cache_path = NULL;
    2678                 :         36 :   char *target_cache_tmppath = NULL;
    2679                 :         36 :   char *target_file_name = NULL;
    2680                 :         36 :   char *key_and_value = NULL;
    2681                 :         36 :   int rc = 0, r;
    2682                 :         36 :   int vfd = client->verbose_fd;
    2683                 :         36 :   struct handle_data *data = NULL;
    2684                 :            :   
    2685                 :         36 :   json_object *json_metadata = json_object_new_object();
    2686                 :         36 :   json_bool json_metadata_complete = true;
    2687                 :         36 :   json_object *json_metadata_arr = json_object_new_array();
    2688         [ -  + ]:         36 :   if (NULL == json_metadata)
    2689                 :            :     {
    2690                 :          0 :       rc = -ENOMEM;
    2691                 :          0 :       goto out;
    2692                 :            :     }
    2693         [ -  + ]:         36 :   json_object_object_add(json_metadata, "results",
    2694                 :          0 :                          json_metadata_arr ?: json_object_new_array() /* Empty array */);
    2695                 :            : 
    2696         [ -  + ]:         36 :   if (NULL == value || NULL == key)
    2697                 :            :     {
    2698                 :          0 :       rc = -EINVAL;
    2699                 :          0 :       goto out;
    2700                 :            :     }
    2701                 :            : 
    2702         [ -  + ]:         36 :   if (vfd >= 0)
    2703                 :          0 :     dprintf (vfd, "debuginfod_find_metadata %s %s\n", key, value);
    2704                 :            : 
    2705                 :            :   /* Without query-able URL, we can stop here*/
    2706                 :         36 :   urls_envvar = getenv(DEBUGINFOD_URLS_ENV_VAR);
    2707         [ -  + ]:         36 :   if (vfd >= 0)
    2708         [ #  # ]:          0 :     dprintf (vfd, "server urls \"%s\"\n",
    2709                 :            :       urls_envvar != NULL ? urls_envvar : "");
    2710   [ +  -  +  + ]:         36 :   if (urls_envvar == NULL || urls_envvar[0] == '\0')
    2711                 :            :   {
    2712                 :         10 :     rc = -ENOSYS;
    2713                 :         10 :     goto out;
    2714                 :            :   }
    2715                 :            : 
    2716                 :            :   /* set paths needed to perform the query
    2717                 :            :      example format:
    2718                 :            :      cache_path:        $HOME/.cache
    2719                 :            :      target_cache_dir:  $HOME/.cache/metadata
    2720                 :            :      target_cache_path: $HOME/.cache/metadata/KEYENCODED_VALUEENCODED
    2721                 :            :      target_cache_path: $HOME/.cache/metadata/KEYENCODED_VALUEENCODED.XXXXXX
    2722                 :            :   */
    2723                 :            : 
    2724                 :            :   // libcurl > 7.62ish has curl_url_set()/etc. to construct these things more properly.
    2725                 :            :   // curl_easy_escape() is older
    2726                 :            :   {
    2727                 :         26 :     CURL *c = curl_easy_init();
    2728         [ -  + ]:         26 :     if (!c)
    2729                 :            :       {
    2730                 :          0 :         rc = -ENOMEM;
    2731                 :          0 :         goto out;
    2732                 :            :       }
    2733                 :         26 :     char *key_escaped = curl_easy_escape(c, key, 0);
    2734                 :         26 :     char *value_escaped = curl_easy_escape(c, value, 0);
    2735                 :            :     
    2736                 :            :     // fallback to unescaped values in unlikely case of error
    2737   [ -  +  -  +  :         52 :     xalloc_str (key_and_value, "key=%s&value=%s", key_escaped ?: key, value_escaped ?: value);
                   -  + ]
    2738         [ -  + ]:         26 :     xalloc_str (target_file_name, "%s_%s", key_escaped ?: key, value_escaped ?: value);
    2739                 :         26 :     curl_free(value_escaped);
    2740                 :         26 :     curl_free(key_escaped);
    2741                 :         26 :     curl_easy_cleanup(c);
    2742                 :            :   }
    2743                 :            : 
    2744                 :            :   /* Check if we have a recent result already in the cache. */
    2745                 :         26 :   cache_path = make_cache_path();
    2746         [ -  + ]:         26 :   if (! cache_path)
    2747                 :            :     {
    2748                 :          0 :       rc = -ENOMEM;
    2749                 :          0 :       goto out;
    2750                 :            :     }
    2751         [ -  + ]:         26 :   xalloc_str (target_cache_dir, "%s/metadata", cache_path);
    2752                 :         26 :   (void) mkdir (target_cache_dir, 0700);
    2753         [ -  + ]:         26 :   xalloc_str (target_cache_path, "%s/%s", target_cache_dir, target_file_name);
    2754         [ -  + ]:         26 :   xalloc_str (target_cache_tmppath, "%s/%s.XXXXXX", target_cache_dir, target_file_name);
    2755                 :            : 
    2756                 :         26 :   int fd = open(target_cache_path, O_RDONLY);
    2757         [ +  + ]:         26 :   if (fd >= 0)
    2758                 :            :     {
    2759                 :         10 :       struct stat st;
    2760                 :         10 :       int metadata_retention = 0;
    2761                 :         10 :       time_t now = time(NULL);
    2762                 :         10 :       char *metadata_retention_path = 0;
    2763                 :            : 
    2764         [ -  + ]:         18 :       xalloc_str (metadata_retention_path, "%s/%s", cache_path, metadata_retention_filename);
    2765         [ +  - ]:         10 :       if (metadata_retention_path)
    2766                 :            :         {
    2767                 :         10 :           rc = debuginfod_config_cache(client, metadata_retention_path,
    2768                 :            :                                        metadata_retention_default_s, &st);
    2769                 :         10 :           free (metadata_retention_path);
    2770         [ -  + ]:         10 :           if (rc < 0)
    2771                 :          0 :             rc = 0;
    2772                 :            :         }
    2773                 :            :       else
    2774                 :            :         rc = 0;
    2775                 :         10 :       metadata_retention = rc;
    2776                 :            : 
    2777         [ -  + ]:         10 :       if (fstat(fd, &st) != 0)
    2778                 :            :         {
    2779                 :          0 :           rc = -errno;
    2780                 :          0 :           close (fd);
    2781                 :          0 :           goto out;
    2782                 :            :         }
    2783                 :            : 
    2784   [ +  +  +  - ]:         10 :       if (metadata_retention > 0 && (now - st.st_mtime <= metadata_retention))
    2785                 :            :         {
    2786         [ -  + ]:          8 :           if (client && client->verbose_fd >= 0)
    2787                 :          0 :             dprintf (client->verbose_fd, "cached metadata %s", target_file_name);
    2788                 :            : 
    2789         [ +  - ]:          8 :           if (path != NULL)
    2790                 :            :             {
    2791                 :          8 :               *path = target_cache_path; // pass over the pointer
    2792                 :          8 :               target_cache_path = NULL; // prevent free() in our own cleanup
    2793                 :            :             }
    2794                 :            : 
    2795                 :            :           /* Success!!!! */
    2796                 :          8 :           rc = fd;
    2797                 :          8 :           goto out;
    2798                 :            :         }
    2799                 :            : 
    2800                 :            :       /* We don't have to clear the likely-expired cached object here
    2801                 :            :          by unlinking.  We will shortly make a new request and save
    2802                 :            :          results right on top.  Erasing here could trigger a TOCTOU
    2803                 :            :          race with another thread just finishing a query and passing
    2804                 :            :          its results back.
    2805                 :            :       */
    2806                 :            :       // (void) unlink (target_cache_path);
    2807                 :            : 
    2808                 :          2 :       close (fd);
    2809                 :            :     }
    2810                 :            : 
    2811                 :            :   /* No valid cached metadata found: time to make the queries. */
    2812                 :            : 
    2813                 :         18 :   free (client->url);
    2814                 :         18 :   client->url = NULL;
    2815                 :            : 
    2816                 :         18 :   long maxtime = 0;
    2817                 :         18 :   const char *maxtime_envvar;
    2818                 :         18 :   maxtime_envvar = getenv(DEBUGINFOD_MAXTIME_ENV_VAR);
    2819         [ -  + ]:         18 :   if (maxtime_envvar != NULL)
    2820                 :          0 :     maxtime = atol (maxtime_envvar);
    2821         [ -  + ]:         18 :   if (maxtime && vfd >= 0)
    2822                 :          0 :     dprintf(vfd, "using max time %lds\n", maxtime);
    2823                 :            : 
    2824                 :         18 :   long timeout = default_timeout;
    2825                 :         18 :   const char* timeout_envvar = getenv(DEBUGINFOD_TIMEOUT_ENV_VAR);
    2826         [ -  + ]:         18 :   if (timeout_envvar != NULL)
    2827                 :          0 :     timeout = atoi (timeout_envvar);
    2828         [ -  + ]:         18 :   if (vfd >= 0)
    2829                 :          0 :     dprintf (vfd, "using timeout %ld\n", timeout);
    2830                 :            : 
    2831                 :         18 :   add_default_headers(client);
    2832                 :            : 
    2833                 :            :   /* Make a copy of the envvar so it can be safely modified.  */
    2834                 :         18 :   server_urls = strdup(urls_envvar);
    2835         [ -  + ]:         18 :   if (server_urls == NULL)
    2836                 :            :   {
    2837                 :          0 :     rc = -ENOMEM;
    2838                 :          0 :     goto out;
    2839                 :            :   }
    2840                 :            : 
    2841                 :            :   /* Thereafter, goto out1 on error*/
    2842                 :            : 
    2843                 :         18 :   char **server_url_list = NULL;
    2844                 :         18 :   ima_policy_t* url_ima_policies = NULL;
    2845                 :         18 :   char *server_url;
    2846                 :         18 :   int num_urls = 0;
    2847                 :         18 :   r = init_server_urls("metadata", NULL, server_urls, &server_url_list, &url_ima_policies, &num_urls, vfd);
    2848         [ -  + ]:         18 :   if (0 != r)
    2849                 :            :     {
    2850                 :          0 :       rc = r;
    2851                 :          0 :       goto out1;
    2852                 :            :     }
    2853                 :            : 
    2854                 :         18 :   CURLM *curlm = client->server_mhandle;
    2855                 :            : 
    2856                 :         18 :   CURL *target_handle = NULL;
    2857                 :         18 :   data = malloc(sizeof(struct handle_data) * num_urls);
    2858         [ -  + ]:         18 :   if (data == NULL)
    2859                 :            :     {
    2860                 :          0 :       rc = -ENOMEM;
    2861                 :          0 :       goto out1;
    2862                 :            :     }
    2863                 :            : 
    2864                 :            :   /* thereafter, goto out2 on error.  */
    2865                 :            : 
    2866                 :            :   /* Initialize handle_data  */
    2867         [ +  + ]:         44 :   for (int i = 0; i < num_urls; i++)
    2868                 :            :     {
    2869         [ +  - ]:         26 :       if ((server_url = server_url_list[i]) == NULL)
    2870                 :            :         break;
    2871         [ -  + ]:         26 :       if (vfd >= 0)
    2872                 :          0 :         dprintf (vfd, "init server %d %s\n", i, server_url);
    2873                 :            :       
    2874                 :         26 :       data[i].errbuf[0] = '\0';
    2875                 :         26 :       data[i].target_handle = &target_handle;
    2876                 :         26 :       data[i].client = client;
    2877                 :         26 :       data[i].metadata = NULL;
    2878                 :         26 :       data[i].metadata_size = 0;
    2879                 :         26 :       data[i].response_data = NULL;
    2880                 :         26 :       data[i].response_data_size = 0;
    2881                 :            :       
    2882                 :         26 :       snprintf(data[i].url, PATH_MAX, "%s?%s", server_url, key_and_value);
    2883                 :            :       
    2884                 :         26 :       r = init_handle(client, metadata_callback, header_callback, &data[i], i, timeout, vfd);
    2885         [ -  + ]:         26 :       if (0 != r)
    2886                 :            :         {
    2887                 :          0 :           rc = r;
    2888                 :          0 :           goto out2;
    2889                 :            :         }
    2890                 :         26 :       curl_multi_add_handle(curlm, data[i].handle);
    2891                 :            :     }
    2892                 :            : 
    2893                 :            :   /* Query servers */
    2894         [ -  + ]:         18 :   if (vfd >= 0)
    2895                 :          0 :     dprintf (vfd, "Starting %d queries\n",num_urls);
    2896                 :         18 :   int committed_to;
    2897                 :         18 :   r = perform_queries(curlm, NULL, data, client, num_urls, maxtime, 0, false, vfd, &committed_to);
    2898         [ -  + ]:         18 :   if (0 != r)
    2899                 :            :     {
    2900                 :          0 :       rc = r;
    2901                 :          0 :       goto out2;
    2902                 :            :     }
    2903                 :            : 
    2904                 :            :   /* NOTE: We don't check the return codes of the curl messages since
    2905                 :            :      a metadata query failing silently is just fine. We want to know what's
    2906                 :            :      available from servers which can be connected with no issues.
    2907                 :            :      If running with additional verbosity, the failure will be noted in stderr */
    2908                 :            : 
    2909                 :            :   /* Building the new json array from all the upstream data and
    2910                 :            :      cleanup while at it.
    2911                 :            :    */
    2912         [ +  + ]:         44 :   for (int i = 0; i < num_urls; i++)
    2913                 :            :     {
    2914                 :         26 :       curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    2915                 :         26 :       curl_easy_cleanup (data[i].handle);
    2916                 :         26 :       free (data[i].response_data);
    2917                 :            :       
    2918         [ +  + ]:         26 :       if (NULL == data[i].metadata)
    2919                 :            :         {
    2920         [ -  + ]:         12 :           if (vfd >= 0)
    2921                 :         12 :             dprintf (vfd, "Query to %s failed with error message:\n\t\"%s\"\n",
    2922                 :          0 :                      data[i].url, data[i].errbuf);
    2923                 :         12 :           json_metadata_complete = false;
    2924                 :         12 :           continue;
    2925                 :            :         }
    2926                 :            : 
    2927                 :         14 :       json_object *upstream_metadata = json_tokener_parse(data[i].metadata);
    2928                 :         14 :       json_object *upstream_complete;
    2929                 :         14 :       json_object *upstream_metadata_arr;
    2930   [ +  -  +  - ]:         28 :       if (NULL == upstream_metadata ||
    2931         [ -  + ]:         28 :           !json_object_object_get_ex(upstream_metadata, "results", &upstream_metadata_arr) ||
    2932                 :         14 :           !json_object_object_get_ex(upstream_metadata, "complete", &upstream_complete))
    2933                 :          0 :         continue;
    2934                 :         14 :       json_metadata_complete &= json_object_get_boolean(upstream_complete);
    2935                 :            :       // Combine the upstream metadata into the json array
    2936         [ +  + ]:         24 :       for (int j = 0, n = json_object_array_length(upstream_metadata_arr); j < n; j++)
    2937                 :            :         {
    2938                 :         10 :           json_object *entry = json_object_array_get_idx(upstream_metadata_arr, j);
    2939                 :         10 :           json_object_get(entry); // increment reference count
    2940                 :         10 :           json_object_array_add(json_metadata_arr, entry);
    2941                 :            :         }
    2942                 :         14 :       json_object_put(upstream_metadata);
    2943                 :            : 
    2944                 :         14 :       free (data[i].metadata);
    2945                 :            :     }
    2946                 :            : 
    2947                 :            :   /* Because of race with cache cleanup / rmdir, try to mkdir/mkstemp up to twice. */
    2948         [ +  - ]:         18 :   for (int i=0; i<2; i++)
    2949                 :            :     {
    2950                 :            :       /* (re)create target directory in cache */
    2951                 :         18 :       (void) mkdir(target_cache_dir, 0700); /* files will be 0400 later */
    2952                 :            : 
    2953                 :            :       /* NB: write to a temporary file first, to avoid race condition of
    2954                 :            :          multiple clients checking the cache, while a partially-written or empty
    2955                 :            :          file is in there, being written from libcurl. */
    2956                 :         18 :       fd = mkstemp (target_cache_tmppath);
    2957         [ -  + ]:         18 :       if (fd >= 0) break;
    2958                 :            :     }
    2959         [ -  + ]:         18 :   if (fd < 0) /* Still failed after two iterations. */
    2960                 :            :     {
    2961                 :          0 :       rc = -errno;
    2962                 :          0 :       goto out1;
    2963                 :            :     }
    2964                 :            :     
    2965                 :            :   /* Plop the complete json_metadata object into the cache. */
    2966                 :         18 :   json_object_object_add(json_metadata, "complete", json_object_new_boolean(json_metadata_complete));
    2967                 :         18 :   const char* json_string = json_object_to_json_string_ext(json_metadata, JSON_C_TO_STRING_PRETTY);
    2968         [ -  + ]:         18 :   if (json_string == NULL)
    2969                 :            :     {
    2970                 :          0 :       rc = -ENOMEM;
    2971                 :          0 :       goto out1;
    2972                 :            :     }
    2973                 :         18 :   ssize_t res = write_retry (fd, json_string, strlen(json_string));
    2974                 :         18 :   (void) lseek(fd, 0, SEEK_SET); // rewind file so client can read it from the top
    2975                 :            :   
    2976                 :            :   /* NB: json_string is auto deleted when json_metadata object is nuked */
    2977   [ +  -  -  + ]:         18 :   if (res < 0 || (size_t) res != strlen(json_string))
    2978                 :            :     {
    2979                 :          0 :       rc = -EIO;
    2980                 :          0 :       goto out1;
    2981                 :            :     }
    2982                 :            :   /* PR27571: make cache files casually unwriteable; dirs are already 0700 */
    2983                 :         18 :   (void) fchmod(fd, 0400);
    2984                 :            : 
    2985                 :            :   /* rename tmp->real */
    2986                 :         18 :   rc = rename (target_cache_tmppath, target_cache_path);
    2987         [ -  + ]:         18 :   if (rc < 0)
    2988                 :            :     {
    2989                 :          0 :       rc = -errno;
    2990                 :          0 :       goto out1;
    2991                 :            :       /* Perhaps we need not give up right away; could retry or something ... */
    2992                 :            :     }
    2993                 :            :   
    2994                 :            :   /* don't close fd - we're returning it */
    2995                 :            :   /* don't unlink the tmppath; it's already been renamed. */
    2996         [ +  - ]:         18 :   if (path != NULL)
    2997                 :         18 :    *path = strdup(target_cache_path);
    2998                 :            : 
    2999                 :         18 :   rc = fd;
    3000                 :         18 :   goto out1;
    3001                 :            : 
    3002                 :            : /* error exits */
    3003                 :          0 : out2:
    3004                 :            :   /* remove all handles from multi */
    3005         [ #  # ]:          0 :   for (int i = 0; i < num_urls; i++)
    3006                 :            :   {
    3007         [ #  # ]:          0 :     if (data[i].handle != NULL)
    3008                 :            :     {
    3009                 :          0 :       curl_multi_remove_handle(curlm, data[i].handle); /* ok to repeat */
    3010                 :          0 :       curl_easy_cleanup (data[i].handle);
    3011                 :          0 :       free (data[i].response_data);
    3012                 :          0 :       free (data[i].metadata);
    3013                 :            :     }
    3014                 :            :   }
    3015                 :            : 
    3016                 :          0 : out1:
    3017                 :         18 :   free(data);
    3018                 :            :                               
    3019         [ +  + ]:         44 :   for (int i = 0; i < num_urls; ++i)
    3020                 :         26 :     free(server_url_list[i]);
    3021                 :         18 :   free(server_url_list);
    3022                 :         18 :   free(url_ima_policies);
    3023                 :            : 
    3024                 :         36 : out:
    3025                 :         36 :   free (server_urls);
    3026                 :         36 :   json_object_put(json_metadata);
    3027                 :            :   /* Reset sent headers */
    3028                 :         36 :   curl_slist_free_all (client->headers);
    3029                 :         36 :   client->headers = NULL;
    3030                 :         36 :   client->user_agent_set_p = 0;
    3031                 :            : 
    3032                 :         36 :   free (target_cache_dir);
    3033                 :         36 :   free (target_cache_path);
    3034                 :         36 :   free (target_cache_tmppath);
    3035                 :         36 :   free (key_and_value);
    3036                 :         36 :   free (target_file_name);
    3037                 :         36 :   free (cache_path);
    3038                 :            :     
    3039                 :         36 :   return rc;
    3040                 :            : }
    3041                 :            : 
    3042                 :            : 
    3043                 :            : /* Add an outgoing HTTP header.  */
    3044                 :       2818 : int debuginfod_add_http_header (debuginfod_client *client, const char* header)
    3045                 :            : {
    3046                 :            :   /* Sanity check header value is of the form Header: Value.
    3047                 :            :      It should contain at least one colon that isn't the first or
    3048                 :            :      last character.  */
    3049                 :       2818 :   char *colon = strchr (header, ':'); /* first colon */
    3050                 :       2818 :   if (colon == NULL /* present */
    3051         [ +  - ]:       2818 :       || colon == header /* not at beginning - i.e., have a header name */
    3052         [ +  - ]:       2818 :       || *(colon + 1) == '\0') /* not at end - i.e., have a value */
    3053                 :            :     /* NB: but it's okay for a value to contain other colons! */
    3054                 :            :     return -EINVAL;
    3055                 :            : 
    3056                 :       2818 :   struct curl_slist *temp = curl_slist_append (client->headers, header);
    3057         [ +  - ]:       2818 :   if (temp == NULL)
    3058                 :            :     return -ENOMEM;
    3059                 :            : 
    3060                 :            :   /* Track if User-Agent: is being set.  If so, signal not to add the
    3061                 :            :      default one. */
    3062         [ +  + ]:       2818 :   if (startswith (header, "User-Agent:"))
    3063                 :       2174 :     client->user_agent_set_p = 1;
    3064                 :            : 
    3065                 :       2818 :   client->headers = temp;
    3066                 :       2818 :   return 0;
    3067                 :            : }
    3068                 :            : 
    3069                 :            : 
    3070                 :            : void
    3071                 :        754 : debuginfod_set_progressfn(debuginfod_client *client,
    3072                 :            :                           debuginfod_progressfn_t fn)
    3073                 :            : {
    3074                 :        754 :   client->progressfn = fn;
    3075                 :        754 : }
    3076                 :            : 
    3077                 :            : void
    3078                 :         72 : debuginfod_set_verbose_fd(debuginfod_client *client, int fd)
    3079                 :            : {
    3080                 :         72 :   client->verbose_fd = fd;
    3081                 :         72 : }
    3082                 :            : 
    3083                 :            : #endif /* DUMMY_LIBDEBUGINFOD */

Generated by: LCOV version 1.16