diff options
-rw-r--r-- | src/tool_metalink.c | 372 | ||||
-rw-r--r-- | src/tool_metalink.h | 57 | ||||
-rw-r--r-- | src/tool_operate.c | 9 |
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 |