aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tool_metalink.c372
-rw-r--r--src/tool_metalink.h57
-rw-r--r--src/tool_operate.c9
3 files changed, 438 insertions, 0 deletions
diff --git a/src/tool_metalink.c b/src/tool_metalink.c
index d31502ab2..4fb0015c7 100644
--- a/src/tool_metalink.c
+++ b/src/tool_metalink.c
@@ -21,6 +21,16 @@
***************************************************************************/
#include "tool_setup.h"
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
#include "rawstr.h"
#include "tool_metalink.h"
@@ -41,6 +51,228 @@
return PARAM_NO_MEM; \
} WHILE_FALSE
+#ifdef USE_GNUTLS_NETTLE
+
+#include <nettle/md5.h>
+#include <nettle/sha.h>
+
+typedef struct md5_ctx MD5_CTX;
+
+static void MD5_Init(MD5_CTX * ctx)
+{
+ md5_init(ctx);
+}
+
+static void MD5_Update(MD5_CTX * ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ md5_update(ctx, inputLen, input);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+{
+ md5_digest(ctx, 16, digest);
+}
+
+typedef struct sha1_ctx SHA_CTX;
+
+static void SHA1_Init(SHA_CTX *ctx)
+{
+ sha1_init(ctx);
+}
+
+static void SHA1_Update(SHA_CTX *ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ sha1_update(ctx, inputLen, input);
+}
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
+{
+ sha1_digest(ctx, 20, digest);
+}
+
+typedef struct sha256_ctx SHA256_CTX;
+
+static void SHA256_Init(SHA256_CTX *ctx)
+{
+ sha256_init(ctx);
+}
+
+static void SHA256_Update(SHA256_CTX *ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ sha256_update(ctx, inputLen, input);
+}
+
+static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
+{
+ sha256_digest(ctx, 32, digest);
+}
+
+#else
+
+#ifdef USE_GNUTLS
+
+#include <gcrypt.h>
+
+typedef gcry_md_hd_t MD5_CTX;
+
+static void MD5_Init(MD5_CTX * ctx)
+{
+ gcry_md_open(ctx, GCRY_MD_MD5, 0);
+}
+
+static void MD5_Update(MD5_CTX * ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ gcry_md_write(*ctx, input, inputLen);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+{
+ memcpy(digest, gcry_md_read(*ctx, 0), 16);
+ gcry_md_close(*ctx);
+}
+
+typedef gcry_md_hd_t SHA_CTX;
+
+static void SHA1_Init(SHA_CTX * ctx)
+{
+ gcry_md_open(ctx, GCRY_MD_SHA1, 0);
+}
+
+static void SHA1_Update(SHA_CTX * ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ gcry_md_write(*ctx, input, inputLen);
+}
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX * ctx)
+{
+ memcpy(digest, gcry_md_read(*ctx, 0), 20);
+ gcry_md_close(*ctx);
+}
+
+typedef gcry_md_hd_t SHA256_CTX;
+
+static void SHA256_Init(SHA256_CTX * ctx)
+{
+ gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+}
+
+static void SHA256_Update(SHA256_CTX * ctx,
+ const unsigned char * input,
+ unsigned int inputLen)
+{
+ gcry_md_write(*ctx, input, inputLen);
+}
+
+static void SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
+{
+ memcpy(digest, gcry_md_read(*ctx, 0), 32);
+ gcry_md_close(*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 */
+
+/* TODO hash functions for other libraries here */
+
+#endif /* USE_SSLEAY */
+
+#endif /* USE_GNUTLS */
+
+#endif /* USE_GNUTLS_NETTLE */
+
+const digest_params MD5_DIGEST_PARAMS[] = {
+ {
+ (Curl_digest_init_func) MD5_Init,
+ (Curl_digest_update_func) MD5_Update,
+ (Curl_digest_final_func) MD5_Final,
+ sizeof(MD5_CTX),
+ 16
+ }
+};
+
+const digest_params SHA1_DIGEST_PARAMS[] = {
+ {
+ (Curl_digest_init_func) SHA1_Init,
+ (Curl_digest_update_func) SHA1_Update,
+ (Curl_digest_final_func) SHA1_Final,
+ sizeof(SHA_CTX),
+ 20
+ }
+};
+
+const digest_params SHA256_DIGEST_PARAMS[] = {
+ {
+ (Curl_digest_init_func) SHA256_Init,
+ (Curl_digest_update_func) SHA256_Update,
+ (Curl_digest_final_func) SHA256_Final,
+ sizeof(SHA256_CTX),
+ 32
+ }
+};
+
+digest_context *Curl_digest_init(const digest_params *dparams)
+{
+ digest_context *ctxt;
+
+ /* Create digest context */
+ ctxt = malloc(sizeof *ctxt);
+
+ if(!ctxt)
+ return ctxt;
+
+ ctxt->digest_hashctx = malloc(dparams->digest_ctxtsize);
+
+ if(!ctxt->digest_hashctx) {
+ free(ctxt);
+ return NULL;
+ }
+
+ ctxt->digest_hash = dparams;
+
+ dparams->digest_init(ctxt->digest_hashctx);
+
+ return ctxt;
+}
+
+int Curl_digest_update(digest_context *context,
+ const unsigned char *data,
+ unsigned int len)
+{
+ (*context->digest_hash->digest_update)(context->digest_hashctx, data, len);
+
+ return 0;
+}
+
+int Curl_digest_final(digest_context *context, unsigned char *result)
+{
+ (*context->digest_hash->digest_final)(result, context->digest_hashctx);
+
+ free(context->digest_hashctx);
+ free(context);
+
+ return 0;
+}
+
struct metalinkfile *new_metalinkfile(metalink_file_t *metalinkfile) {
struct metalinkfile *f;
f = (struct metalinkfile*)malloc(sizeof(struct metalinkfile));
@@ -174,3 +406,143 @@ 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,
+ struct metalinkfile *mlfile,
+ const char *filename)
+{
+ metalink_checksum_t **checksum;
+ const metalink_digest_alias *digest_alias;
+ int rv;
+ if(!mlfile->file->checksums) {
+ return -2;
+ }
+ for(digest_alias = digest_aliases; digest_alias->alias_name;
+ ++digest_alias) {
+ for(checksum = mlfile->file->checksums; *checksum; ++checksum) {
+ if(Curl_raw_equal(digest_alias->alias_name, (*checksum)->type) &&
+ strlen((*checksum)->hash) ==
+ digest_alias->digest_def->dparams->digest_resultlen*2) {
+ break;
+ }
+ }
+ if(*checksum) {
+ 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,
+ (*checksum)->hash, 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;
+}
diff --git a/src/tool_metalink.h b/src/tool_metalink.h
index f7a0abe84..0c9d589eb 100644
--- a/src/tool_metalink.h
+++ b/src/tool_metalink.h
@@ -56,4 +56,61 @@ int parse_metalink(struct Configurable *config, const char *infile);
*/
int check_metalink_content_type(const char *content_type);
+typedef void (* Curl_digest_init_func)(void *context);
+typedef void (* Curl_digest_update_func)(void *context,
+ const unsigned char *data,
+ unsigned int len);
+typedef void (* Curl_digest_final_func)(unsigned char *result, void *context);
+
+typedef struct {
+ Curl_digest_init_func digest_init; /* Initialize context procedure */
+ Curl_digest_update_func digest_update; /* Update context with data */
+ Curl_digest_final_func digest_final; /* Get final result procedure */
+ unsigned int digest_ctxtsize; /* Context structure size */
+ unsigned int digest_resultlen; /* Result length (bytes) */
+} digest_params;
+
+typedef struct {
+ const digest_params *digest_hash; /* Hash function definition */
+ void *digest_hashctx; /* Hash function context */
+} digest_context;
+
+extern const digest_params MD5_DIGEST_PARAMS[1];
+extern const digest_params SHA1_DIGEST_PARAMS[1];
+extern const digest_params SHA256_DIGEST_PARAMS[1];
+
+digest_context * Curl_digest_init(const digest_params *dparams);
+int Curl_digest_update(digest_context *context,
+ const unsigned char *data,
+ unsigned int len);
+int Curl_digest_final(digest_context *context, unsigned char *result);
+
+typedef struct {
+ const char *hash_name;
+ const digest_params *dparams;
+} metalink_digest_def;
+
+typedef struct {
+ const char *alias_name;
+ const metalink_digest_def *digest_def;
+} metalink_digest_alias;
+
+/*
+ * Check checksum of file denoted by filename.
+ *
+ * This function returns 1 if the checksum matches or one of the
+ * following integers:
+ *
+ * 0:
+ * Checksum didn't match.
+ * -1:
+ * Could not open file; or could not read data from file.
+ * -2:
+ * No checksum in Metalink supported; or Metalink does not contain
+ * checksum.
+ */
+int metalink_check_hash(struct Configurable *config,
+ struct metalinkfile *mlfile,
+ const char *filename);
+
#endif /* HEADER_CURL_TOOL_METALINK_H */
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 0d17329b0..be860b983 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -1594,6 +1594,12 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
fprintf(config->errors, "Could not parse Metalink file.\n");
}
}
+ 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 /* HAVE_LIBMETALINK */
/* No more business with this output struct */
@@ -1619,6 +1625,9 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
break;
}
if(!metalink_next_res || *(++mlres) == NULL)
+ /* TODO If metalink_next_res is 1 and mlres is NULL,
+ * set res to error code
+ */
break;
}
else