Commit 7bdb9fba authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa Committed by Daniel Stenberg
Browse files

Disable hash check if neither OpenSSL nor GNUTLS is installed.

parent 69271537
Loading
Loading
Loading
Loading
+149 −151
Original line number Diff line number Diff line
@@ -182,25 +182,21 @@ static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)

#else

#ifdef USE_SSLEAY

#ifdef USE_OPENSSL
#  include <openssl/md5.h>
#  include <openssl/sha.h>
#  else
/* TODO What to do if USE_OPENSSL is undefined? */
#  endif

#else /* USE_SSLEAY */
#else /* USE_OPENSSL */

/* TODO hash functions for other libraries here */

#endif /* USE_SSLEAY */
#endif /* USE_OPENSSL */

#endif /* USE_GNUTLS */

#endif /* USE_GNUTLS_NETTLE */

#ifdef METALINK_HASH_CHECK

const digest_params MD5_DIGEST_PARAMS[] = {
  {
    (Curl_digest_init_func) MD5_Init,
@@ -274,6 +270,148 @@ int Curl_digest_final(digest_context *context, unsigned char *result)
  return 0;
}

static const metalink_digest_def SHA256_DIGEST_DEF[] = {
  {"sha-256", SHA256_DIGEST_PARAMS}
};

static const metalink_digest_def SHA1_DIGEST_DEF[] = {
  {"sha-1", SHA1_DIGEST_PARAMS}
};

static const metalink_digest_def MD5_DIGEST_DEF[] = {
  {"md5", MD5_DIGEST_PARAMS}
};

/*
 * The alias of supported hash functions in the order by preference
 * (basically stronger hash comes first). We included "sha-256" and
 * "sha256". The former is the name defined in the IANA registry named
 * "Hash Function Textual Names". The latter is widely (and
 * historically) used in Metalink version 3.
 */
static const metalink_digest_alias digest_aliases[] = {
  {"sha-256", SHA256_DIGEST_DEF},
  {"sha256", SHA256_DIGEST_DEF},
  {"sha-1", SHA1_DIGEST_DEF},
  {"sha1", SHA1_DIGEST_DEF},
  {"md5", MD5_DIGEST_DEF},
  {NULL, NULL}
};

static unsigned char hex_to_uint(const char *s)
{
  int v[2];
  int i;
  for(i = 0; i < 2; ++i) {
    v[i] = Curl_raw_toupper(s[i]);
    if('0' <= v[i] && v[i] <= '9') {
      v[i] -= '0';
    }
    else if('A' <= v[i] && v[i] <= 'Z') {
      v[i] -= 'A'-10;
    }
  }
  return (unsigned char)((v[0] << 4) | v[1]);
}

/*
 * Check checksum of file denoted by filename. The expected hash value
 * is given in hex_hash which is hex-encoded string.
 *
 * This function returns 1 if it succeeds or one of the following
 * integers:
 *
 * 0:
 *   Checksum didn't match.
 * -1:
 *   Could not open file; or could not read data from file.
 */
static int check_hash(const char *filename,
                      const metalink_digest_def *digest_def,
                      const char *hex_hash, FILE *error)
{
  unsigned char *result;
  digest_context *dctx;
  int check_ok;
  int fd;
  size_t i;
  fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name,
          filename);
  fd = open(filename, O_RDONLY);
  if(fd == -1) {
    fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno));
    return -1;
  }
  dctx = Curl_digest_init(digest_def->dparams);
  result = malloc(digest_def->dparams->digest_resultlen);
  while(1) {
    unsigned char buf[4096];
    ssize_t len = read(fd, buf, sizeof(buf));
    if(len == 0) {
      break;
    }
    else if(len == -1) {
      fprintf(error, "Could not read file %s: %s\n", filename,
              strerror(errno));
      Curl_digest_final(dctx, result);
      close(fd);
      return -1;
    }
    Curl_digest_update(dctx, buf, (unsigned int)len);
  }
  Curl_digest_final(dctx, result);
  check_ok = 1;
  for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) {
    if(hex_to_uint(&hex_hash[i*2]) != result[i]) {
      check_ok = 0;
      break;
    }
  }
  free(result);
  close(fd);
  return check_ok;
}

int metalink_check_hash(struct Configurable *config,
                        metalinkfile *mlfile,
                        const char *filename)
{
  metalink_checksum *chksum;
  const metalink_digest_alias *digest_alias;
  int rv;
  if(mlfile->checksum == NULL) {
    return -2;
  }
  for(digest_alias = digest_aliases; digest_alias->alias_name;
      ++digest_alias) {
    for(chksum = mlfile->checksum; chksum; chksum = chksum->next) {
      if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) &&
         strlen(chksum->hash_value) ==
         digest_alias->digest_def->dparams->digest_resultlen*2) {
        break;
      }
    }
    if(chksum) {
      break;
    }
  }
  if(!digest_alias->alias_name) {
    fprintf(config->errors, "No supported checksum in Metalink file\n");
    return -2;
  }
  rv = check_hash(filename, digest_alias->digest_def,
                  chksum->hash_value, config->errors);
  if(rv == 1) {
    fprintf(config->errors, "Checksum matched\n");
  }
  else if(rv == 0) {
    fprintf(config->errors, "Checksum didn't match\n");
  }
  return rv;
}

#endif /* METALINK_HASH_CHECK */

