/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ #include "tool_setup.h" #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_FCNTL_H # include #endif #include "rawstr.h" #include "tool_metalink.h" #include "tool_getparam.h" #include "tool_paramhlp.h" #include "memdebug.h" /* keep this as LAST include */ /* Copied from tool_getparam.c */ #define GetStr(str,val) do { \ if(*(str)) { \ free(*(str)); \ *(str) = NULL; \ } \ if((val)) \ *(str) = strdup((val)); \ if(!(val)) \ return PARAM_NO_MEM; \ } WHILE_FALSE #ifdef USE_GNUTLS_NETTLE #include #include 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 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 # include # 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)); f->file = metalinkfile; f->next = NULL; return f; } struct metalink *new_metalink(metalink_t *metalink) { struct metalink *ml; ml = (struct metalink*)malloc(sizeof(struct metalink)); ml->metalink = metalink; ml->next = NULL; return ml; } int count_next_metalink_resource(struct metalinkfile *mlfile) { int count = 0; metalink_resource_t **mlres; for(mlres = mlfile->file->resources; *mlres; ++mlres, ++count); return count; } void clean_metalink(struct Configurable *config) { while(config->metalinkfile_list) { struct metalinkfile *mlfile = config->metalinkfile_list; config->metalinkfile_list = config->metalinkfile_list->next; Curl_safefree(mlfile); } config->metalinkfile_last = 0; while(config->metalink_list) { struct metalink *ml = config->metalink_list; config->metalink_list = config->metalink_list->next; metalink_delete(ml->metalink); Curl_safefree(ml); } config->metalink_last = 0; } int parse_metalink(struct Configurable *config, const char *infile) { metalink_error_t r; metalink_t* metalink; metalink_file_t **files; struct metalink *ml; r = metalink_parse_file(infile, &metalink); if(r != 0) { return -1; } if(metalink->files == NULL) { fprintf(config->errors, "\nMetalink does not contain any file.\n"); return 0; } ml = new_metalink(metalink); if(config->metalink_list) { config->metalink_last->next = ml; config->metalink_last = ml; } else { config->metalink_list = config->metalink_last = ml; } for(files = metalink->files; *files; ++files) { struct getout *url; /* Skip an entry which has no resource. */ if(!(*files)->resources) { fprintf(config->errors, "\nFile %s does not have any resource.\n", (*files)->name); continue; } if(config->url_get || ((config->url_get = config->url_list) != NULL)) { /* there's a node here, if it already is filled-in continue to find an "empty" node */ while(config->url_get && (config->url_get->flags & GETOUT_URL)) config->url_get = config->url_get->next; } /* now there might or might not be an available node to fill in! */ if(config->url_get) /* existing node */ url = config->url_get; else /* there was no free node, create one! */ url=new_getout(config); if(url) { struct metalinkfile *mlfile; /* Set name as url */ GetStr(&url->url, (*files)->name); /* set flag metalink here */ url->flags |= GETOUT_URL | GETOUT_METALINK; mlfile = new_metalinkfile(*files); if(config->metalinkfile_list) { config->metalinkfile_last->next = mlfile; config->metalinkfile_last = mlfile; } else { config->metalinkfile_list = config->metalinkfile_last = mlfile; } } } return 0; } /* * Returns nonzero if content_type includes mediatype. */ static int check_content_type(const char *content_type, const char *media_type) { const char *ptr = content_type; size_t media_type_len = strlen(media_type); for(; *ptr && (*ptr == ' ' || *ptr == '\t'); ++ptr); if(!*ptr) { return 0; } return Curl_raw_nequal(ptr, media_type, media_type_len) && (*(ptr+media_type_len) == '\0' || *(ptr+media_type_len) == ' ' || *(ptr+media_type_len) == '\t' || *(ptr+media_type_len) == ';'); } 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; }