* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#ifdef USE_METALINK
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
# define MD5_CTX gcry_md_hd_t
# define SHA_CTX gcry_md_hd_t
# define SHA256_CTX gcry_md_hd_t
+#elif defined(USE_NSS)
+# include <nss.h>
+# include <pk11pub.h>
+# define MD5_CTX void *
+# define SHA_CTX void *
+# define SHA256_CTX void *
+ static NSSInitContext *nss_context;
+#elif defined(USE_POLARSSL)
+# include <polarssl/md5.h>
+# include <polarssl/sha1.h>
+# include <polarssl/sha256.h>
+# define MD5_CTX md5_context
+# define SHA_CTX sha1_context
+# define SHA256_CTX sha256_context
+#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
+ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
+ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
+/* For Apple operating systems: CommonCrypto has the functions we need.
+ The library's headers are even backward-compatible with OpenSSL's
+ headers as long as we define COMMON_DIGEST_FOR_OPENSSL first.
+
+ These functions are available on Tiger and later, as well as iOS 2.0
+ and later. If you're building for an older cat, well, sorry. */
+# define COMMON_DIGEST_FOR_OPENSSL
+# include <CommonCrypto/CommonDigest.h>
+#elif defined(_WIN32)
+/* For Windows: If no other crypto library is provided, we fallback
+ to the hash functions provided within the Microsoft Windows CryptoAPI */
+# include <wincrypt.h>
+/* Custom structure in order to store the required provider and hash handle */
+struct win32_crypto_hash {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+};
+/* Custom Microsoft AES Cryptographic Provider defines required for MinGW */
+# ifndef ALG_SID_SHA_256
+# define ALG_SID_SHA_256 12
+# endif
+# ifndef CALG_SHA_256
+# define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
+# endif
+# define MD5_CTX struct win32_crypto_hash
+# define SHA_CTX struct win32_crypto_hash
+# define SHA256_CTX struct win32_crypto_hash
#else
# error "Can't compile METALINK support without a crypto library."
#endif
return PARAM_NO_MEM; \
} WHILE_FALSE
-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
- }
-};
-
-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}
-};
-
#ifdef USE_GNUTLS_NETTLE
-static void MD5_Init(MD5_CTX *ctx)
+static int MD5_Init(MD5_CTX *ctx)
{
md5_init(ctx);
+ return 1;
}
static void MD5_Update(MD5_CTX *ctx,
md5_digest(ctx, 16, digest);
}
-static void SHA1_Init(SHA_CTX *ctx)
+static int SHA1_Init(SHA_CTX *ctx)
{
sha1_init(ctx);
+ return 1;
}
static void SHA1_Update(SHA_CTX *ctx,
sha1_digest(ctx, 20, digest);
}
-static void SHA256_Init(SHA256_CTX *ctx)
+static int SHA256_Init(SHA256_CTX *ctx)
{
sha256_init(ctx);
+ return 1;
}
static void SHA256_Update(SHA256_CTX *ctx,
#elif defined(USE_GNUTLS)
-static void MD5_Init(MD5_CTX *ctx)
+static int MD5_Init(MD5_CTX *ctx)
{
gcry_md_open(ctx, GCRY_MD_MD5, 0);
+ return 1;
}
static void MD5_Update(MD5_CTX *ctx,
gcry_md_close(*ctx);
}
-static void SHA1_Init(SHA_CTX *ctx)
+static int SHA1_Init(SHA_CTX *ctx)
{
gcry_md_open(ctx, GCRY_MD_SHA1, 0);
+ return 1;
}
static void SHA1_Update(SHA_CTX *ctx,
gcry_md_close(*ctx);
}
-static void SHA256_Init(SHA256_CTX *ctx)
+static int SHA256_Init(SHA256_CTX *ctx)
{
gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+ return 1;
}
static void SHA256_Update(SHA256_CTX *ctx,
gcry_md_close(*ctx);
}
+#elif defined(USE_NSS)
+
+static int nss_hash_init(void **pctx, SECOidTag hash_alg)
+{
+ PK11Context *ctx;
+
+ /* we have to initialize NSS if not initialized alraedy */
+ if(!NSS_IsInitialized() && !nss_context) {
+ static NSSInitParameters params;
+ params.length = sizeof params;
+ nss_context = NSS_InitContext("", "", "", "", ¶ms, NSS_INIT_READONLY
+ | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN
+ | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
+ }
+
+ ctx = PK11_CreateDigestContext(hash_alg);
+ if(!ctx)
+ return /* failure */ 0;
+
+ if(PK11_DigestBegin(ctx) != SECSuccess) {
+ PK11_DestroyContext(ctx, PR_TRUE);
+ return /* failure */ 0;
+ }
+
+ *pctx = ctx;
+ return /* success */ 1;
+}
+
+static void nss_hash_final(void **pctx, unsigned char *out, unsigned int len)
+{
+ PK11Context *ctx = *pctx;
+ unsigned int outlen;
+ PK11_DigestFinal(ctx, out, &outlen, len);
+ PK11_DestroyContext(ctx, PR_TRUE);
+}
+
+static int MD5_Init(MD5_CTX *pctx)
+{
+ return nss_hash_init(pctx, SEC_OID_MD5);
+}
+
+static void MD5_Update(MD5_CTX *pctx,
+ const unsigned char *input,
+ unsigned int input_len)
+{
+ PK11_DigestOp(*pctx, input, input_len);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX *pctx)
+{
+ nss_hash_final(pctx, digest, 16);
+}
+
+static int SHA1_Init(SHA_CTX *pctx)
+{
+ return nss_hash_init(pctx, SEC_OID_SHA1);
+}
+
+static void SHA1_Update(SHA_CTX *pctx,
+ const unsigned char *input,
+ unsigned int input_len)
+{
+ PK11_DigestOp(*pctx, input, input_len);
+}
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX *pctx)
+{
+ nss_hash_final(pctx, digest, 20);
+}
+
+static int SHA256_Init(SHA256_CTX *pctx)
+{
+ return nss_hash_init(pctx, SEC_OID_SHA256);
+}
+
+static void SHA256_Update(SHA256_CTX *pctx,
+ const unsigned char *input,
+ unsigned int input_len)
+{
+ PK11_DigestOp(*pctx, input, input_len);
+}
+
+static void SHA256_Final(unsigned char digest[32], SHA256_CTX *pctx)
+{
+ nss_hash_final(pctx, digest, 32);
+}
+
+#elif defined(USE_POLARSSL)
+
+static int MD5_Init(MD5_CTX *ctx)
+{
+ md5_starts(ctx);
+ return 1;
+}
+
+static void MD5_Update(MD5_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ md5_update(ctx, input, inputLen);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+ md5_finish(ctx, digest);
+}
+
+static int SHA1_Init(SHA_CTX *ctx)
+{
+ sha1_starts(ctx);
+ return 1;
+}
+
+static void SHA1_Update(SHA_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ sha1_update(ctx, input, inputLen);
+}
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
+{
+ sha1_finish(ctx, digest);
+}
+
+static int SHA256_Init(SHA256_CTX *ctx)
+{
+ sha256_starts(ctx, 0); /* 0 = sha256 */
+ return 1;
+}
+
+static void SHA256_Update(SHA256_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ sha256_update(ctx, input, inputLen);
+}
+
+static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
+{
+ sha256_finish(ctx, digest);
+}
+
+#elif defined(_WIN32) && !defined(USE_SSLEAY)
+
+static void win32_crypto_final(struct win32_crypto_hash *ctx,
+ unsigned char *digest,
+ unsigned int digestLen)
+{
+ unsigned long length;
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
+ if(length == digestLen)
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
+ if(ctx->hHash)
+ CryptDestroyHash(ctx->hHash);
+ if(ctx->hCryptProv)
+ CryptReleaseContext(ctx->hCryptProv, 0);
+}
+
+static int MD5_Init(MD5_CTX *ctx)
+{
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
+ }
+ return 1;
+}
+
+static void MD5_Update(MD5_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+}
+
+static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+ win32_crypto_final(ctx, digest, 16);
+}
+
+static int SHA1_Init(SHA_CTX *ctx)
+{
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_SHA1, 0, 0, &ctx->hHash);
+ }
+ return 1;
+}
+
+static void SHA1_Update(SHA_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+}
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX *ctx)
+{
+ win32_crypto_final(ctx, digest, 20);
+}
+
+static int SHA256_Init(SHA256_CTX *ctx)
+{
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL,
+ PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
+ }
+ return 1;
+}
+
+static void SHA256_Update(SHA256_CTX *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+}
+
+static void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
+{
+ win32_crypto_final(ctx, digest, 32);
+}
+
#endif /* CRYPTO LIBS */
+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
+ }
+};
+
+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}
+};
+
digest_context *Curl_digest_init(const digest_params *dparams)
{
digest_context *ctxt;
ctxt->digest_hash = dparams;
- dparams->digest_init(ctxt->digest_hashctx);
+ if(dparams->digest_init(ctxt->digest_hashctx) != 1) {
+ free(ctxt);
+ return NULL;
+ }
return ctxt;
}
* Checksum didn't match.
* -1:
* Could not open file; or could not read data from file.
+ * -2:
+ * Hash algorithm not available.
*/
static int check_hash(const char *filename,
const metalink_digest_def *digest_def,
{
unsigned char *result;
digest_context *dctx;
- int check_ok;
- int fd;
- fprintf(error,
- "Metalink: Validating %s checksum (This may take some time)...\n",
- digest_def->hash_name);
- fd = open(filename, O_RDONLY);
+ int check_ok, flags, fd;
+
+ flags = O_RDONLY;
+#ifdef O_BINARY
+ /* O_BINARY is required in order to avoid binary EOF in text mode */
+ flags |= O_BINARY;
+#endif
+
+ fd = open(filename, flags);
if(fd == -1) {
- fprintf(error, "Metalink: Could not open file %s: %s\n", filename,
- strerror(errno));
+ fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
+ digest_def->hash_name, strerror(errno));
return -1;
}
+
dctx = Curl_digest_init(digest_def->dparams);
+ if(!dctx) {
+ fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
+ digest_def->hash_name, "failed to initialize hash algorithm");
+ close(fd);
+ return -2;
+ }
+
result = malloc(digest_def->dparams->digest_resultlen);
while(1) {
unsigned char buf[4096];
break;
}
else if(len == -1) {
- fprintf(error, "Metalink: Could not read file %s: %s\n", filename,
- strerror(errno));
+ fprintf(error, "Metalink: validating (%s) [%s] FAILED (%s)\n", filename,
+ digest_def->hash_name, strerror(errno));
Curl_digest_final(dctx, result);
close(fd);
return -1;
digest_def->dparams->digest_resultlen) == 0;
/* sha*sum style verdict output */
if(check_ok)
- fprintf(error, "Metalink: %s: OK\n", filename);
+ fprintf(error, "Metalink: validating (%s) [%s] OK\n", filename,
+ digest_def->hash_name);
else
- fprintf(error, "Metalink: %s: FAILED\n", filename);
+ fprintf(error, "Metalink: validating (%s) [%s] FAILED (digest mismatch)\n",
+ filename, digest_def->hash_name);
free(result);
close(fd);
return check_ok;
}
-int metalink_check_hash(struct Configurable *config,
+int metalink_check_hash(struct GlobalConfig *config,
metalinkfile *mlfile,
const char *filename)
{
int rv;
+ fprintf(config->errors, "Metalink: validating (%s)...\n", filename);
if(mlfile->checksum == NULL) {
+ fprintf(config->errors,
+ "Metalink: validating (%s) FAILED (digest missing)\n", filename);
return -2;
}
rv = check_hash(filename, mlfile->checksum->digest_def,
tail = &root;
for(p = fileinfo->resources; *p; ++p) {
metalink_resource *res;
- res = new_metalink_resource((*p)->url);
- tail->next = res;
- tail = res;
+ /* Filter by type if it is non-NULL. In Metalink v3, type
+ includes the type of the resource. In curl, we are only
+ interested in HTTP, HTTPS and FTP. In addition to them,
+ Metalink v3 file may contain bittorrent type URL, which
+ points to the BitTorrent metainfo file. We ignore it here.
+ In Metalink v4, type was deprecated and all
+ fileinfo->resources point to the target file. BitTorrent
+ metainfo file URL may be appeared in fileinfo->metaurls.
+ */
+ if((*p)->type == NULL ||
+ Curl_raw_equal((*p)->type, "http") ||
+ Curl_raw_equal((*p)->type, "https") ||
+ Curl_raw_equal((*p)->type, "ftp") ||
+ Curl_raw_equal((*p)->type, "ftps")) {
+ res = new_metalink_resource((*p)->url);
+ tail->next = res;
+ tail = res;
+ }
}
f->resource = root.next;
}
return f;
}
-int parse_metalink(struct Configurable *config, struct OutStruct *outs)
+int parse_metalink(struct OperationConfig *config, struct OutStruct *outs,
+ const char *metalink_url)
{
metalink_error_t r;
metalink_t* metalink;
metalink_file_t **files;
+ bool warnings = FALSE;
/* metlaink_parse_final deletes outs->metalink_parser */
r = metalink_parse_final(outs->metalink_parser, NULL, 0, &metalink);
return -1;
}
if(metalink->files == NULL) {
- fprintf(config->errors,
- "\nMetalink: Metalink XML file does not contain any file.\n");
+ fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
+ "(missing or invalid file name)\n",
+ metalink_url);
metalink_delete(metalink);
- return 0;
+ return -1;
}
for(files = metalink->files; *files; ++files) {
struct getout *url;
/* Skip an entry which has no resource. */
if(!(*files)->resources) {
- fprintf(config->errors,
- "\nMetalink: File %s does not have any resource.\n",
- (*files)->name);
+ fprintf(config->global->errors, "Metalink: parsing (%s) WARNING "
+ "(missing or invalid resource)\n",
+ metalink_url, (*files)->name);
continue;
}
if(config->url_get ||
if(url) {
metalinkfile *mlfile;
mlfile = new_metalinkfile(*files);
-
+ if(!mlfile->checksum) {
+ warnings = TRUE;
+ fprintf(config->global->errors,
+ "Metalink: parsing (%s) WARNING (digest missing)\n",
+ metalink_url);
+ }
/* Set name as url */
GetStr(&url->url, mlfile->filename);
}
}
metalink_delete(metalink);
- return 0;
+ return (warnings) ? -2 : 0;
}
size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
void *userdata)
{
struct OutStruct *outs = userdata;
- struct Configurable *config = outs->config;
+ struct OperationConfig *config = outs->config;
int rv;
/*
if(rv == 0)
return sz * nmemb;
else {
- warnf(config, "Metalink: Failed to parse Metalink XML file\n");
+ fprintf(config->global->errors, "Metalink: parsing FAILED\n");
return failure;
}
}
Curl_safefree(mlfile);
}
-void clean_metalink(struct Configurable *config)
+void clean_metalink(struct OperationConfig *config)
{
while(config->metalinkfile_list) {
metalinkfile *mlfile = config->metalinkfile_list;
config->metalinkfile_last = 0;
}
+void metalink_cleanup(void)
+{
+#ifdef USE_NSS
+ if(nss_context) {
+ NSS_ShutdownContext(nss_context);
+ nss_context = NULL;
+ }
+#endif
+}
+
#endif /* USE_METALINK */