aboutsummaryrefslogtreecommitdiff
path: root/src/tool_metalink.c
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2012-05-22 01:40:11 +0900
committerDaniel Stenberg <daniel@haxx.se>2012-05-26 23:11:46 +0200
commit9f7f7925da648b3b39714e6c9fd65500a869bdfe (patch)
tree5dcc6fd6301c14d33589c86728cd798461351549 /src/tool_metalink.c
parentc3ef63f16743202eae8b0a1207130a5ba65b8ec2 (diff)
Check checksum of downloaded file if checksum is available
Metalink file contains several hash types of checksums, such as md5, sha-1, sha-256, etc. To deal with these checksums, I created abstraction layer based on lib/curl_md5.h and lib/md5.c. Basically, they are almost the same but I changed the code so that it is not hash type dependent. Currently, GNUTLS(nettle or gcrypt) and OpenSSL functions are supported. Checksum checking is done by reopening download file. If there is an I/O error, the current implementation just prints error message and does not try next resource. In this patch, the supported hash types are: md5, sha-1 and sha-256.
Diffstat (limited to 'src/tool_metalink.c')
-rw-r--r--src/tool_metalink.c372
1 files changed, 372 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;
+}