static metalink_checksum *new_metalink_checksum(const char *hash_name,
                                                const char *hash_value)
{
@@ -474,143 +612,3 @@ int check_metalink_content_type(const char *content_type)
{
  return check_content_type(content_type, "application/metalink+xml");
}

static const metalink_digest_def SHA256_DIGEST_DEF[] = {
  {"sha-256", SHA256_DIGEST_PARAMS}
};

static const metalink_digest_def SHA1_DIGEST_DEF[] = {
  {"sha-1", SHA1_DIGEST_PARAMS}
};

static const metalink_digest_def MD5_DIGEST_DEF[] = {
  {"md5", MD5_DIGEST_PARAMS}
};

/*
 * The alias of supported hash functions in the order by preference
 * (basically stronger hash comes first). We included "sha-256" and
 * "sha256". The former is the name defined in the IANA registry named
 * "Hash Function Textual Names". The latter is widely (and
 * historically) used in Metalink version 3.
 */
static const metalink_digest_alias digest_aliases[] = {
  {"sha-256", SHA256_DIGEST_DEF},
  {"sha256", SHA256_DIGEST_DEF},
  {"sha-1", SHA1_DIGEST_DEF},
  {"sha1", SHA1_DIGEST_DEF},
  {"md5", MD5_DIGEST_DEF},
  {NULL, NULL}
};

static unsigned char hex_to_uint(const char *s)
{
  int v[2];
  int i;
  for(i = 0; i < 2; ++i) {
    v[i] = Curl_raw_toupper(s[i]);
    if('0' <= v[i] && v[i] <= '9') {
      v[i] -= '0';
    }
    else if('A' <= v[i] && v[i] <= 'Z') {
      v[i] -= 'A'-10;
    }
  }
  return (unsigned char)((v[0] << 4) | v[1]);
}

/*
 * Check checksum of file denoted by filename. The expected hash value
 * is given in hex_hash which is hex-encoded string.
 *
 * This function returns 1 if it succeeds or one of the following
 * integers:
 *
 * 0:
 *   Checksum didn't match.
 * -1:
 *   Could not open file; or could not read data from file.
 */
static int check_hash(const char *filename,
                      const metalink_digest_def *digest_def,
                      const char *hex_hash, FILE *error)
{
  unsigned char *result;
  digest_context *dctx;
  int check_ok;
  int fd;
  size_t i;
  fprintf(error, "Checking %s checksum of file %s\n", digest_def->hash_name,
          filename);
  fd = open(filename, O_RDONLY);
  if(fd == -1) {
    fprintf(error, "Could not open file %s: %s\n", filename, strerror(errno));
    return -1;
  }
  dctx = Curl_digest_init(digest_def->dparams);
  result = malloc(digest_def->dparams->digest_resultlen);
  while(1) {
    unsigned char buf[4096];
    ssize_t len = read(fd, buf, sizeof(buf));
    if(len == 0) {
      break;
    }
    else if(len == -1) {
      fprintf(error, "Could not read file %s: %s\n", filename,
              strerror(errno));
      Curl_digest_final(dctx, result);
      close(fd);
      return -1;
    }
    Curl_digest_update(dctx, buf, (unsigned int)len);
  }
  Curl_digest_final(dctx, result);
  check_ok = 1;
  for(i = 0; i < digest_def->dparams->digest_resultlen; ++i) {
    if(hex_to_uint(&hex_hash[i*2]) != result[i]) {
      check_ok = 0;
      break;
    }
  }
  free(result);
  close(fd);
  return check_ok;
}

int metalink_check_hash(struct Configurable *config,
                        metalinkfile *mlfile,
                        const char *filename)
{
  metalink_checksum *chksum;
  const metalink_digest_alias *digest_alias;
  int rv;
  if(mlfile->checksum == NULL) {
    return -2;
  }
  for(digest_alias = digest_aliases; digest_alias->alias_name;
      ++digest_alias) {
    for(chksum = mlfile->checksum; chksum; chksum = chksum->next) {
      if(Curl_raw_equal(digest_alias->alias_name, chksum->hash_name) &&
         strlen(chksum->hash_value) ==
         digest_alias->digest_def->dparams->digest_resultlen*2) {
        break;
      }
    }
    if(chksum) {
      break;
    }
  }
  if(!digest_alias->alias_name) {
    fprintf(config->errors, "No supported checksum in Metalink file\n");
    return -2;
  }
  rv = check_hash(filename, digest_alias->digest_def,
                  chksum->hash_value, config->errors);
  if(rv == 1) {
    fprintf(config->errors, "Checksum matched\n");
  }
  else if(rv == 0) {
    fprintf(config->errors, "Checksum didn't match\n");
  }
  return rv;
}
+5 −0
Original line number Diff line number Diff line
@@ -27,6 +27,11 @@

struct Configurable;

#if defined(USE_OPENSSL) || defined(USE_GNUTLS)
/* Define 1 if hash check is enabled in Metalink transfer */
#  define METALINK_HASH_CHECK 1
#endif

typedef struct metalink_checksum {
  struct metalink_checksum *next;
  char *hash_name;
+2 −0
Original line number Diff line number Diff line
@@ -1594,12 +1594,14 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
              fprintf(config->errors, "Could not parse Metalink file.\n");
          }
        }
#  ifdef METALINK_HASH_CHECK
        else if(metalink && res == CURLE_OK && !metalink_next_res) {
          int rv = metalink_check_hash(config, mlfile, outs.filename);
          if(rv == 0) {
            metalink_next_res = 1;
          }
        }
#  endif /* METALINK_HASH_CHECK */
#endif /* HAVE_LIBMETALINK */

        /* No more business with this output struct */